compiler can emulate thread local variables
This commit is contained in:
parent
adbb48fbce
commit
4fa80956b8
10 changed files with 255 additions and 250 deletions
|
|
@ -1482,7 +1482,6 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
|
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
|
||||||
var
|
var
|
||||||
a, b, idx: TLoc
|
a, b, idx: TLoc
|
||||||
ts: string
|
|
||||||
if nfAllConst in e.flags:
|
if nfAllConst in e.flags:
|
||||||
putIntoDest(p, d, e.typ, genSetNode(p, e))
|
putIntoDest(p, d, e.typ, genSetNode(p, e))
|
||||||
else:
|
else:
|
||||||
|
|
@ -1504,7 +1503,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
[rdLoc(d), rdSetElemLoc(a, e.typ)])
|
[rdLoc(d), rdSetElemLoc(a, e.typ)])
|
||||||
else:
|
else:
|
||||||
# small set
|
# small set
|
||||||
ts = "NI" & $(getSize(e.typ) * 8)
|
var ts = "NI" & $(getSize(e.typ) * 8)
|
||||||
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)])
|
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)])
|
||||||
for i in countup(0, sonsLen(e) - 1):
|
for i in countup(0, sonsLen(e) - 1):
|
||||||
if e.sons[i].kind == nkRange:
|
if e.sons[i].kind == nkRange:
|
||||||
|
|
@ -1645,10 +1644,17 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
of skEnumField:
|
of skEnumField:
|
||||||
putIntoDest(p, d, e.typ, toRope(sym.position))
|
putIntoDest(p, d, e.typ, toRope(sym.position))
|
||||||
of skVar:
|
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)):
|
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
|
||||||
InternalError(e.info, "expr: var not init " & sym.name.s)
|
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:
|
of skForVar, skTemp:
|
||||||
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
|
if ((sym.loc.r == nil) or (sym.loc.t == nil)):
|
||||||
InternalError(e.info, "expr: temp not init " & sym.name.s)
|
InternalError(e.info, "expr: temp not init " & sym.name.s)
|
||||||
|
|
@ -1727,7 +1733,6 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
|
||||||
# XXX: tySequence!
|
# XXX: tySequence!
|
||||||
result = genConstSimpleList(p, n)
|
result = genConstSimpleList(p, n)
|
||||||
else:
|
else:
|
||||||
# result := genLiteral(p, n)
|
|
||||||
var d: TLoc
|
var d: TLoc
|
||||||
initLocExpr(p, n, d)
|
initLocExpr(p, n, d)
|
||||||
result = rdLoc(d)
|
result = rdLoc(d)
|
||||||
|
|
|
||||||
54
compiler/ccgthreadvars.nim
Normal file
54
compiler/ccgthreadvars.nim
Normal 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)
|
||||||
|
|
||||||
|
|
@ -66,6 +66,7 @@ type
|
||||||
s: TCProcSections # the procs sections; short name for readability
|
s: TCProcSections # the procs sections; short name for readability
|
||||||
prc: PSym # the Nimrod proc that this C proc belongs to
|
prc: PSym # the Nimrod proc that this C proc belongs to
|
||||||
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
|
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
|
nestedTryStmts: seq[PNode] # in how many nested try statements we are
|
||||||
# (the vars must be volatile then)
|
# (the vars must be volatile then)
|
||||||
labels: Natural # for generating unique labels in the C proc
|
labels: Natural # for generating unique labels in the C proc
|
||||||
|
|
@ -85,6 +86,7 @@ type
|
||||||
filename*: string
|
filename*: string
|
||||||
s*: TCFileSections # sections of the C file
|
s*: TCFileSections # sections of the C file
|
||||||
PreventStackTrace: bool # true if stack traces need to be prevented
|
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,
|
cfilename*: string # filename of the module (including path,
|
||||||
# without extension)
|
# without extension)
|
||||||
typeCache*: TIdTable # cache the generated types
|
typeCache*: TIdTable # cache the generated types
|
||||||
|
|
@ -386,29 +388,12 @@ proc localDebugInfo(p: BProc, s: PSym) =
|
||||||
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
|
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
|
||||||
# XXX work around a bug: No type information for open arrays possible:
|
# XXX work around a bug: No type information for open arrays possible:
|
||||||
if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return
|
if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return
|
||||||
if gCmd == cmdCompileToLLVM:
|
var a = con("&", s.loc.r)
|
||||||
# "address" is the 0th field
|
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
|
||||||
# "typ" is the 1rst field
|
appf(p.s[cpsInit],
|
||||||
# "name" is the 2nd field
|
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
|
||||||
var name = cstringLit(p, p.s[cpsInit], normalize(s.name.s))
|
[toRope(p.frameLen), makeCString(normalize(s.name.s)), a,
|
||||||
if (s.kind == skParam) and not ccgIntroducedPtr(s): allocParam(p, s)
|
genTypeInfo(p.module, s.loc.t)])
|
||||||
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)])
|
|
||||||
inc(p.frameLen)
|
inc(p.frameLen)
|
||||||
|
|
||||||
proc assignLocalVar(p: BProc, s: PSym) =
|
proc assignLocalVar(p: BProc, s: PSym) =
|
||||||
|
|
@ -417,55 +402,35 @@ proc assignLocalVar(p: BProc, s: PSym) =
|
||||||
# for each module that uses them!
|
# for each module that uses them!
|
||||||
if s.loc.k == locNone:
|
if s.loc.k == locNone:
|
||||||
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
|
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
|
||||||
if gCmd == cmdCompileToLLVM:
|
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
|
||||||
appf(p.s[cpsLocals], "$1 = alloca $2$n",
|
if sfRegister in s.flags: app(p.s[cpsLocals], " register")
|
||||||
[s.loc.r, getTypeDesc(p.module, s.loc.t)])
|
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
|
||||||
incl(s.loc.flags, lfIndirect)
|
app(p.s[cpsLocals], " volatile")
|
||||||
else:
|
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)
|
localDebugInfo(p, s)
|
||||||
|
|
||||||
proc declareThreadVar(m: BModule, s: PSym) =
|
include ccgthreadvars
|
||||||
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))
|
|
||||||
|
|
||||||
proc assignGlobalVar(p: BProc, s: PSym) =
|
proc assignGlobalVar(p: BProc, s: PSym) =
|
||||||
if s.loc.k == locNone:
|
if s.loc.k == locNone:
|
||||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
|
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
|
||||||
useHeader(p.module, s)
|
useHeader(p.module, s)
|
||||||
if lfNoDecl in s.loc.flags: return
|
if lfNoDecl in s.loc.flags: return
|
||||||
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
if sfThreadVar in s.flags:
|
||||||
if sfThreadVar in s.flags: declareThreadVar(p.module, s)
|
declareThreadVar(p.module, s, sfImportc in s.flags)
|
||||||
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
|
else:
|
||||||
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
|
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
||||||
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
|
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
|
||||||
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
|
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
|
||||||
if {optStackTrace, optEndb} * p.module.module.options ==
|
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
|
||||||
{optStackTrace, optEndb}:
|
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],
|
appcg(p.module, p.module.s[cfsDebugInit],
|
||||||
"#dbgRegisterGlobal($1, &$2, $3);$n",
|
"#dbgRegisterGlobal($1, &$2, $3);$n",
|
||||||
[cstringLit(p, p.module.s[cfsDebugInit],
|
[cstringLit(p, p.module.s[cfsDebugInit],
|
||||||
normalize(s.owner.name.s & '.' & s.name.s)),
|
normalize(s.owner.name.s & '.' & s.name.s)),
|
||||||
s.loc.r, genTypeInfo(p.module, s.typ)])
|
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) =
|
proc assignParam(p: BProc, s: PSym) =
|
||||||
assert(s.loc.r != nil)
|
assert(s.loc.r != nil)
|
||||||
|
|
@ -691,9 +656,8 @@ proc genProcPrototype(m: BModule, sym: PSym) =
|
||||||
if lfDynamicLib in sym.loc.Flags:
|
if lfDynamicLib in sym.loc.Flags:
|
||||||
if sym.owner.id != m.module.id and
|
if sym.owner.id != m.module.id and
|
||||||
not ContainsOrIncl(m.declaredThings, sym.id):
|
not ContainsOrIncl(m.declaredThings, sym.id):
|
||||||
appff(m.s[cfsVars], "extern $1 $2;$n",
|
appf(m.s[cfsVars], "extern $1 $2;$n",
|
||||||
"@$2 = linkonce global $1 zeroinitializer$n",
|
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
|
||||||
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
|
|
||||||
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
|
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
|
||||||
elif not ContainsOrIncl(m.declaredProtos, sym.id):
|
elif not ContainsOrIncl(m.declaredProtos, sym.id):
|
||||||
appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)])
|
appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)])
|
||||||
|
|
@ -725,20 +689,16 @@ proc genVarPrototype(m: BModule, sym: PSym) =
|
||||||
assert(sfGlobal in sym.flags)
|
assert(sfGlobal in sym.flags)
|
||||||
useHeader(m, sym)
|
useHeader(m, sym)
|
||||||
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
|
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
|
||||||
if (lfNoDecl in sym.loc.Flags) or
|
if (lfNoDecl in sym.loc.Flags) or ContainsOrIncl(m.declaredThings, sym.id):
|
||||||
ContainsOrIncl(m.declaredThings, sym.id):
|
|
||||||
return
|
return
|
||||||
if sym.owner.id != m.module.id:
|
if sym.owner.id != m.module.id:
|
||||||
# else we already have the symbol generated!
|
# else we already have the symbol generated!
|
||||||
assert(sym.loc.r != nil)
|
assert(sym.loc.r != nil)
|
||||||
if gCmd == cmdCompileToLLVM:
|
if sfThreadVar in sym.flags:
|
||||||
incl(sym.loc.flags, lfIndirect)
|
declareThreadVar(m, sym, true)
|
||||||
appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
|
else:
|
||||||
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
|
|
||||||
else:
|
|
||||||
app(m.s[cfsVars], "extern ")
|
app(m.s[cfsVars], "extern ")
|
||||||
if sfThreadVar in sym.flags: declareThreadVar(m, sym)
|
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
|
||||||
else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
|
|
||||||
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
|
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
|
||||||
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
|
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
|
||||||
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
|
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
|
||||||
|
|
@ -898,6 +858,7 @@ proc genInitCode(m: BModule) =
|
||||||
proc genModule(m: BModule, cfilenoext: string): PRope =
|
proc genModule(m: BModule, cfilenoext: string): PRope =
|
||||||
result = getFileHeader(cfilenoext)
|
result = getFileHeader(cfilenoext)
|
||||||
generateHeaders(m)
|
generateHeaders(m)
|
||||||
|
generateThreadLocalStorage(m)
|
||||||
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
|
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
|
||||||
|
|
||||||
proc rawNewModule(module: PSym, filename: string): BModule =
|
proc rawNewModule(module: PSym, filename: string): BModule =
|
||||||
|
|
@ -990,6 +951,7 @@ proc writeModule(m: BModule) =
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
# generate main file:
|
# generate main file:
|
||||||
app(m.s[cfsProcHeaders], mainModProcs)
|
app(m.s[cfsProcHeaders], mainModProcs)
|
||||||
|
GenerateThreadVarsSize(m)
|
||||||
var code = genModule(m, cfilenoext)
|
var code = genModule(m, cfilenoext)
|
||||||
|
|
||||||
when hasTinyCBackend:
|
when hasTinyCBackend:
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,15 @@ type
|
||||||
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
||||||
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
||||||
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
||||||
errXhasSideEffects, errIteratorExpected, errDifferentHeaps,
|
errXhasSideEffects, errIteratorExpected,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||||
warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated,
|
warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated,
|
||||||
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
|
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
|
||||||
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
||||||
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
|
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
|
||||||
|
warnDifferentHeaps,
|
||||||
warnUser,
|
warnUser,
|
||||||
hintSuccess, hintSuccessX,
|
hintSuccess, hintSuccessX,
|
||||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||||
|
|
@ -310,7 +311,6 @@ const
|
||||||
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
|
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
|
||||||
errXhasSideEffects: "\'$1\' can have side effects",
|
errXhasSideEffects: "\'$1\' can have side effects",
|
||||||
errIteratorExpected: "iterator within for loop context expected",
|
errIteratorExpected: "iterator within for loop context expected",
|
||||||
errDifferentHeaps: "possible inconsistency of thread local heaps",
|
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||||
|
|
@ -327,7 +327,8 @@ const
|
||||||
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
|
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
|
||||||
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
|
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
|
||||||
warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]",
|
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]",
|
warnUser: "$1 [User]",
|
||||||
hintSuccess: "operation successful [Success]",
|
hintSuccess: "operation successful [Success]",
|
||||||
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
|
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
|
||||||
|
|
@ -345,12 +346,12 @@ const
|
||||||
hintUser: "$1 [User]"]
|
hintUser: "$1 [User]"]
|
||||||
|
|
||||||
const
|
const
|
||||||
WarningsToStr*: array[0..16, string] = ["CannotOpenFile", "OctalEscape",
|
WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape",
|
||||||
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
|
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
|
||||||
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
|
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
|
||||||
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
|
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
|
||||||
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
|
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
|
||||||
"AnalysisLoophole", "User"]
|
"AnalysisLoophole", "DifferentHeaps", "User"]
|
||||||
|
|
||||||
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
|
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
|
||||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
|
||||||
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
|
of toVoid, toUndefined: InternalError(n.info, "writeAccess")
|
||||||
of toTheirs, toMine:
|
of toTheirs, toMine:
|
||||||
if lastOwner != owner and owner != toNil:
|
if lastOwner != owner and owner != toNil:
|
||||||
LocalError(n.info, errDifferentHeaps)
|
Message(n.info, warnDifferentHeaps)
|
||||||
else:
|
else:
|
||||||
# we could not backtrack to a concrete symbol, but that's fine:
|
# we could not backtrack to a concrete symbol, but that's fine:
|
||||||
var lastOwner = analyseSym(c, n)
|
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 toVoid, toUndefined: InternalError(n.info, "writeAccess")
|
||||||
of toTheirs, toMine:
|
of toTheirs, toMine:
|
||||||
if lastOwner != owner and owner != toNil:
|
if lastOwner != owner and owner != toNil:
|
||||||
LocalError(n.info, errDifferentHeaps)
|
Message(n.info, warnDifferentHeaps)
|
||||||
|
|
||||||
proc analyseAssign(c: PProcCtx, le, ri: PNode) =
|
proc analyseAssign(c: PProcCtx, le, ri: PNode) =
|
||||||
var y = analyse(c, ri) # read access; ok
|
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]
|
newCtx.mapping[formal.id] = call.args[i-1]
|
||||||
pushInfoContext(n.info)
|
pushInfoContext(n.info)
|
||||||
result = analyse(newCtx, prc.ast.sons[codePos])
|
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))
|
Message(n.info, warnAnalysisLoophole, renderTree(n))
|
||||||
if prc.typ.sons[0] != nil:
|
if prc.typ.sons[0] != nil:
|
||||||
if prc.ast.len > resultPos:
|
if prc.ast.len > resultPos:
|
||||||
|
|
@ -221,14 +221,15 @@ template aggregateOwner(result, ana: expr) =
|
||||||
var a = ana # eval once
|
var a = ana # eval once
|
||||||
if result != a:
|
if result != a:
|
||||||
if result == toNil: 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) =
|
proc analyseArgs(c: PProcCtx, n: PNode, start = 1) =
|
||||||
for i in start..n.len-1: discard analyse(c, n[i])
|
for i in start..n.len-1: discard analyse(c, n[i])
|
||||||
|
|
||||||
proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
|
proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
|
||||||
if n[0].kind != nkSym or n[0].sym.kind != skProc:
|
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
|
result = toNil
|
||||||
else:
|
else:
|
||||||
var prc = n[0].sym
|
var prc = n[0].sym
|
||||||
|
|
@ -316,7 +317,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
|
||||||
result = analyse(c, n.sons[0])
|
result = analyse(c, n.sons[0])
|
||||||
of nkRaiseStmt:
|
of nkRaiseStmt:
|
||||||
var a = analyse(c, n.sons[0])
|
var a = analyse(c, n.sons[0])
|
||||||
if a != toMine: LocalError(n.info, errDifferentHeaps)
|
if a != toMine: Message(n.info, warnDifferentHeaps)
|
||||||
result = toVoid
|
result = toVoid
|
||||||
of nkVarSection: result = analyseVarSection(c, n)
|
of nkVarSection: result = analyseVarSection(c, n)
|
||||||
of nkConstSection: result = analyseConstSection(c, n)
|
of nkConstSection: result = analyseConstSection(c, n)
|
||||||
|
|
|
||||||
|
|
@ -1479,8 +1479,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
strDesc.size = sizeof(string)
|
strDesc.size = sizeof(string)
|
||||||
strDesc.kind = tyString
|
strDesc.kind = tyString
|
||||||
strDesc.flags = {ntfAcyclic}
|
strDesc.flags = {ntfAcyclic}
|
||||||
initStackBottom()
|
|
||||||
initGC() # BUGFIX: need to be called here!
|
|
||||||
|
|
||||||
include "system/ansi_c"
|
include "system/ansi_c"
|
||||||
|
|
||||||
|
|
@ -1692,6 +1690,10 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
|
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
include "system/threads"
|
include "system/threads"
|
||||||
|
else:
|
||||||
|
initStackBottom()
|
||||||
|
initGC()
|
||||||
|
|
||||||
include "system/excpt"
|
include "system/excpt"
|
||||||
# we cannot compile this with stack tracing on
|
# we cannot compile this with stack tracing on
|
||||||
# as it would recurse endlessly!
|
# as it would recurse endlessly!
|
||||||
|
|
@ -1755,8 +1757,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
|
|
||||||
proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} =
|
proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} =
|
||||||
## retrieves the current exception; if there is none, nil is returned.
|
## retrieves the current exception; if there is none, nil is returned.
|
||||||
ThreadGlobals()
|
result = currException
|
||||||
result = ||currException
|
|
||||||
|
|
||||||
proc getCurrentExceptionMsg*(): string {.inline.} =
|
proc getCurrentExceptionMsg*(): string {.inline.} =
|
||||||
## retrieves the error message that was attached to the current
|
## retrieves the error message that was attached to the current
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
# Exception handling code. This is difficult because it has
|
# Exception handling code. This is difficult because it has
|
||||||
# to work if there is no more memory (but it doesn't yet!).
|
# 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
|
var
|
||||||
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
|
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
|
||||||
## for CGI applications
|
## 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 chckRangeF(x, a, b: float): float {.inline, compilerproc.}
|
||||||
proc chckNil(p: pointer) {.inline, compilerproc.}
|
proc chckNil(p: pointer) {.inline, compilerproc.}
|
||||||
|
|
||||||
when hasThreadSupport:
|
var
|
||||||
template ThreadGlobals =
|
framePtr {.rtlThreadVar.}: PFrame
|
||||||
var currentThread = ThisThread()
|
excHandler {.rtlThreadVar.}: PSafePoint
|
||||||
template `||`(varname: expr): expr = currentThread.g.varname
|
# list of exception handlers
|
||||||
|
# a global variable for the root of all try blocks
|
||||||
else:
|
currException {.rtlThreadVar.}: ref E_Base
|
||||||
template ThreadGlobals = nil # nothing
|
|
||||||
template `||`(varname: expr): expr = varname
|
|
||||||
|
|
||||||
var
|
buf {.rtlThreadVar.}: string # cannot be allocated on the stack!
|
||||||
framePtr: PFrame
|
assertBuf {.rtlThreadVar.}: string
|
||||||
excHandler: PSafePoint = nil
|
# we need a different buffer for
|
||||||
# list of exception handlers
|
# assert, as it raises an exception and
|
||||||
# a global variable for the root of all try blocks
|
# exception handler needs the buffer too
|
||||||
currException: ref E_Base
|
gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed
|
||||||
|
|
||||||
buf: string # cannot be allocated on the stack!
|
proc initGlobals() =
|
||||||
assertBuf: string # we need a different buffer for
|
new(gAssertionFailed)
|
||||||
# assert, as it raises an exception and
|
buf = newStringOfCap(2000)
|
||||||
# exception handler needs the buffer too
|
assertBuf = newStringOfCap(2000)
|
||||||
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
|
||||||
gAssertionFailed: ref EAssertionFailed
|
|
||||||
|
|
||||||
new(||gAssertionFailed)
|
|
||||||
||buf = newStringOfCap(2000)
|
|
||||||
||assertBuf = newStringOfCap(2000)
|
|
||||||
|
|
||||||
|
when not hasThreadSupport:
|
||||||
|
initGlobals()
|
||||||
|
|
||||||
proc pushFrame(s: PFrame) {.compilerRtl, inl.} =
|
proc pushFrame(s: PFrame) {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
s.prev = framePtr
|
||||||
s.prev = ||framePtr
|
framePtr = s
|
||||||
||framePtr = s
|
|
||||||
|
|
||||||
proc popFrame {.compilerRtl, inl.} =
|
proc popFrame {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
framePtr = framePtr.prev
|
||||||
||framePtr = (||framePtr).prev
|
|
||||||
|
|
||||||
proc setFrame(s: PFrame) {.compilerRtl, inl.} =
|
proc setFrame(s: PFrame) {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
framePtr = s
|
||||||
||framePtr = s
|
|
||||||
|
|
||||||
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
s.prev = excHandler
|
||||||
s.prev = ||excHandler
|
excHandler = s
|
||||||
||excHandler = s
|
|
||||||
|
|
||||||
proc popSafePoint {.compilerRtl, inl.} =
|
proc popSafePoint {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
excHandler = excHandler.prev
|
||||||
||excHandler = (||excHandler).prev
|
|
||||||
|
|
||||||
proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} =
|
proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
e.parent = currException
|
||||||
e.parent = ||currException
|
currException = e
|
||||||
||currException = e
|
|
||||||
|
|
||||||
proc popCurrentException {.compilerRtl, inl.} =
|
proc popCurrentException {.compilerRtl, inl.} =
|
||||||
ThreadGlobals()
|
currException = currException.parent
|
||||||
||currException = (||currException).parent
|
|
||||||
|
|
||||||
# some platforms have native support for stack traces:
|
# some platforms have native support for stack traces:
|
||||||
const
|
const
|
||||||
|
|
@ -143,18 +133,24 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
|
||||||
# Once we're past signalHandler, we're at what the user is
|
# Once we're past signalHandler, we're at what the user is
|
||||||
# interested in
|
# interested in
|
||||||
enabled = true
|
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) =
|
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
const
|
when hasThreadSupport:
|
||||||
|
var
|
||||||
|
tempFrames: array [0..127, PFrame] # but better than a threadvar
|
||||||
|
const
|
||||||
firstCalls = 32
|
firstCalls = 32
|
||||||
ThreadGlobals()
|
|
||||||
var
|
var
|
||||||
it = f
|
it = f
|
||||||
i = 0
|
i = 0
|
||||||
total = 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
|
# the (-1) is for a nil entry that marks where the '...' should occur
|
||||||
(||tempFrames)[i] = it
|
tempFrames[i] = it
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(total)
|
inc(total)
|
||||||
it = it.prev
|
it = it.prev
|
||||||
|
|
@ -165,38 +161,37 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
for j in 1..total-i-(firstCalls-1):
|
for j in 1..total-i-(firstCalls-1):
|
||||||
if b != nil: b = b.prev
|
if b != nil: b = b.prev
|
||||||
if total != i:
|
if total != i:
|
||||||
(||tempFrames)[i] = nil
|
tempFrames[i] = nil
|
||||||
inc(i)
|
inc(i)
|
||||||
while b != nil and i <= high(||tempFrames):
|
while b != nil and i <= high(tempFrames):
|
||||||
(||tempFrames)[i] = b
|
tempFrames[i] = b
|
||||||
inc(i)
|
inc(i)
|
||||||
b = b.prev
|
b = b.prev
|
||||||
for j in countdown(i-1, 0):
|
for j in countdown(i-1, 0):
|
||||||
if (||tempFrames)[j] == nil:
|
if tempFrames[j] == nil:
|
||||||
add(s, "(")
|
add(s, "(")
|
||||||
add(s, $(total-i-1))
|
add(s, $(total-i-1))
|
||||||
add(s, " calls omitted) ...")
|
add(s, " calls omitted) ...")
|
||||||
else:
|
else:
|
||||||
var oldLen = s.len
|
var oldLen = s.len
|
||||||
add(s, (||tempFrames)[j].filename)
|
add(s, tempFrames[j].filename)
|
||||||
if (||tempFrames)[j].line > 0:
|
if tempFrames[j].line > 0:
|
||||||
add(s, '(')
|
add(s, '(')
|
||||||
add(s, $(||tempFrames)[j].line)
|
add(s, $tempFrames[j].line)
|
||||||
add(s, ')')
|
add(s, ')')
|
||||||
for k in 1..max(1, 25-(s.len-oldLen)): 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)
|
add(s, stackTraceNewLine)
|
||||||
|
|
||||||
proc rawWriteStackTrace(s: var string) =
|
proc rawWriteStackTrace(s: var string) =
|
||||||
when nimrodStackTrace:
|
when nimrodStackTrace:
|
||||||
ThreadGlobals()
|
if framePtr == nil:
|
||||||
if ||framePtr == nil:
|
|
||||||
add(s, "No stack traceback available")
|
add(s, "No stack traceback available")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
else:
|
else:
|
||||||
add(s, "Traceback (most recent call last)")
|
add(s, "Traceback (most recent call last)")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
auxWriteStackTrace(||framePtr, s)
|
auxWriteStackTrace(framePtr, s)
|
||||||
elif defined(nativeStackTrace) and nativeStackTraceSupported:
|
elif defined(nativeStackTrace) and nativeStackTraceSupported:
|
||||||
add(s, "Traceback from system (most recent call last)")
|
add(s, "Traceback from system (most recent call last)")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
|
|
@ -216,53 +211,50 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
|
||||||
if raiseHook != nil:
|
if raiseHook != nil:
|
||||||
if not raiseHook(e): return
|
if not raiseHook(e): return
|
||||||
GC_disable() # a bad thing is an error in the GC while raising an exception
|
GC_disable() # a bad thing is an error in the GC while raising an exception
|
||||||
ThreadGlobals()
|
if excHandler != nil:
|
||||||
if ||excHandler != nil:
|
|
||||||
pushCurrentException(e)
|
pushCurrentException(e)
|
||||||
c_longjmp((||excHandler).context, 1)
|
c_longjmp(excHandler.context, 1)
|
||||||
else:
|
else:
|
||||||
if not isNil(||buf):
|
if not isNil(buf):
|
||||||
setLen(||buf, 0)
|
setLen(buf, 0)
|
||||||
rawWriteStackTrace(||buf)
|
rawWriteStackTrace(buf)
|
||||||
if e.msg != nil and e.msg[0] != '\0':
|
if e.msg != nil and e.msg[0] != '\0':
|
||||||
add(||buf, "Error: unhandled exception: ")
|
add(buf, "Error: unhandled exception: ")
|
||||||
add(||buf, $e.msg)
|
add(buf, $e.msg)
|
||||||
else:
|
else:
|
||||||
add(||buf, "Error: unhandled exception")
|
add(buf, "Error: unhandled exception")
|
||||||
add(||buf, " [")
|
add(buf, " [")
|
||||||
add(||buf, $ename)
|
add(buf, $ename)
|
||||||
add(||buf, "]\n")
|
add(buf, "]\n")
|
||||||
writeToStdErr(||buf)
|
writeToStdErr(buf)
|
||||||
else:
|
else:
|
||||||
writeToStdErr(ename)
|
writeToStdErr(ename)
|
||||||
quitOrDebug()
|
quitOrDebug()
|
||||||
GC_enable()
|
GC_enable()
|
||||||
|
|
||||||
proc reraiseException() {.compilerRtl.} =
|
proc reraiseException() {.compilerRtl.} =
|
||||||
ThreadGlobals()
|
if currException == nil:
|
||||||
if ||currException == nil:
|
|
||||||
raise newException(ENoExceptionToReraise, "no exception to reraise")
|
raise newException(ENoExceptionToReraise, "no exception to reraise")
|
||||||
else:
|
else:
|
||||||
raiseException(||currException, (||currException).name)
|
raiseException(currException, currException.name)
|
||||||
|
|
||||||
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
||||||
if not cond:
|
if not cond:
|
||||||
ThreadGlobals()
|
|
||||||
#c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
#c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
||||||
#quit(1)
|
#quit(1)
|
||||||
GC_disable() # BUGFIX: `$` allocates a new string object!
|
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
|
# BUGFIX: when debugging the GC, assertBuf may be nil
|
||||||
setLen(||assertBuf, 0)
|
setLen(assertBuf, 0)
|
||||||
add(||assertBuf, "[Assertion failure] file: ")
|
add(assertBuf, "[Assertion failure] file: ")
|
||||||
add(||assertBuf, file)
|
add(assertBuf, file)
|
||||||
add(||assertBuf, " line: ")
|
add(assertBuf, " line: ")
|
||||||
add(||assertBuf, $line)
|
add(assertBuf, $line)
|
||||||
add(||assertBuf, "\n")
|
add(assertBuf, "\n")
|
||||||
(||gAssertionFailed).msg = ||assertBuf
|
gAssertionFailed.msg = assertBuf
|
||||||
GC_enable()
|
GC_enable()
|
||||||
if ||gAssertionFailed != nil:
|
if gAssertionFailed != nil:
|
||||||
raise ||gAssertionFailed
|
raise gAssertionFailed
|
||||||
else:
|
else:
|
||||||
c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
@ -277,24 +269,23 @@ var
|
||||||
|
|
||||||
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
|
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
|
||||||
# print stack trace and quit
|
# print stack trace and quit
|
||||||
ThreadGlobals()
|
|
||||||
var s = sig
|
var s = sig
|
||||||
GC_disable()
|
GC_disable()
|
||||||
setLen(||buf, 0)
|
setLen(buf, 0)
|
||||||
rawWriteStackTrace(||buf)
|
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:
|
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:
|
elif s == SIGABRT:
|
||||||
if dbgAborting: return # the debugger wants to abort
|
if dbgAborting: return # the debugger wants to abort
|
||||||
add(||buf, "SIGABRT: Abnormal termination.\n")
|
add(buf, "SIGABRT: Abnormal termination.\n")
|
||||||
elif s == SIGFPE: add(||buf, "SIGFPE: Arithmetic error.\n")
|
elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n")
|
||||||
elif s == SIGILL: add(||buf, "SIGILL: Illegal operation.\n")
|
elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n")
|
||||||
elif s == SIGBUS:
|
elif s == SIGBUS:
|
||||||
add(||buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
|
add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
|
||||||
else: add(||buf, "unknown signal\n")
|
else: add(buf, "unknown signal\n")
|
||||||
writeToStdErr(||buf)
|
writeToStdErr(buf)
|
||||||
dbgAborting = True # play safe here...
|
dbgAborting = True # play safe here...
|
||||||
GC_enable()
|
GC_enable()
|
||||||
quit(1) # always quit when SIGABRT
|
quit(1) # always quit when SIGABRT
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ type
|
||||||
var
|
var
|
||||||
stackBottom {.rtlThreadVar.}: pointer
|
stackBottom {.rtlThreadVar.}: pointer
|
||||||
gch {.rtlThreadVar.}: TGcHeap
|
gch {.rtlThreadVar.}: TGcHeap
|
||||||
cycleThreshold {.rtlThreadVar.}: int = InitialCycleThreshold
|
cycleThreshold {.rtlThreadVar.}: int
|
||||||
|
|
||||||
proc acquire(gch: var TGcHeap) {.inline.} =
|
proc acquire(gch: var TGcHeap) {.inline.} =
|
||||||
when hasThreadSupport and hasSharedHeap:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
|
|
@ -267,6 +267,7 @@ proc initGC() =
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
when traceGC:
|
when traceGC:
|
||||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||||
|
cycleThreshold = InitialCycleThreshold
|
||||||
gch.stat.stackScans = 0
|
gch.stat.stackScans = 0
|
||||||
gch.stat.cycleCollections = 0
|
gch.stat.cycleCollections = 0
|
||||||
gch.stat.maxThreshold = 0
|
gch.stat.maxThreshold = 0
|
||||||
|
|
|
||||||
|
|
@ -52,22 +52,22 @@ when defined(Windows):
|
||||||
LockSemaphore: int
|
LockSemaphore: int
|
||||||
Reserved: int32
|
Reserved: int32
|
||||||
|
|
||||||
proc InitSysLock(L: var TSysLock) {.stdcall,
|
proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect,
|
||||||
dynlib: "kernel32", importc: "InitializeCriticalSection".}
|
dynlib: "kernel32", importc: "InitializeCriticalSection".}
|
||||||
## Initializes the lock `L`.
|
## Initializes the lock `L`.
|
||||||
|
|
||||||
proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall,
|
proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect,
|
||||||
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
|
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
|
||||||
## Tries to acquire the lock `L`.
|
## Tries to acquire the lock `L`.
|
||||||
|
|
||||||
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
|
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
|
||||||
result = TryAcquireSysAux(L) != 0'i32
|
result = TryAcquireSysAux(L) != 0'i32
|
||||||
|
|
||||||
proc AcquireSys(L: var TSysLock) {.stdcall,
|
proc AcquireSys(L: var TSysLock) {.stdcall, noSideEffect,
|
||||||
dynlib: "kernel32", importc: "EnterCriticalSection".}
|
dynlib: "kernel32", importc: "EnterCriticalSection".}
|
||||||
## Acquires the lock `L`.
|
## Acquires the lock `L`.
|
||||||
|
|
||||||
proc ReleaseSys(L: var TSysLock) {.stdcall,
|
proc ReleaseSys(L: var TSysLock) {.stdcall, noSideEffect,
|
||||||
dynlib: "kernel32", importc: "LeaveCriticalSection".}
|
dynlib: "kernel32", importc: "LeaveCriticalSection".}
|
||||||
## Releases the lock `L`.
|
## Releases the lock `L`.
|
||||||
|
|
||||||
|
|
@ -120,17 +120,17 @@ else:
|
||||||
header: "<sys/types.h>".} = object
|
header: "<sys/types.h>".} = object
|
||||||
|
|
||||||
proc InitSysLock(L: var TSysLock, attr: pointer = nil) {.
|
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>".}
|
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>".}
|
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||||
|
|
||||||
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
|
proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
|
||||||
result = TryAcquireSysAux(L) == 0'i32
|
result = TryAcquireSysAux(L) == 0'i32
|
||||||
|
|
||||||
proc ReleaseSys(L: var TSysLock) {.
|
proc ReleaseSys(L: var TSysLock) {.noSideEffect,
|
||||||
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -184,32 +184,25 @@ else:
|
||||||
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {.
|
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {.
|
||||||
importc: "pthread_setspecific", header: "<pthread.h>".}
|
importc: "pthread_setspecific", header: "<pthread.h>".}
|
||||||
|
|
||||||
proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} =
|
proc ThreadVarAlloc(): TThreadVarSlot {.inline.} =
|
||||||
discard pthread_key_create(addr(result), nil)
|
discard pthread_key_create(addr(result), nil)
|
||||||
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.
|
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} =
|
||||||
compilerproc, inline.} =
|
|
||||||
discard pthread_setspecific(s, value)
|
discard pthread_setspecific(s, value)
|
||||||
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc, inline.} =
|
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.inline.} =
|
||||||
result = pthread_getspecific(s)
|
result = pthread_getspecific(s)
|
||||||
|
|
||||||
type
|
const emulatedThreadVars = defined(macosx)
|
||||||
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!
|
|
||||||
|
|
||||||
proc initGlobals(g: var TGlobals) =
|
when emulatedThreadVars:
|
||||||
new(g.gAssertionFailed)
|
# the compiler generates this proc for us, so that we can get the size of
|
||||||
g.buf = newStringOfCap(2000)
|
# the thread local var block:
|
||||||
g.assertBuf = newStringOfCap(2000)
|
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
|
type
|
||||||
PGcThread = ptr TGcThread
|
PGcThread = ptr TGcThread
|
||||||
|
|
@ -218,7 +211,6 @@ type
|
||||||
next, prev: PGcThread
|
next, prev: PGcThread
|
||||||
stackBottom, stackTop, threadLocalStorage: pointer
|
stackBottom, stackTop, threadLocalStorage: pointer
|
||||||
stackSize: int
|
stackSize: int
|
||||||
g: TGlobals
|
|
||||||
locksLen: int
|
locksLen: int
|
||||||
locks: array [0..MaxLocksPerThread-1, pointer]
|
locks: array [0..MaxLocksPerThread-1, pointer]
|
||||||
registers: array[0..maxRegisters-1, pointer] # register contents for GC
|
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.
|
# of all threads; it's not to be stopped etc.
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
var mainThread: TGcThread
|
var mainThread: TGcThread
|
||||||
initGlobals(mainThread.g)
|
|
||||||
ThreadVarSetValue(globalsSlot, addr(mainThread))
|
ThreadVarSetValue(globalsSlot, addr(mainThread))
|
||||||
|
when emulatedThreadVars:
|
||||||
|
mainThread.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
|
||||||
|
|
||||||
|
initStackBottom()
|
||||||
|
initGC()
|
||||||
|
initGlobals()
|
||||||
|
|
||||||
var heapLock: TSysLock
|
var heapLock: TSysLock
|
||||||
InitSysLock(HeapLock)
|
InitSysLock(HeapLock)
|
||||||
|
|
@ -296,15 +294,21 @@ when not defined(boehmgc) and not hasSharedHeap:
|
||||||
template ThreadProcWrapperBody(closure: expr) =
|
template ThreadProcWrapperBody(closure: expr) =
|
||||||
ThreadVarSetValue(globalsSlot, closure)
|
ThreadVarSetValue(globalsSlot, closure)
|
||||||
var t = cast[ptr TThread[TParam]](closure)
|
var t = cast[ptr TThread[TParam]](closure)
|
||||||
|
when emulatedThreadVars:
|
||||||
|
t.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
|
||||||
when not defined(boehmgc) and not hasSharedHeap:
|
when not defined(boehmgc) and not hasSharedHeap:
|
||||||
# init the GC for this thread:
|
# init the GC for this thread:
|
||||||
setStackBottom(addr(t))
|
setStackBottom(addr(t))
|
||||||
initGC()
|
initGC()
|
||||||
t.stackBottom = addr(t)
|
t.stackBottom = addr(t)
|
||||||
|
initGlobals()
|
||||||
registerThread(t)
|
registerThread(t)
|
||||||
try:
|
try:
|
||||||
t.fn(t.data)
|
t.fn(t.data)
|
||||||
finally:
|
finally:
|
||||||
|
# XXX shut-down is not executed when the thread is forced down!
|
||||||
|
when emulatedThreadVars:
|
||||||
|
ThreadVarsDealloc(t.threadLocalStorage)
|
||||||
unregisterThread(t)
|
unregisterThread(t)
|
||||||
when defined(deallocOsPages): deallocOsPages()
|
when defined(deallocOsPages): deallocOsPages()
|
||||||
|
|
||||||
|
|
|
||||||
49
todo.txt
49
todo.txt
|
|
@ -1,14 +1,17 @@
|
||||||
* codegen for threadvars
|
High priority (version 0.8.12)
|
||||||
|
==============================
|
||||||
* implement message passing built-ins
|
* 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
|
* 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
|
- warning for implicit openArray -> varargs convention
|
||||||
- implement explicit varargs
|
- implement explicit varargs
|
||||||
- tests: run modules that contain "#RUN_ME", compile the other
|
- tests: run modules that contain "#RUN_ME", compile the other
|
||||||
|
|
@ -17,6 +20,7 @@ High priority (version 0.9.0)
|
||||||
- fix overloading resolution
|
- fix overloading resolution
|
||||||
- wrong co-/contravariance
|
- wrong co-/contravariance
|
||||||
- make ^ available as operator
|
- make ^ available as operator
|
||||||
|
- implement closures for the C code generator
|
||||||
|
|
||||||
Bugs
|
Bugs
|
||||||
----
|
----
|
||||||
|
|
@ -33,26 +37,13 @@ Bugs
|
||||||
--> system.swap or genericAssign is broken! And indeed, if reference counts
|
--> 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
|
are not modified and the GC is triggered in between a swap, bad things
|
||||||
may happen!
|
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
|
- distinct types for array/seq indexes
|
||||||
* implement closures for the C code generator
|
- GC: marker procs for native Nimrod GC and Boehm GC
|
||||||
* GC: marker procs for native Nimrod GC and Boehm GC
|
|
||||||
* built-in serialization
|
|
||||||
|
|
||||||
|
|
||||||
Low priority
|
|
||||||
------------
|
|
||||||
- implicit ref/ptr->var conversion; the compiler may store an object
|
- implicit ref/ptr->var conversion; the compiler may store an object
|
||||||
implicitly on the heap for write barrier efficiency
|
implicitly on the heap for write barrier efficiency
|
||||||
- resizing of strings/sequences could take into account the memory that
|
- 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
|
- 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
|
is hard because of partial evaluation --> symbol files will fix this as
|
||||||
a side effect
|
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?
|
- prefer proc in current module over other procs with same overloading result?
|
||||||
- real types for template results
|
|
||||||
- generalized case statement (requires better transf)
|
- generalized case statement (requires better transf)
|
||||||
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
||||||
- nested tuple unpacking
|
- nested tuple unpacking
|
||||||
|
|
@ -96,7 +86,7 @@ Library
|
||||||
|
|
||||||
|
|
||||||
Version 2
|
Version 2
|
||||||
---------
|
=========
|
||||||
|
|
||||||
- language change: inheritance should only work with reference types, so that
|
- language change: inheritance should only work with reference types, so that
|
||||||
the ``type`` field is not needed for objects! --> zero overhead aggregation
|
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
|
var x = myProc() # checks myProc() initializes every pointer explicitely
|
||||||
|
|
||||||
- the two other parsers
|
- the two other parsers
|
||||||
|
- rethink the syntax: distinction between expr and stmt is unfortunate;
|
||||||
|
indentation handling is quite complex too
|
||||||
|
|
||||||
|
|
||||||
Low priority
|
Low priority
|
||||||
|
|
@ -135,10 +127,3 @@ Low priority
|
||||||
important than constructors)
|
important than constructors)
|
||||||
- code generated for type information is wasteful
|
- code generated for type information is wasteful
|
||||||
|
|
||||||
|
|
||||||
Other ideas
|
|
||||||
-----------
|
|
||||||
- startsWith `=^`
|
|
||||||
- endsWith `=$`
|
|
||||||
- ignore case `=?` --> `=$?` too?
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue