basic thread analysis working

This commit is contained in:
Araq
2011-06-13 16:22:19 +02:00
parent c019d17561
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,
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)

View File

@@ -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:

View File

@@ -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 = ""

View File

@@ -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

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

View File

@@ -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

View File

@@ -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",

View File

@@ -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

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
* 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])