compiler can emulate thread local variables

This commit is contained in:
Araq
2011-06-15 02:09:02 +02:00
parent adbb48fbce
commit 4fa80956b8
10 changed files with 255 additions and 250 deletions

View File

@@ -1482,7 +1482,6 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
var
a, b, idx: TLoc
ts: string
if nfAllConst in e.flags:
putIntoDest(p, d, e.typ, genSetNode(p, e))
else:
@@ -1504,7 +1503,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
[rdLoc(d), rdSetElemLoc(a, e.typ)])
else:
# small set
ts = "NI" & $(getSize(e.typ) * 8)
var ts = "NI" & $(getSize(e.typ) * 8)
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)])
for i in countup(0, sonsLen(e) - 1):
if e.sons[i].kind == nkRange:
@@ -1645,10 +1644,17 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
of skEnumField:
putIntoDest(p, d, e.typ, toRope(sym.position))
of skVar:
if (sfGlobal in sym.flags): genVarPrototype(p.module, sym)
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
InternalError(e.info, "expr: var not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
if sfThreadVar in sym.flags:
AccessThreadLocalVar(p, sym)
if emulatedThreadVars():
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
else:
putLocIntoDest(p, d, sym.loc)
else:
putLocIntoDest(p, d, sym.loc)
of skForVar, skTemp:
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
InternalError(e.info, "expr: temp not init " & sym.name.s)
@@ -1727,7 +1733,6 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
# XXX: tySequence!
result = genConstSimpleList(p, n)
else:
# result := genLiteral(p, n)
var d: TLoc
initLocExpr(p, n, d)
result = rdLoc(d)

View File

@@ -0,0 +1,54 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Thread var support for crappy architectures that lack native support for
## thread local storage.
proc AccessThreadLocalVar(p: BProc, s: PSym) =
if optThreads in gGlobalOptions:
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
if not p.ThreadVarAccessed:
p.ThreadVarAccessed = true
p.module.usesThreadVars = true
appf(p.s[cpsLocals], "NimThreadVars* NimTV;$n")
appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n")
var
nimtv: PRope # nimrod thread vars
nimtvDeps: seq[PType] = @[]
nimtvDeclared = initIntSet()
proc emulatedThreadVars(): bool {.inline.} =
result = optThreads in gGlobalOptions and
platform.OS[targetOS].props.contains(ospLacksThreadVars)
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
if emulatedThreadVars():
# we gather all thread locals var into a struct; we need to allocate
# storage for that somehow, can't use the thread local storage
# allocator for it :-(
if not containsOrIncl(nimtvDeclared, s.id):
nimtvDeps.add(s.loc.t)
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else:
if isExtern: app(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
appf(m.s[cfsVars], " $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
for t in items(nimtvDeps): discard getTypeDesc(m, t)
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
proc GenerateThreadVarsSize(m: BModule) =
if nimtv != nil:
app(m.s[cfsProcs],
"NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}" & tnl)

View File

@@ -66,6 +66,7 @@ type
s: TCProcSections # the procs sections; short name for readability
prc: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
ThreadVarAccessed: bool # true if the proc already accessed some threadvar
nestedTryStmts: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then)
labels: Natural # for generating unique labels in the C proc
@@ -85,6 +86,7 @@ type
filename*: string
s*: TCFileSections # sections of the C file
PreventStackTrace: bool # true if stack traces need to be prevented
usesThreadVars: bool # true if the module uses a thread var
cfilename*: string # filename of the module (including path,
# without extension)
typeCache*: TIdTable # cache the generated types
@@ -386,29 +388,12 @@ proc localDebugInfo(p: BProc, s: PSym) =
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
# XXX work around a bug: No type information for open arrays possible:
if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return
if gCmd == cmdCompileToLLVM:
# "address" is the 0th field
# "typ" is the 1rst field
# "name" is the 2nd field
var name = cstringLit(p, p.s[cpsInit], normalize(s.name.s))
if (s.kind == skParam) and not ccgIntroducedPtr(s): allocParam(p, s)
inc(p.labels, 3)
appf(p.s[cpsInit], "%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n" &
"%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n" &
"%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n" &
"store i8* $2, i8** %LOC$6$n" & "store $3* $4, $3** %LOC$7$n" &
"store i8* $5, i8** %LOC$8$n", [toRope(p.frameLen), s.loc.r,
getTypeDesc(p.module, "TNimType"),
genTypeInfo(p.module, s.loc.t), name,
toRope(p.labels), toRope(p.labels - 1),
toRope(p.labels - 2)])
else:
var a = con("&", s.loc.r)
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
appf(p.s[cpsInit],
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)])
var a = con("&", s.loc.r)
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
appf(p.s[cpsInit],
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)])
inc(p.frameLen)
proc assignLocalVar(p: BProc, s: PSym) =
@@ -417,55 +402,35 @@ proc assignLocalVar(p: BProc, s: PSym) =
# for each module that uses them!
if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
if gCmd == cmdCompileToLLVM:
appf(p.s[cpsLocals], "$1 = alloca $2$n",
[s.loc.r, getTypeDesc(p.module, s.loc.t)])
incl(s.loc.flags, lfIndirect)
else:
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
app(p.s[cpsLocals], " volatile")
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
app(p.s[cpsLocals], " volatile")
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
localDebugInfo(p, s)
proc declareThreadVar(m: BModule, s: PSym) =
if optThreads in gGlobalOptions:
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
# we gather all thread locals var into a struct and put that into
# nim__dat.c; we need to allocate storage for that somehow, can't use
# the thread local storage allocator for it :-(
# XXX we need to adapt expr() too, every reference to a thread local var
# generates quite some code ...
InternalError("no workaround for lack of thread local vars implemented")
else:
app(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
else:
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
include ccgthreadvars
proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone:
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
if sfThreadVar in s.flags: declareThreadVar(p.module, s)
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
if {optStackTrace, optEndb} * p.module.module.options ==
{optStackTrace, optEndb}:
if lfNoDecl in s.loc.flags: return
if sfThreadVar in s.flags:
declareThreadVar(p.module, s, sfImportc in s.flags)
else:
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
if p.module.module.options * {optStackTrace, optEndb} ==
{optStackTrace, optEndb}:
appcg(p.module, p.module.s[cfsDebugInit],
"#dbgRegisterGlobal($1, &$2, $3);$n",
[cstringLit(p, p.module.s[cfsDebugInit],
normalize(s.owner.name.s & '.' & s.name.s)),
s.loc.r, genTypeInfo(p.module, s.typ)])
proc iff(cond: bool, the, els: PRope): PRope =
if cond: result = the
else: result = els
proc assignParam(p: BProc, s: PSym) =
assert(s.loc.r != nil)
@@ -691,9 +656,8 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.Flags:
if sym.owner.id != m.module.id and
not ContainsOrIncl(m.declaredThings, sym.id):
appff(m.s[cfsVars], "extern $1 $2;$n",
"@$2 = linkonce global $1 zeroinitializer$n",
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
appf(m.s[cfsVars], "extern $1 $2;$n",
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
elif not ContainsOrIncl(m.declaredProtos, sym.id):
appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)])
@@ -725,20 +689,16 @@ proc genVarPrototype(m: BModule, sym: PSym) =
assert(sfGlobal in sym.flags)
useHeader(m, sym)
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
if (lfNoDecl in sym.loc.Flags) or
ContainsOrIncl(m.declaredThings, sym.id):
if (lfNoDecl in sym.loc.Flags) or ContainsOrIncl(m.declaredThings, sym.id):
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.r != nil)
if gCmd == cmdCompileToLLVM:
incl(sym.loc.flags, lfIndirect)
appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
else:
if sfThreadVar in sym.flags:
declareThreadVar(m, sym, true)
else:
app(m.s[cfsVars], "extern ")
if sfThreadVar in sym.flags: declareThreadVar(m, sym)
else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
@@ -898,6 +858,7 @@ proc genInitCode(m: BModule) =
proc genModule(m: BModule, cfilenoext: string): PRope =
result = getFileHeader(cfilenoext)
generateHeaders(m)
generateThreadLocalStorage(m)
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
proc rawNewModule(module: PSym, filename: string): BModule =
@@ -990,6 +951,7 @@ proc writeModule(m: BModule) =
if sfMainModule in m.module.flags:
# generate main file:
app(m.s[cfsProcHeaders], mainModProcs)
GenerateThreadVarsSize(m)
var code = genModule(m, cfilenoext)
when hasTinyCBackend:

