summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ccgexprs.nim2
-rwxr-xr-xcompiler/commands.nim6
-rwxr-xr-xcompiler/options.nim7
-rwxr-xr-xcompiler/pragmas.nim18
-rwxr-xr-xcompiler/semexprs.nim3
-rw-r--r--compiler/semthreads.nim69
-rwxr-xr-xcompiler/wordrecg.nim4
-rwxr-xr-xdoc/advopt.txt2
-rw-r--r--tests/accept/compile/tthreadanalysis.nim54
-rw-r--r--tests/reject/tthreadanalysis2.nim54
-rwxr-xr-xtodo.txt4
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])