basic thread analysis working

This commit is contained in:
Araq 2011-06-13 16:22:19 +02:00
commit 9f9f0f0818
11 changed files with 179 additions and 44 deletions

View file

@ -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, of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet: mInSet:
genSetOp(p, e, d, op) genSetOp(p, e, d, op)
of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit: of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mCreateThread:
genCall(p, e, d) genCall(p, e, d)
of mReset: genReset(p, e) of mReset: genReset(p, e)
of mEcho: genEcho(p, e) of mEcho: genEcho(p, e)

View file

@ -104,7 +104,7 @@ Advanced options:
--genMapping generate a mapping file containing --genMapping generate a mapping file containing
(Nimrod, mangled) identifier pairs (Nimrod, mangled) identifier pairs
--lineDir:on|off generation of #line directive on|off --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 --skipCfg do not read the general configuration file
--skipProjCfg do not read the project's configuration file --skipProjCfg do not read the project's configuration file
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC --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 wForceBuild, wF: result = contains(gGlobalOptions, optForceFullMake)
of wWarnings, wW: result = contains(gOptions, optWarns) of wWarnings, wW: result = contains(gOptions, optWarns)
of wHints: result = contains(gOptions, optHints) 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 wStackTrace: result = contains(gOptions, optStackTrace)
of wLineTrace: result = contains(gOptions, optLineTrace) of wLineTrace: result = contains(gOptions, optLineTrace)
of wDebugger: result = contains(gOptions, optEndb) 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 wWarning: ProcessSpecificNote(arg, wWarning, pass, info)
of wHint: ProcessSpecificNote(arg, wHint, pass, info) of wHint: ProcessSpecificNote(arg, wHint, pass, info)
of wHints: ProcessOnOffSwitch({optHints}, arg, 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 wStackTrace: ProcessOnOffSwitch({optStackTrace}, arg, pass, info)
of wLineTrace: ProcessOnOffSwitch({optLineTrace}, arg, pass, info) of wLineTrace: ProcessOnOffSwitch({optLineTrace}, arg, pass, info)
of wDebugger: of wDebugger:

View file

@ -25,7 +25,6 @@ type # please make sure we have under 32 options
optEndb, # embedded debugger optEndb, # embedded debugger
optByRef, # use pass by ref for objects optByRef, # use pass by ref for objects
# (for interfacing with C) # (for interfacing with C)
optCheckpoints, # check for checkpoints (used for debugging)
optProfiler # profiler turned on optProfiler # profiler turned on
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum TGlobalOption* = enum
@ -46,7 +45,9 @@ type # please make sure we have under 32 options
optStdout, # output to stdout optStdout, # output to stdout
optSuggest, # ideTools: 'suggest' optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context' optContext, # ideTools: 'context'
optDef # ideTools: 'def' optDef, # ideTools: 'def'
optThreadAnalysis # thread analysis pass
TGlobalOptions* = set[TGlobalOption] TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nimrod's commands TCommands* = enum # Nimrod's commands
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
@ -71,7 +72,7 @@ var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns, optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace} optHints, optStackTrace, optLineTrace}
gGlobalOptions*: TGlobalOptions = {optRefcGC} gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis}
gExitcode*: int8 gExitcode*: int8
searchPaths*: TLinkedList searchPaths*: TLinkedList
outFile*: string = "" outFile*: string = ""

View file

@ -106,23 +106,23 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
else: result = defaultStr else: result = defaultStr
proc processMagic(c: PContext, n: PNode, s: PSym) = proc processMagic(c: PContext, n: PNode, s: PSym) =
var v: string #if sfSystemModule notin c.module.flags:
#if not (sfSystemModule in c.module.flags) then # liMessage(n.info, errMagicOnlyInSystem)
# liMessage(n.info, errMagicOnlyInSystem);
if n.kind != nkExprColonExpr: if n.kind != nkExprColonExpr:
LocalError(n.info, errStringLiteralExpected) LocalError(n.info, errStringLiteralExpected)
return return
var v: string
if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s
else: v = expectStrLit(c, n) 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)): for m in countup(low(TMagic), high(TMagic)):
if substr($m, 1) == v: if substr($m, 1) == v:
s.magic = m s.magic = m
return break
Message(n.info, warnUnknownMagic, v) 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 = proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
# this assumes that the order of special words and calling conventions is # this assumes that the order of special words and calling conventions is

View file