View File

@@ -86,14 +86,15 @@ type
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errDifferentHeaps,
errUser,
errXhasSideEffects, errIteratorExpected,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
warnDifferentHeaps,
warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
@@ -310,7 +311,6 @@ const
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
errXhasSideEffects: "\'$1\' can have side effects",
errIteratorExpected: "iterator within for loop context expected",
errDifferentHeaps: "possible inconsistency of thread local heaps",
errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
@@ -327,7 +327,8 @@ const
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]",
warnAnalysisLoophole: "thread analysis incomplete due to indirect call '$1' [AnalysisLoophole]",
warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
warnDifferentHeaps: "possible inconsistency of thread local heaps",
warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
@@ -345,12 +346,12 @@ const
hintUser: "$1 [User]"]
const
WarningsToStr*: array[0..16, string] = ["CannotOpenFile", "OctalEscape",
WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
"AnalysisLoophole", "User"]
"AnalysisLoophole", "DifferentHeaps", "User"]
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",

View File

@@ -138,7 +138,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine:
if lastOwner != owner and owner != toNil:
LocalError(n.info, errDifferentHeaps)
Message(n.info, warnDifferentHeaps)
else:
# we could not backtrack to a concrete symbol, but that's fine:
var lastOwner = analyseSym(c, n)
@@ -147,7 +147,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine:
if lastOwner != owner and owner != toNil:
LocalError(n.info, errDifferentHeaps)
Message(n.info, warnDifferentHeaps)
proc analyseAssign(c: PProcCtx, le, ri: PNode) =
var y = analyse(c, ri) # read access; ok
@@ -171,7 +171,7 @@ proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner =
newCtx.mapping[formal.id] = call.args[i-1]
pushInfoContext(n.info)
result = analyse(newCtx, prc.ast.sons[codePos])
if prc.ast.sons[codePos].kind == nkEmpty:
if prc.ast.sons[codePos].kind == nkEmpty and sfNoSideEffect notin prc.flags:
Message(n.info, warnAnalysisLoophole, renderTree(n))
if prc.typ.sons[0] != nil:
if prc.ast.len > resultPos:
@@ -221,14 +221,15 @@ template aggregateOwner(result, ana: expr) =
var a = ana # eval once
if result != a:
if result == toNil: result = a
else: localError(n.info, errDifferentHeaps)
else: Message(n.info, warnDifferentHeaps)
proc analyseArgs(c: PProcCtx, n: PNode, start = 1) =
for i in start..n.len-1: discard analyse(c, n[i])
proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
if n[0].kind != nkSym or n[0].sym.kind != skProc:
Message(n.info, warnAnalysisLoophole, renderTree(n))
if tfNoSideEffect notin n[0].typ.flags:
Message(n.info, warnAnalysisLoophole, renderTree(n))
result = toNil
else:
var prc = n[0].sym
@@ -316,7 +317,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
result = analyse(c, n.sons[0])
of nkRaiseStmt:
var a = analyse(c, n.sons[0])
if a != toMine: LocalError(n.info, errDifferentHeaps)
if a != toMine: Message(n.info, warnDifferentHeaps)
result = toVoid
of nkVarSection: result = analyseVarSection(c, n)
of nkConstSection: result = analyseConstSection(c, n)

