mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 22:10:33 +00:00
basic thread analysis working
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
54
tests/accept/compile/tthreadanalysis.nim
Normal file
54
tests/accept/compile/tthreadanalysis.nim
Normal file
@@ -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()
|
||||
|
||||
54
tests/reject/tthreadanalysis2.nim
Normal file
54
tests/reject/tthreadanalysis2.nim
Normal file
@@ -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()
|
||||
|
||||
4
todo.txt
4
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])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user