mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 22:33:49 +00:00
compiler can emulate thread local variables
This commit is contained in:
@@ -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)
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
49
todo.txt
49
todo.txt
@@ -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?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user