View File

@@ -1479,8 +1479,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
strDesc.size = sizeof(string)
strDesc.kind = tyString
strDesc.flags = {ntfAcyclic}
initStackBottom()
initGC() # BUGFIX: need to be called here!
include "system/ansi_c"
@@ -1692,6 +1690,10 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when hasThreadSupport:
include "system/threads"
else:
initStackBottom()
initGC()
include "system/excpt"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
@@ -1755,8 +1757,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} =
## retrieves the current exception; if there is none, nil is returned.
ThreadGlobals()
result = ||currException
result = currException
proc getCurrentExceptionMsg*(): string {.inline.} =
## retrieves the error message that was attached to the current

View File

@@ -10,6 +10,9 @@
# Exception handling code. This is difficult because it has
# to work if there is no more memory (but it doesn't yet!).
# XXX assertions are unnecessarily complex; system should use their own
# assertion mechanism instead!
var
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
## for CGI applications
@@ -32,64 +35,51 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc.}
proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
proc chckNil(p: pointer) {.inline, compilerproc.}
when hasThreadSupport:
template ThreadGlobals =
var currentThread = ThisThread()
template `||`(varname: expr): expr = currentThread.g.varname
else:
template ThreadGlobals = nil # nothing
template `||`(varname: expr): expr = varname
var
framePtr {.rtlThreadVar.}: PFrame
excHandler {.rtlThreadVar.}: PSafePoint
# list of exception handlers
# a global variable for the root of all try blocks
currException {.rtlThreadVar.}: ref E_Base
var
framePtr: PFrame
excHandler: PSafePoint = nil
# list of exception handlers
# a global variable for the root of all try blocks
currException: ref E_Base
buf {.rtlThreadVar.}: string # cannot be allocated on the stack!
assertBuf {.rtlThreadVar.}: string
# we need a different buffer for
# assert, as it raises an exception and
# exception handler needs the buffer too
gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed
buf: string # cannot be allocated on the stack!
assertBuf: string # we need a different buffer for
# assert, as it raises an exception and
# exception handler needs the buffer too
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
gAssertionFailed: ref EAssertionFailed
new(||gAssertionFailed)
||buf = newStringOfCap(2000)
||assertBuf = newStringOfCap(2000)
proc initGlobals() =
new(gAssertionFailed)
buf = newStringOfCap(2000)
assertBuf = newStringOfCap(2000)
when not hasThreadSupport:
initGlobals()
proc pushFrame(s: PFrame) {.compilerRtl, inl.} =
ThreadGlobals()
s.prev = ||framePtr
||framePtr = s
s.prev = framePtr
framePtr = s
proc popFrame {.compilerRtl, inl.} =
ThreadGlobals()
||framePtr = (||framePtr).prev
framePtr = framePtr.prev
proc setFrame(s: PFrame) {.compilerRtl, inl.} =
ThreadGlobals()
||framePtr = s
framePtr = s
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
ThreadGlobals()
s.prev = ||excHandler
||excHandler = s
s.prev = excHandler
excHandler = s
proc popSafePoint {.compilerRtl, inl.} =
ThreadGlobals()
||excHandler = (||excHandler).prev
excHandler = excHandler.prev
proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} =
ThreadGlobals()
e.parent = ||currException
||currException = e
e.parent = currException
currException = e
proc popCurrentException {.compilerRtl, inl.} =
ThreadGlobals()
||currException = (||currException).parent
currException = currException.parent
# some platforms have native support for stack traces:
const
@@ -143,18 +133,24 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
# Once we're past signalHandler, we're at what the user is
# interested in
enabled = true
when not hasThreadSupport:
var
tempFrames: array [0..127, PFrame] # should not be alloc'd on stack
proc auxWriteStackTrace(f: PFrame, s: var string) =
const
when hasThreadSupport:
var
tempFrames: array [0..127, PFrame] # but better than a threadvar
const
firstCalls = 32
ThreadGlobals()
var
it = f
i = 0
total = 0
while it != nil and i <= high(||tempFrames)-(firstCalls-1):
while it != nil and i <= high(tempFrames)-(firstCalls-1):
# the (-1) is for a nil entry that marks where the '...' should occur
(||tempFrames)[i] = it
tempFrames[i] = it
inc(i)
inc(total)
it = it.prev
@@ -165,38 +161,37 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
(||tempFrames)[i] = nil
tempFrames[i] = nil
inc(i)
while b != nil and i <= high(||tempFrames):
(||tempFrames)[i] = b
while b != nil and i <= high(tempFrames):
tempFrames[i] = b
inc(i)
b = b.prev
for j in countdown(i-1, 0):
if (||tempFrames)[j] == nil:
if tempFrames[j] == nil:
add(s, "(")
add(s, $(total-i-1))
add(s, " calls omitted) ...")
else:
var oldLen = s.len
add(s, (||tempFrames)[j].filename)
if (||tempFrames)[j].line > 0:
add(s, tempFrames[j].filename)
if tempFrames[j].line > 0:
add(s, '(')
add(s, $(||tempFrames)[j].line)
add(s, $tempFrames[j].line)
add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, (||tempFrames)[j].procname)
add(s, tempFrames[j].procname)
add(s, stackTraceNewLine)
proc rawWriteStackTrace(s: var string) =
when nimrodStackTrace:
ThreadGlobals()
if ||framePtr == nil:
if framePtr == nil:
add(s, "No stack traceback available")
add(s, stackTraceNewLine)
else:
add(s, "Traceback (most recent call last)")
add(s, stackTraceNewLine)
auxWriteStackTrace(||framePtr, s)
auxWriteStackTrace(framePtr, s)
elif defined(nativeStackTrace) and nativeStackTraceSupported:
add(s, "Traceback from system (most recent call last)")
add(s, stackTraceNewLine)
@@ -216,53 +211,50 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
if raiseHook != nil:
if not raiseHook(e): return
GC_disable() # a bad thing is an error in the GC while raising an exception
ThreadGlobals()
if ||excHandler != nil:
if excHandler != nil:
pushCurrentException(e)
c_longjmp((||excHandler).context, 1)
c_longjmp(excHandler.context, 1)
else:
if not isNil(||buf):
setLen(||buf, 0)
rawWriteStackTrace(||buf)
if not isNil(buf):
setLen(buf, 0)
rawWriteStackTrace(buf)
if e.msg != nil and e.msg[0] != '\0':
add(||buf, "Error: unhandled exception: ")
add(||buf, $e.msg)
add(buf, "Error: unhandled exception: ")
add(buf, $e.msg)
else:
add(||buf, "Error: unhandled exception")
add(||buf, " [")
add(||buf, $ename)
add(||buf, "]\n")
writeToStdErr(||buf)
add(buf, "Error: unhandled exception")
add(buf, " [")
add(buf, $ename)
add(buf, "]\n")
writeToStdErr(buf)
else:
writeToStdErr(ename)
quitOrDebug()
GC_enable()
proc reraiseException() {.compilerRtl.} =
ThreadGlobals()
if ||currException == nil:
if currException == nil:
raise newException(ENoExceptionToReraise, "no exception to reraise")
else:
raiseException(||currException, (||currException).name)
raiseException(currException, currException.name)
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
if not cond:
ThreadGlobals()
#c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
#quit(1)
GC_disable() # BUGFIX: `$` allocates a new string object!
if not isNil(||assertBuf):
if not isNil(assertBuf):
# BUGFIX: when debugging the GC, assertBuf may be nil
setLen(||assertBuf, 0)
add(||assertBuf, "[Assertion failure] file: ")
add(||assertBuf, file)
add(||assertBuf, " line: ")
add(||assertBuf, $line)
add(||assertBuf, "\n")
(||gAssertionFailed).msg = ||assertBuf
setLen(assertBuf, 0)
add(assertBuf, "[Assertion failure] file: ")
add(assertBuf, file)
add(assertBuf, " line: ")
add(assertBuf, $line)
add(assertBuf, "\n")
gAssertionFailed.msg = assertBuf
GC_enable()
if ||gAssertionFailed != nil:
raise ||gAssertionFailed
if gAssertionFailed != nil:
raise gAssertionFailed
else:
c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
quit(1)
@@ -277,24 +269,23 @@ var
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
# print stack trace and quit
ThreadGlobals()
var s = sig
GC_disable()
setLen(||buf, 0)
rawWriteStackTrace(||buf)
setLen(buf, 0)
rawWriteStackTrace(buf)
if s == SIGINT: add(||buf, "SIGINT: Interrupted by Ctrl-C.\n")
if s == SIGINT: add(buf, "SIGINT: Interrupted by Ctrl-C.\n")
elif s == SIGSEGV:
add(||buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
add(buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
elif s == SIGABRT:
if dbgAborting: return # the debugger wants to abort
add(||buf, "SIGABRT: Abnormal termination.\n")
elif s == SIGFPE: add(||buf, "SIGFPE: Arithmetic error.\n")
elif s == SIGILL: add(||buf, "SIGILL: Illegal operation.\n")
add(buf, "SIGABRT: Abnormal termination.\n")
elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n")
elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n")
elif s == SIGBUS:
add(||buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
else: add(||buf, "unknown signal\n")
writeToStdErr(||buf)
add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
else: add(buf, "unknown signal\n")
writeToStdErr(buf)
dbgAborting = True # play safe here...
GC_enable()
quit(1) # always quit when SIGABRT

View File

@@ -63,7 +63,7 @@ type
var
stackBottom {.rtlThreadVar.}: pointer
gch {.rtlThreadVar.}: TGcHeap
cycleThreshold {.rtlThreadVar.}: int = InitialCycleThreshold
cycleThreshold {.rtlThreadVar.}: int
proc acquire(gch: var TGcHeap) {.inline.} =
when hasThreadSupport and hasSharedHeap:
@@ -267,6 +267,7 @@ proc initGC() =
when not defined(useNimRtl):
when traceGC:
for i in low(TCellState)..high(TCellState): Init(states[i])
cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0
gch.stat.cycleCollections = 0
gch.stat.maxThreshold = 0

View File

@@ -52,22 +52,22 @@ when defined(Windows):
LockSemaphore: int
Reserved: int32
proc InitSysLock(L: var TSysLock) {.stdcall,
proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`.
proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall,
proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to acquire the lock `L`.
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
result = TryAcquireSysAux(L) != 0'i32
proc AcquireSys(L: var TSysLock) {.stdcall,
proc AcquireSys(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "EnterCriticalSection".}
## Acquires the lock `L`.
proc ReleaseSys(L: var TSysLock) {.stdcall,
proc ReleaseSys(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`.
@@ -120,17 +120,17 @@ else:
header: "<sys/types.h>".} = object
proc InitSysLock(L: var TSysLock, attr: pointer = nil) {.
importc: "pthread_mutex_init", header: "<pthread.h>".}
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
proc AcquireSys(L: var TSysLock) {.
proc AcquireSys(L: var TSysLock) {.noSideEffect,
importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc TryAcquireSysAux(L: var TSysLock): cint {.
proc TryAcquireSysAux(L: var TSysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
result = TryAcquireSysAux(L) == 0'i32
proc ReleaseSys(L: var TSysLock) {.
proc ReleaseSys(L: var TSysLock) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
type
@@ -184,32 +184,25 @@ else:
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".}
proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} =
proc ThreadVarAlloc(): TThreadVarSlot {.inline.} =
discard pthread_key_create(addr(result), nil)
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.
compilerproc, inline.} =
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} =
discard pthread_setspecific(s, value)
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc, inline.} =
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.inline.} =
result = pthread_getspecific(s)
type
TGlobals {.final, pure.} = object
excHandler: PSafePoint
currException: ref E_Base
framePtr: PFrame
buf: string # cannot be allocated on the stack!
assertBuf: string # we need a different buffer for
# assert, as it raises an exception and
# exception handler needs the buffer too
gAssertionFailed: ref EAssertionFailed
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
data: float # compiler should add thread local variables here!
const emulatedThreadVars = defined(macosx)
proc initGlobals(g: var TGlobals) =
new(g.gAssertionFailed)
g.buf = newStringOfCap(2000)
g.assertBuf = newStringOfCap(2000)
when emulatedThreadVars:
# the compiler generates this proc for us, so that we can get the size of
# the thread local var block:
proc NimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
proc ThreadVarsAlloc(size: int): pointer =
result = c_malloc(size)
zeroMem(result, size)
proc ThreadVarsDealloc(p: pointer) {.importc: "free", nodecl.}
proc initGlobals()
type
PGcThread = ptr TGcThread
@@ -218,7 +211,6 @@ type
next, prev: PGcThread
stackBottom, stackTop, threadLocalStorage: pointer
stackSize: int
g: TGlobals
locksLen: int
locks: array [0..MaxLocksPerThread-1, pointer]
registers: array[0..maxRegisters-1, pointer] # register contents for GC
@@ -240,8 +232,14 @@ proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
# of all threads; it's not to be stopped etc.
when not defined(useNimRtl):
var mainThread: TGcThread
initGlobals(mainThread.g)
ThreadVarSetValue(globalsSlot, addr(mainThread))
when emulatedThreadVars:
mainThread.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
initStackBottom()
initGC()
initGlobals()
var heapLock: TSysLock
InitSysLock(HeapLock)
@@ -296,15 +294,21 @@ when not defined(boehmgc) and not hasSharedHeap:
template ThreadProcWrapperBody(closure: expr) =
ThreadVarSetValue(globalsSlot, closure)
var t = cast[ptr TThread[TParam]](closure)
when emulatedThreadVars:
t.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
when not defined(boehmgc) and not hasSharedHeap:
# init the GC for this thread:
setStackBottom(addr(t))
initGC()
t.stackBottom = addr(t)
initGlobals()
registerThread(t)
try:
t.fn(t.data)
finally:
# XXX shut-down is not executed when the thread is forced down!
when emulatedThreadVars:
ThreadVarsDealloc(t.threadLocalStorage)
unregisterThread(t)
when defined(deallocOsPages): deallocOsPages()

View File

@@ -1,14 +1,17 @@
* codegen for threadvars
High priority (version 0.8.12)
==============================
* implement message passing built-ins
* add --deadlock_prevention:on|off switch? timeout for locks?
* test the sort implementation again
* iterators should not always be destructive!
* real types for template results
* built-in serialization
High priority (version 0.9.0)
=============================
version 0.9.0
=============
- iterators should not always be destructive!
- test the sort implementation again
- warning for implicit openArray -> varargs convention
- implement explicit varargs
- tests: run modules that contain "#RUN_ME", compile the other
@@ -17,6 +20,7 @@ High priority (version 0.9.0)
- fix overloading resolution
- wrong co-/contravariance
- make ^ available as operator
- implement closures for the C code generator
Bugs
----
@@ -33,26 +37,13 @@ Bugs
--> system.swap or genericAssign is broken! And indeed, if reference counts
are not modified and the GC is triggered in between a swap, bad things
may happen!
proc sort*[A](t: var TCountTable[A]) =
for i in 0 .. high(t.data)-1:
var maxIdx = i
for j in i+1 .. high(t.data):
if t.data[j].val > t.data[maxIdx].val: maxIdx = j
swap(t.data[maxIdx], t.data[i])
To implement
------------
version 0.9.XX
==============
* distinct types for array/seq indexes
* implement closures for the C code generator
* GC: marker procs for native Nimrod GC and Boehm GC
* built-in serialization
Low priority
------------
- distinct types for array/seq indexes
- GC: marker procs for native Nimrod GC and Boehm GC
- implicit ref/ptr->var conversion; the compiler may store an object
implicitly on the heap for write barrier efficiency
- resizing of strings/sequences could take into account the memory that
@@ -61,9 +52,8 @@ Low priority
- find a way to reintroduce the cleanup() pass for C code generation: this
is hard because of partial evaluation --> symbol files will fix this as
a side effect
- floating point checks for EcmaScript
- EcmaScript needs a new and better code gen: simply adapt the C code gen to it
- prefer proc in current module over other procs with same overloading result?
- real types for template results
- generalized case statement (requires better transf)
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
- nested tuple unpacking
@@ -96,7 +86,7 @@ Library
Version 2
---------
=========
- language change: inheritance should only work with reference types, so that
the ``type`` field is not needed for objects! --> zero overhead aggregation
@@ -123,6 +113,8 @@ Version 2
var x = myProc() # checks myProc() initializes every pointer explicitely
- the two other parsers
- rethink the syntax: distinction between expr and stmt is unfortunate;
indentation handling is quite complex too
Low priority
@@ -135,10 +127,3 @@ Low priority
important than constructors)
- code generated for type information is wasteful
Other ideas
-----------
- startsWith `=^`
- endsWith `=$`
- ignore case `=?` --> `=$?` too?