@ -568,7 +568,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mEcho: result = semEcho(c, setMs(n, s)) of mEcho: result = semEcho(c, setMs(n, s))
of mCreateThread: of mCreateThread:
result = semDirectOp(c, n, flags) 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 # XXX This analysis should be done as late as possible
# (forward references!) # (forward references!)
semthreads.AnalyseThread(result) semthreads.AnalyseThread(result)

View file

@ -170,12 +170,23 @@ proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner =
var formal = skipTypes(prc.typ, abstractInst).n.sons[i].sym var formal = skipTypes(prc.typ, abstractInst).n.sons[i].sym
newCtx.mapping[formal.id] = call.args[i-1] newCtx.mapping[formal.id] = call.args[i-1]
pushInfoContext(n.info) 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() popInfoContext()
else: else:
# ugh, cycle! We are already computing it but don't know the outcome yet... result = computed[call]
if prc.typ.sons[0] == nil: result = toVoid if result == toUndefined:
else: result = toNil # 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) = proc analyseVarTuple(c: PProcCtx, n: PNode) =
if n.kind != nkVarTuple: InternalError(n.info, "analyseVarTuple") if n.kind != nkVarTuple: InternalError(n.info, "analyseVarTuple")
@ -210,28 +221,39 @@ template aggregateOwner(result, ana: expr) =
if result == toNil: result = a if result == toNil: result = a
else: localError(n.info, errDifferentHeaps) 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 = proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
case n.kind case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit, nkHiddenCallConv: nkCallStrLit, nkHiddenCallConv:
if n[0].kind != nkSym or n[0].sym.kind != skProc: result = analyseOp(c, n)
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)
of nkAsgn, nkFastAsgn: of nkAsgn, nkFastAsgn:
analyseAssign(c, n) analyseAssign(c, n)
result = toVoid result = toVoid
@ -290,7 +312,8 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
of nkVarSection: result = analyseVarSection(c, n) of nkVarSection: result = analyseVarSection(c, n)
of nkConstSection: result = analyseConstSection(c, n) of nkConstSection: result = analyseConstSection(c, n)
of nkTypeSection, nkCommentStmt: result = toVoid 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]) for i in 0 .. <n.len: discard analyse(c, n[i])
result = toVoid result = toVoid
of nkBreakStmt, nkContinueStmt: result = toVoid of nkBreakStmt, nkContinueStmt: result = toVoid

View file

@ -52,7 +52,7 @@ type
wPassc, wT, wPassl, wL, wListcmd, wGendoc, wGenmapping, wOs, wCpu, wPassc, wT, wPassl, wL, wListcmd, wGendoc, wGenmapping, wOs, wCpu,
wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH, wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH,
wSymbolFiles, wFieldChecks, wX, wVersion, wAdvanced, wSkipcfg, wSkipProjCfg, wSymbolFiles, wFieldChecks, wX, wVersion, wAdvanced, wSkipcfg, wSkipProjCfg,
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar, wCc, wGenscript, wCheckPoint, wThreadAnalysis, wNoMain, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wIndex, wIndex,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads, wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads,
@ -100,7 +100,7 @@ const
"gui", "passc", "t", "passl", "l", "listcmd", "gendoc", "genmapping", "os", "gui", "passc", "t", "passl", "l", "listcmd", "gendoc", "genmapping", "os",
"cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v", "cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v",
"help", "h", "symbolfiles", "fieldchecks", "x", "version", "advanced", "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", "nomain", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"index", "index",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit", "write", "putenv", "prependenv", "appendenv", "threadvar", "emit",

View file

@ -37,7 +37,7 @@ Advanced options:
--genMapping generate a mapping file containing --genMapping generate a mapping file containing
(Nimrod, mangled) identifier pairs (Nimrod, mangled) identifier pairs
--lineDir:on|off generation of #line directive on|off --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 --skipCfg do not read the general configuration file
--skipProjCfg do not read the project's configuration file --skipProjCfg do not read the project's configuration file
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC --gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC

View 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()

View 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()

View file

@ -1,4 +1,7 @@
* codegen for threadvars * 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? * add --deadlock_prevention:on|off switch? timeout for locks?
* test the sort implementation again * test the sort implementation again
@ -37,7 +40,6 @@ Bugs
for i in 0 .. high(t.data)-1: for i in 0 .. high(t.data)-1:
var maxIdx = i var maxIdx = i
for j in i+1 .. high(t.data): 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 if t.data[j].val > t.data[maxIdx].val: maxIdx = j
swap(t.data[maxIdx], t.data[i]) swap(t.data[maxIdx], t.data[i])