diff options
-rwxr-xr-x | compiler/ccgexprs.nim | 2 | ||||
-rwxr-xr-x | compiler/commands.nim | 6 | ||||
-rwxr-xr-x | compiler/options.nim | 7 | ||||
-rwxr-xr-x | compiler/pragmas.nim | 18 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 3 | ||||
-rw-r--r-- | compiler/semthreads.nim | 69 | ||||
-rwxr-xr-x | compiler/wordrecg.nim | 4 | ||||
-rwxr-xr-x | doc/advopt.txt | 2 | ||||
-rw-r--r-- | tests/accept/compile/tthreadanalysis.nim | 54 | ||||
-rw-r--r-- | tests/reject/tthreadanalysis2.nim | 54 | ||||
-rwxr-xr-x | todo.txt | 4 |
11 files changed, 179 insertions, 44 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7447dbb6c..faf37247e 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1450,7 +1450,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) - of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit: + of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mCreateThread: genCall(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e) diff --git a/compiler/commands.nim b/compiler/commands.nim index 1295a5490..c5a689dfc 100755 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -104,7 +104,7 @@ Advanced options: --genMapping generate a mapping file containing (Nimrod, mangled) identifier pairs --lineDir:on|off generation of #line directive on|off - --checkpoints:on|off turn checkpoints on|off; for debugging Nimrod + --threadanalysis:on|off turn thread analysis on|off --skipCfg do not read the general configuration file --skipProjCfg do not read the project's configuration file --gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC @@ -252,7 +252,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of wForceBuild, wF: result = contains(gGlobalOptions, optForceFullMake) of wWarnings, wW: result = contains(gOptions, optWarns) of wHints: result = contains(gOptions, optHints) - of wCheckpoints: result = contains(gOptions, optCheckpoints) + of wThreadAnalysis: result = contains(gGlobalOptions, optThreadAnalysis) of wStackTrace: result = contains(gOptions, optStackTrace) of wLineTrace: result = contains(gOptions, optLineTrace) of wDebugger: result = contains(gOptions, optEndb) @@ -367,7 +367,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of wWarning: ProcessSpecificNote(arg, wWarning, pass, info) of wHint: ProcessSpecificNote(arg, wHint, pass, info) of wHints: ProcessOnOffSwitch({optHints}, arg, pass, info) - of wCheckpoints: ProcessOnOffSwitch({optCheckpoints}, arg, pass, info) + of wThreadAnalysis: ProcessOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of wStackTrace: ProcessOnOffSwitch({optStackTrace}, arg, pass, info) of wLineTrace: ProcessOnOffSwitch({optLineTrace}, arg, pass, info) of wDebugger: diff --git a/compiler/options.nim b/compiler/options.nim index d7025e6f7..662848053 100755 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -25,7 +25,6 @@ type # please make sure we have under 32 options optEndb, # embedded debugger optByRef, # use pass by ref for objects # (for interfacing with C) - optCheckpoints, # check for checkpoints (used for debugging) optProfiler # profiler turned on TOptions* = set[TOption] TGlobalOption* = enum @@ -46,7 +45,9 @@ type # please make sure we have under 32 options optStdout, # output to stdout optSuggest, # ideTools: 'suggest' optContext, # ideTools: 'context' - optDef # ideTools: 'def' + optDef, # ideTools: 'def' + optThreadAnalysis # thread analysis pass + TGlobalOptions* = set[TGlobalOption] TCommands* = enum # Nimrod's commands cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, @@ -71,7 +72,7 @@ var gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace} - gGlobalOptions*: TGlobalOptions = {optRefcGC} + gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis} gExitcode*: int8 searchPaths*: TLinkedList outFile*: string = "" diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 7c78570e2..a450db3a4 100755 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -106,23 +106,23 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = else: result = defaultStr proc processMagic(c: PContext, n: PNode, s: PSym) = - var v: string - #if not (sfSystemModule in c.module.flags) then - # liMessage(n.info, errMagicOnlyInSystem); + #if sfSystemModule notin c.module.flags: + # liMessage(n.info, errMagicOnlyInSystem) if n.kind != nkExprColonExpr: LocalError(n.info, errStringLiteralExpected) return + var v: string if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s else: v = expectStrLit(c, n) - incl(s.flags, sfImportc) - # magics don't need an implementation, so we - # treat them as imported, instead of modifing a lot of working code - # BUGFIX: magic does not imply ``lfNoDecl`` anymore! for m in countup(low(TMagic), high(TMagic)): if substr($m, 1) == v: s.magic = m - return - Message(n.info, warnUnknownMagic, v) + break + if s.magic == mNone: Message(n.info, warnUnknownMagic, v) + elif s.magic != mCreateThread: + # magics don't need an implementation, so we + # treat them as imported, instead of modifing a lot of working code: + incl(s.flags, sfImportc) proc wordToCallConv(sw: TSpecialWord): TCallingConvention = # this assumes that the order of special words and calling conventions is diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 39f3e1f15..5ea8e765b 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -568,7 +568,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mEcho: result = semEcho(c, setMs(n, s)) of mCreateThread: result = semDirectOp(c, n, flags) - if optThreads in gGlobalOptions: + if gGlobalOptions * {optThreads, optThreadAnalysis} == + {optThreads, optThreadAnalysis}: # XXX This analysis should be done as late as possible # (forward references!) semthreads.AnalyseThread(result) diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 9d78123d6..9f4e62756 100644 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -170,12 +170,23 @@ proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner = var formal = skipTypes(prc.typ, abstractInst).n.sons[i].sym newCtx.mapping[formal.id] = call.args[i-1] pushInfoContext(n.info) - computed[call] = analyse(newCtx, prc.ast.sons[codePos]) + result = analyse(newCtx, prc.ast.sons[codePos]) + if prc.typ.sons[0] != nil: + if prc.ast.len > resultPos: + result = newCtx.mapping[prc.ast.sons[resultPos].sym.id] + else: + result = toNil + else: + result = toVoid + computed[call] = result popInfoContext() else: - # ugh, cycle! We are already computing it but don't know the outcome yet... - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil + result = computed[call] + if result == toUndefined: + # ugh, cycle! We are already computing it but don't know the + # outcome yet... + if prc.typ.sons[0] == nil: result = toVoid + else: result = toNil proc analyseVarTuple(c: PProcCtx, n: PNode) = if n.kind != nkVarTuple: InternalError(n.info, "analyseVarTuple") @@ -210,28 +221,39 @@ template aggregateOwner(result, ana: expr) = if result == toNil: result = a else: localError(n.info, errDifferentHeaps) +proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner = + if n[0].kind != nkSym or n[0].sym.kind != skProc: + Message(n.info, warnAnalysisLoophole, renderTree(n)) + result = toNil + else: + var prc = n[0].sym + # XXX create thread!? + case prc.magic + of mNew, mNewFinalize, mNewSeq, mSetLengthStr, mSetLengthSeq, + mAppendSeqElem, mReset, mAppendStrCh, mAppendStrStr: + writeAccess(c, n[1], toMine) + result = toVoid + of mSwap: + var a = analyse(c, n[2]) + writeAccess(c, n[1], a) + writeAccess(c, n[2], a) + result = toVoid + of mIntToStr, mInt64ToStr, mFloatToStr, mBoolToStr, mCharToStr, + mCStrToStr, mStrToStr, mEnumToStr, + mConStrStr, mConArrArr, mConArrT, + mConTArr, mConTT, mSlice, + mRepr, mArrToSeq, mCopyStr, mCopyStrLast, + mNewString, mNewStringOfCap: + # XXX no check for effects in the arguments? + result = toMine + else: + result = analyseCall(c, n) + proc analyse(c: PProcCtx, n: PNode): TThreadOwner = case n.kind of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit, nkHiddenCallConv: - if n[0].kind != nkSym or n[0].sym.kind != skProc: - Message(n.info, warnAnalysisLoophole, renderTree(n)) - result = toNil - else: - var prc = n[0].sym - # XXX create thread!? - case prc.magic - of mNew, mNewFinalize, mNewSeq, mSetLengthStr, mSetLengthSeq, - mAppendSeqElem, mReset, mAppendStrCh, mAppendStrStr: - writeAccess(c, n[1], toMine) - result = toVoid - of mSwap: - var a = analyse(c, n[2]) - writeAccess(c, n[1], a) - writeAccess(c, n[2], a) - result = toVoid - else: - result = analyseCall(c, n) + result = analyseOp(c, n) of nkAsgn, nkFastAsgn: analyseAssign(c, n) result = toVoid @@ -290,7 +312,8 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = of nkVarSection: result = analyseVarSection(c, n) of nkConstSection: result = analyseConstSection(c, n) of nkTypeSection, nkCommentStmt: result = toVoid - of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt: + of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, + nkElifBranch, nkElse, nkExceptBranch, nkOfBranch: for i in 0 .. <n.len: discard analyse(c, n[i]) result = toVoid of nkBreakStmt, nkContinueStmt: result = toVoid diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index ff336f1c0..088972e0e 100755 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -52,7 +52,7 @@ type wPassc, wT, wPassl, wL, wListcmd, wGendoc, wGenmapping, wOs, wCpu, wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH, wSymbolFiles, wFieldChecks, wX, wVersion, wAdvanced, wSkipcfg, wSkipProjCfg, - wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar, + wCc, wGenscript, wCheckPoint, wThreadAnalysis, wNoMain, wSubsChar, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wIndex, wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads, @@ -100,7 +100,7 @@ const "gui", "passc", "t", "passl", "l", "listcmd", "gendoc", "genmapping", "os", "cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v", "help", "h", "symbolfiles", "fieldchecks", "x", "version", "advanced", - "skipcfg", "skipprojcfg", "cc", "genscript", "checkpoint", "checkpoints", + "skipcfg", "skipprojcfg", "cc", "genscript", "checkpoint", "threadanalysis", "nomain", "subschar", "acyclic", "shallow", "unroll", "linearscanend", "index", "write", "putenv", "prependenv", "appendenv", "threadvar", "emit", diff --git a/doc/advopt.txt b/doc/advopt.txt index 2a0b64c4c..5b22c6b48 100755 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -37,7 +37,7 @@ Advanced options: --genMapping generate a mapping file containing (Nimrod, mangled) identifier pairs --lineDir:on|off generation of #line directive on|off - --checkpoints:on|off turn checkpoints on|off; for debugging Nimrod + --threadanalysis:on|off turn thread analysis on|off --skipCfg do not read the general configuration file --skipProjCfg do not read the project's configuration file --gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC diff --git a/tests/accept/compile/tthreadanalysis.nim b/tests/accept/compile/tthreadanalysis.nim new file mode 100644 index 000000000..0a1415fe3 --- /dev/null +++ b/tests/accept/compile/tthreadanalysis.nim @@ -0,0 +1,54 @@ +discard """ + outputsub: "101" + cmd: "nimrod cc --hints:on --threads:on $# $#" +""" + +import os + +const + noDeadlocks = defined(system.deadlocksPrevented) + +var + thr: array [0..5, TThread[tuple[a, b: int]]] + +proc doNothing() = nil + +type + PNode = ref TNode + TNode = object {.pure.} + le, ri: PNode + data: string + +var + root: PNode + +proc buildTree(depth: int): PNode = + if depth == 3: return nil + new(result) + result.le = buildTree(depth-1) + result.ri = buildTree(depth-1) + result.data = $depth + +proc echoLeTree(n: PNode) = + var it: PNode + it = nil + it = n + while it != nil: + echo it.data + it = it.le + +proc threadFunc(interval: tuple[a, b: int]) {.procvar.} = + doNothing() + for i in interval.a..interval.b: + var r = buildTree(i) + echoLeTree(r) # for local data + echoLeTree(root) # and the same for foreign data :-) + +proc main = + root = buildTree(5) + for i in 0..high(thr): + createThread(thr[i], threadFunc, (i*3, i*3+2)) + joinThreads(thr) + +main() + diff --git a/tests/reject/tthreadanalysis2.nim b/tests/reject/tthreadanalysis2.nim new file mode 100644 index 000000000..50550bd65 --- /dev/null +++ b/tests/reject/tthreadanalysis2.nim @@ -0,0 +1,54 @@ +discard """ + file: "tthreadanalysis2.nim" + line: 44 + errormsg: "possible inconsistency of thread local heaps" +""" + +import os + +const + noDeadlocks = defined(system.deadlocksPrevented) + +var + thr: array [0..5, TThread[tuple[a, b: int]]] + +proc doNothing() = nil + +type + PNode = ref TNode + TNode = object {.pure.} + le, ri: PNode + data: string + +var + root: PNode + +proc buildTree(depth: int): PNode = + if depth == 3: return nil + new(result) + result.le = buildTree(depth-1) + result.ri = buildTree(depth-1) + result.data = $depth + +proc echoLeTree(n: PNode) = + var it = n + while it != nil: + echo it.data + it = it.le + +proc threadFunc(interval: tuple[a, b: int]) {.procvar.} = + doNothing() + for i in interval.a..interval.b: + var r = buildTree(i) + echoLeTree(r) # for local data + root = buildTree(2) # BAD! + echoLeTree(root) # and the same for foreign data :-) + +proc main = + root = buildTree(5) + for i in 0..high(thr): + createThread(thr[i], threadFunc, (i*100, i*100+50)) + joinThreads(thr) + +main() + diff --git a/todo.txt b/todo.txt index 396325c33..2af2e28fa 100755 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,7 @@ * codegen for threadvars +* clean up thread analysis: fix remaining XXX; thread analysis as a separate + pass +* implement message passing built-ins * add --deadlock_prevention:on|off switch? timeout for locks? * test the sort implementation again @@ -37,7 +40,6 @@ Bugs for i in 0 .. high(t.data)-1: var maxIdx = i for j in i+1 .. high(t.data): - if t.data[j].val == 3: echo "touched! ", t.data[j].key if t.data[j].val > t.data[maxIdx].val: maxIdx = j swap(t.data[maxIdx], t.data[i]) |