GC: get rid of pathological behaviour for stack marking

This commit is contained in:
Araq
2015-03-08 13:05:44 +01:00
parent e390d8ec4a
commit c40aac8e20
5 changed files with 326 additions and 323 deletions

View File

@@ -10,15 +10,15 @@
# This module implements the passes functionality. A pass must implement the
# `TPass` interface.
import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen
type
type
TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext
TPassOpen* = proc (module: PSym): PPassContext {.nimcall.}
@@ -33,8 +33,8 @@ type
TPassData* = tuple[input: PNode, closeOutput: PNode]
TPasses* = openArray[TPass]
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures.
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures.
# This mechanism used to be used for the instantiation of generics.
proc makePass*(open: TPassOpen = nil,
@@ -53,46 +53,46 @@ proc makePass*(open: TPassOpen = nil,
proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
# the semantic checker needs these:
var
var
gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.}
# implementation
proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global
# error count instead.
result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool =
proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
if (s.kind in {skMethod, skProc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false
# XXX this doesn't really make sense with excessive CTFE
else:
result = true
const
const
maxPasses = 10
type
type
TPassContextArray = array[0..maxPasses - 1, PPassContext]
var
var
gPasses: array[0..maxPasses - 1, TPass]
gPassesLen*: int
proc clearPasses* =
gPassesLen = 0
proc registerPass*(p: TPass) =
proc registerPass*(p: TPass) =
gPasses[gPassesLen] = p
inc(gPassesLen)
@@ -109,48 +109,48 @@ proc carryPasses*(nodes: PNode, module: PSym, passes: TPasses) =
passdata = carryPass(pass, module, passdata)
proc openPasses(a: var TPassContextArray, module: PSym) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module)
else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, rd)
if a[i] != nil:
if a[i] != nil:
a[i].fromCache = true
else:
a[i] = nil
proc closePasses(a: var TPassContextArray) =
proc closePasses(a: var TPassContextArray) =
var m: PNode = nil
for i in countup(0, gPassesLen - 1):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
# this implements the code transformation pipeline
var m = n
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process):
m = gPasses[i].process(a[i], m)
if isNil(m): return false
result = true
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
# this implements the code transformation pipeline
var m = n
for i in countup(0, gPassesLen - 1):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
proc closePassesCached(a: var TPassContextArray) =
proc closePassesCached(a: var TPassContextArray) =
var m: PNode = nil
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray) =
for module in items(implicits):
@@ -159,45 +159,45 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
str.info = gCmdLineInfo
importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break
proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
var
var
p: TParsers
a: TPassContextArray
s: PLLStream
fileIdx = module.fileIdx
if rd == nil:
if rd == nil:
openPasses(a, module)
if stream == nil:
if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-":
module.name.s = "stdinfile"
s = llStreamOpen(stdin)
else:
s = llStreamOpen(filename, fmRead)
if s == nil:
if s == nil:
rawMessage(errCannotOpenFile, filename)
return
else:
s = stream
while true:
while true:
openParsers(p, fileIdx, s)
if sfSystemModule notin module.flags:
# XXX what about caching? no processing then? what if I change the
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only
# for the interactive mode.
processImplicits implicitImports, nkImportStmt, a
processImplicits implicitIncludes, nkIncludeStmt, a
while true:
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
if n.kind == nkEmpty: break
if not processTopLevelStmt(n, a): break
closeParsers(p)
if s.kind != llsStdIn: break
if s.kind != llsStdIn: break
closePasses(a)
# id synchronization point for more consistent code generation:
idSynchronizationPoint(1000)

View File

@@ -17,27 +17,27 @@
# * introduces method dispatchers
# * performs lambda lifting for closure support
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings
# implementation
type
type
PTransNode* = distinct PNode
PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable
mapping: TIdNodeTable # mapping from symbols to nodes
owner: PSym # current owner
forStmt: PNode # current for stmt
forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements,
forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements,
# because we need to introduce new variables
# if we encounter the 2nd yield statement
next: PTransCon # for stacking
TTransfContext = object of passes.TPassContext
module: PSym
transCon: PTransCon # top of a TransCon stack
@@ -46,52 +46,52 @@ type
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} =
proc newTransNode(a: PNode): PTransNode {.inline.} =
result = PTransNode(shallowCopy(a))
proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} =
proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} =
var x = newNodeI(kind, info)
newSeq(x.sons, sons)
result = x.PTransNode
proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} =
proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} =
var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons)
x.typ = n.typ
result = x.PTransNode
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a)
n.sons[i] = PNode(x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a)
result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
proc newTransCon(owner: PSym): PTransCon =
proc newTransCon(owner: PSym): PTransCon =
assert owner != nil
new(result)
initIdNodeTable(result.mapping)
result.owner = owner
proc pushTransCon(c: PTransf, t: PTransCon) =
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon
c.transCon = t
proc popTransCon(c: PTransf) =
proc popTransCon(c: PTransf) =
if (c.transCon == nil): internalError("popTransCon")
c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym =
proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner
else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric)
@@ -100,10 +100,10 @@ proc transform(c: PTransf, n: PNode): PTransNode
proc transformSons(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
result[i] = transform(c, n.sons[i])
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le)
result[1] = ri
@@ -113,30 +113,30 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
# return liftIterSym(n)
var b: PNode
var tc = c.transCon
if sfBorrow in n.sym.flags:
if sfBorrow in n.sym.flags:
# simply exchange the symbol:
b = n.sym.getBody
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym)
b.info = n.info
else:
else:
b = n
while tc != nil:
while tc != nil:
result = idNodeTableGet(tc.mapping, b.sym)
if result != nil: return
tc = tc.next
result = b
proc transformSym(c: PTransf, n: PNode): PTransNode =
proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n))
proc transformVarSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v)
for i in countup(0, sonsLen(v)-1):
var it = v.sons[i]
if it.kind == nkCommentStmt:
if it.kind == nkCommentStmt:
result[i] = PTransNode(it)
elif it.kind == nkIdentDefs:
elif it.kind == nkIdentDefs:
if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection")
internalAssert(it.len == 3)
var newVar = copySym(it.sons[0].sym)
@@ -153,12 +153,12 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2])
result[i] = defs
else:
if it.kind != nkVarTuple:
else:
if it.kind != nkVarTuple:
internalError(it.info, "transformVarSection: not nkVarTuple")
var L = sonsLen(it)
var defs = newTransNode(it.kind, it.info, L)
for j in countup(0, L-3):
for j in countup(0, L-3):
var newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c)
@@ -188,12 +188,12 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
else:
result[i] = PTransNode(it)
proc hasContinue(n: PNode): bool =
proc hasContinue(n: PNode): bool =
case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
of nkContinueStmt: result = true
else:
for i in countup(0, sonsLen(n) - 1):
else:
for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym =
@@ -224,10 +224,10 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode =
discard c.breakSyms.pop
result[0] = newSymNode(labl).PTransNode
proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs
proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same!
# We fix this here by making every 'break' belong to its enclosing loop
# and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already).
@@ -239,7 +239,7 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n)
discard c.contSyms.pop()
else:
else:
result = transform(c, n)
proc transformWhile(c: PTransf; n: PNode): PTransNode =
@@ -273,27 +273,27 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
# XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))))
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkVarSection, nkLetSection:
result = transformVarSection(c, n)
else:
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
result[i] = introduceNewLocalVars(c, n.sons[i])
proc transformYield(c: PTransf, n: PNode): PTransNode =
proc transformYield(c: PTransf, n: PNode): PTransNode =
result = newTransNode(nkStmtList, n.info, 0)
var e = n.sons[0]
# c.transCon.forStmt.len == 3 means that there is one for loop variable
@@ -301,21 +301,21 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
c.transCon.forStmt.len != 3:
e = skipConv(e)
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, e.sons[i])))
else:
else:
unpackTuple(c, e, result)
else:
else:
var x = transform(c, e)
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
inc(c.transCon.yieldStmts)
if c.transCon.yieldStmts <= 1:
# common case
add(result, c.transCon.forLoopBody)
else:
else:
# we need to introduce new local variables:
add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
@@ -340,25 +340,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0])
proc transformConv(c: PTransf, n: PNode): PTransNode =
proc transformConv(c: PTransf, n: PNode): PTransNode =
# numeric types need range checks:
var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
# we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source):
# float -> int conversions. ugh.
result = transformSons(c, n)
elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
# BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check:
result = transformSons(c, n)
else:
else:
# generate a range check:
if dest.kind == tyInt64 or source.kind == tyInt64:
if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3)
else:
result = newTransNode(nkChckRange, n, 3)
@@ -368,7 +368,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128:
# XXX int64 -> float conversion?
if skipTypes(n.typ, abstractVar).kind == tyRange:
if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1])
@@ -378,81 +378,81 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n)
of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1])
of tyCString:
if source.kind == tyString:
of tyCString:
if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyString:
if source.kind == tyCString:
of tyString:
if source.kind == tyCString:
result = newTransNode(nkCStringToString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyRef, tyPtr:
of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs)
if source.kind == tyObject:
if source.kind == tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
else:
result = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyObject:
of tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
else:
result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc.
else:
else:
result = transformSons(c, n)
type
TPutArgInto = enum
type
TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
# This analyses how to treat the mapping "formal <-> arg" in an
# inline context.
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects?
case arg.kind
of nkEmpty..nkNilLit:
of nkEmpty..nkNilLit:
result = paDirectMapping
of nkPar, nkCurly, nkBracket:
of nkPar, nkCurly, nkBracket:
result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping
else:
else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) =
if n.kind == nkVarSection:
let x = n.sons[0].sons[0]
if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
internalError(x.info, "bah " & x.sym.name.s & " " &
internalError(x.info, "bah " & x.sym.name.s & " " &
x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
else:
for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i])
proc transformFor(c: PTransf, n: PNode): PTransNode =
proc transformFor(c: PTransf, n: PNode): PTransNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters
if n.kind != nkForStmt: internalError(n.info, "transformFor")
@@ -466,26 +466,26 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode
if call.typ.kind != tyIter and
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].sym.kind != skIterator):
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
result[1] = lambdalifting.liftForLoop(n).PTransNode
discard c.breakSyms.pop
return result
#echo "transforming: ", renderTree(n)
var stmtList = newTransNode(nkStmtList, n.info, 0)
var loopBody = transformLoopBody(c, n.sons[length-1])
result[1] = stmtList
discard c.breakSyms.pop
var v = newNodeI(nkVarSection, n.info)
for i in countup(0, length - 3):
for i in countup(0, length - 3):
addVar(v, copyTree(n.sons[i])) # declare new vars
add(stmtList, v.PTransNode)
# Bugfix: inlined locals belong to the invoking routine, not to the invoked
# iterator!
let iter = call.sons[0].sym
@@ -496,9 +496,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
if iter.kind != skIterator: return result
# generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1):
for i in countup(1, sonsLen(call) - 1):
var arg = transform(c, call.sons[i]).PNode
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
if arg.typ.kind == tyIter: continue
case putArgInto(arg, formal.typ)
of paDirectMapping:
@@ -527,20 +527,20 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
popInfoContext()
popTransCon(c)
# echo "transformed: ", stmtList.PNode.renderTree
proc getMagicOp(call: PNode): TMagic =
proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
result = call.sons[0].sym.magic
else:
result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode =
proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator
result = newTransNode(nkCaseStmt, n, 0)
var ifs = PTransNode(nil)
for i in 0 .. sonsLen(n)-1:
for i in 0 .. sonsLen(n)-1:
var it = n.sons[i]
var e = transform(c, it)
case it.kind
@@ -564,8 +564,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
add(result, elseBranch)
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
# XXX this is really bad; transf should use a proper AST visitor
if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
result = n.PTransNode
@@ -573,45 +573,45 @@ proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n)
for i in 0 .. < n.len:
result[i] = transform(c, skipConv(n.sons[i]))
proc getMergeOp(n: PNode): PSym =
proc getMergeOp(n: PNode): PSym =
case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and
(sfMerge in n.sons[0].sym.flags):
(sfMerge in n.sons[0].sym.flags):
result = n.sons[0].sym
else: discard
proc flattenTreeAux(d, a: PNode, op: PSym) =
proc flattenTreeAux(d, a: PNode, op: PSym) =
let op2 = getMergeOp(a)
if op2 != nil and
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
else:
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode =
proc flattenTree(root: PNode): PNode =
let op = getMergeOp(root)
if op != nil:
if op != nil:
result = copyNode(root)
addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op)
else:
else:
result = root
proc transformCall(c: PTransf, n: PNode): PTransNode =
proc transformCall(c: PTransf, n: PNode): PTransNode =
var n = flattenTree(n)
let op = getMergeOp(n)
let magic = getMagic(n)
if op != nil and op.magic != mNone and n.len >= 3:
if op != nil and op.magic != mNone and n.len >= 3:
result = newTransNode(nkCall, n, 0)
add(result, transform(c, n.sons[0]))
var j = 1
while j < sonsLen(n):
while j < sonsLen(n):
var a = transform(c, n.sons[j]).PNode
inc(j)
if isConstExpr(a):
if isConstExpr(a):
while (j < sonsLen(n)):
let b = transform(c, n.sons[j]).PNode
if not isConstExpr(b): break
@@ -640,7 +640,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array:
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
cnst.len != 0
proc commonOptimizations*(c: PSym, n: PNode): PNode =
@@ -673,11 +673,11 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
else:
result = n
proc transform(c: PTransf, n: PNode): PTransNode =
proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n)
@@ -702,7 +702,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkForStmt:
of nkForStmt:
result = transformFor(c, n)
of nkParForStmt:
result = transformSons(c, n)
@@ -713,14 +713,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: result = transformWhile(c, n)
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
result = transformCall(c, n)
of nkAddr, nkHiddenAddr:
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:
of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkDiscardStmt:
result = PTransNode(n)
@@ -730,7 +730,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# ensure that e.g. discard "some comment" gets optimized away
# completely:
result = PTransNode(newNode(nkCommentStmt))
of nkCommentStmt, nkTemplateDef:
of nkCommentStmt, nkTemplateDef:
return n.PTransNode
of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3``
@@ -744,10 +744,10 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformVarSection(c, n)
else:
result = transformSons(c, n)
of nkYieldStmt:
of nkYieldStmt:
if c.inlining > 0:
result = transformYield(c, n)
else:
else:
result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n)
@@ -764,7 +764,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node.
@@ -774,7 +774,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
popTransCon(c)
incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PTransf =
proc openTransf(module: PSym, filename: string): PTransf =
new(result)
result.contSyms = @[]
result.breakSyms = @[]

View File

@@ -89,7 +89,7 @@ type
SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32
## type class matching all ordinal types; however this includes enums with
## holes.
SomeReal* = float|float32|float64
## type class matching all floating point number types
@@ -181,7 +181,7 @@ proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {.
## freeing the object. Note: The `finalizer` refers to the type `T`, not to
## the object! This means that for each object of type `T` the finalizer
## will be called!
proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
## resets an object `obj` to its initial (binary zero) value. This needs to
## be called before any possible `object branch transition`:idx:.
@@ -348,7 +348,7 @@ type
## This field is filled automatically in the
## ``raise`` statement.
msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message
## providing an exception message
## is bad style.
trace: string
@@ -483,7 +483,7 @@ type
E_Base: Exception, ESystem: SystemError, EIO: IOError,
EOS: OSError, EInvalidLibrary: LibraryError,
EResourceExhausted: ResourceExhaustedError,
EResourceExhausted: ResourceExhaustedError,
EArithmetic: ArithmeticError, EDivByZero: DivByZeroError,
EOverflow: OverflowError, EAccessViolation: AccessViolationError,
EAssertionFailed: AssertionError, EInvalidValue: ValueError,
@@ -494,7 +494,7 @@ type
EInvalidObjectAssignment: ObjectAssignmentError,
EInvalidObjectConversion: ObjectConversionError,
EDeadThread: DeadThreadError,
EFloatInexact: FloatInexactError,
EFloatInexact: FloatInexactError,
EFloatUnderflow: FloatUnderflowError,
EFloatingPoint: FloatingPointError,
EFloatInvalidOp: FloatInvalidOpError,
@@ -511,11 +511,11 @@ proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.}
proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.}
## unary ``<`` that can be used for nice looking excluding ranges:
##
##
## .. code-block:: nim
## for i in 0 .. <10: echo i
##
## Semantically this is the same as ``pred``.
## Semantically this is the same as ``pred``.
proc succ*[T](x: Ordinal[T], y = 1): T {.magic: "Succ", noSideEffect.}
## returns the ``y``-th successor of the value ``x``. ``T`` has to be
@@ -536,7 +536,7 @@ proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.
## decrements the ordinal ``x`` by ``y``. If such a value does not
## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a
## short notation for: ``x = pred(x, y)``.
proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
## creates a new sequence of type ``seq[T]`` with length ``len``.
## This is equivalent to ``s = @[]; setlen(s, len)``, but more
@@ -636,7 +636,7 @@ when not defined(JS):
proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.}
## treats `x` as unsigned and converts it to a byte by taking the last 8 bits
## from `x`.
## from `x`.
proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.}
## treats `x` as unsigned and converts it to an ``int16`` by taking the last
## 16 bits from `x`.
@@ -800,7 +800,7 @@ proc `%%` *(x, y: int64): int64 {.magic: "ModU", noSideEffect.}
## The result is truncated to fit into the result.
## This implements modulo arithmetic.
## No overflow errors are possible.
proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.}
proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.}
## treats `x` and `y` as unsigned and compares them.
@@ -889,7 +889,7 @@ template `notin` * (x, y: expr): expr {.immediate, dirty.} = not contains(y, x)
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
## Checks if T is of the same type as S
##
##
## .. code-block:: Nim
## proc test[T](a: T): int =
## when (T is int):
@@ -924,7 +924,7 @@ proc cmp*(x, y: string): int {.noSideEffect, procvar.}
proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
magic: "ArrToSeq", nosideeffect.}
## turns an array into a sequence. This most often useful for constructing
## sequences with the array constructor: ``@[1, 2, 3]`` has the type
## sequences with the array constructor: ``@[1, 2, 3]`` has the type
## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``.
proc setLen*[T](s: var seq[T], newlen: int) {.
@@ -933,14 +933,14 @@ proc setLen*[T](s: var seq[T], newlen: int) {.
## ``T`` may be any sequence type.
## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with
## a size, use ``newSeq`` instead.
## a size, use ``newSeq`` instead.
proc setLen*(s: var string, newlen: int) {.
magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`.
## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a string with
## a size, use ``newString`` instead.
## a size, use ``newString`` instead.
proc newString*(len: int): string {.
magic: "NewString", importc: "mnewString", noSideEffect.}
@@ -953,7 +953,7 @@ proc newString*(len: int): string {.
proc newStringOfCap*(cap: int): string {.
magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.}
## returns a new string of length ``0`` but with capacity `cap`.This
## procedure exists only for optimization purposes; the same effect can
## procedure exists only for optimization purposes; the same effect can
## be achieved with the ``&`` operator or with ``add``.
proc `&` * (x: string, y: char): string {.
@@ -982,7 +982,7 @@ proc `&` * (x: char, y: string): string {.
## assert('a' & "bc" == "abc")
# implementation note: These must all have the same magic value "ConStrStr" so
# that the merge optimization works properly.
# that the merge optimization works properly.
proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.}
## Appends `y` to `x` in place
@@ -1039,15 +1039,15 @@ proc compileOption*(option: string): bool {.
## can be used to determine an on|off compile-time option. Example:
##
## .. code-block:: nim
## when compileOption("floatchecks"):
## when compileOption("floatchecks"):
## echo "compiled with floating point NaN and Inf checks"
proc compileOption*(option, arg: string): bool {.
magic: "CompileOptionArg", noSideEffect.}
## can be used to determine an enum compile-time option. Example:
##
## .. code-block:: nim
## when compileOption("opt", "size") and compileOption("gc", "boehm"):
## when compileOption("opt", "size") and compileOption("gc", "boehm"):
## echo "compiled with optimization for size and uses Boehm's GC"
const
@@ -1056,16 +1056,16 @@ const
taintMode = compileOption("taintmode")
when taintMode:
type TaintedString* = distinct string ## a distinct string type that
type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode``
## command line switch to turn the taint
## mode on.
proc len*(s: TaintedString): int {.borrow.}
else:
type TaintedString* = string ## a distinct string type that
type TaintedString* = string ## a distinct string type that
## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode``
@@ -1136,25 +1136,25 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
## only changes the semantics for sequences and strings (and types which
## contain those). Be careful with the changed semantics though! There
## contain those). Be careful with the changed semantics though! There
## is a reason why the default assignment does a deep copy of sequences
## and strings.
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
## This is an O(1) operation.
let xl = x.len
shallowCopy(x[i], x[xl-1])
setLen(x, xl-1)
proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} =
proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} =
## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
## This is an O(n) operation.
let xl = x.len
for j in i..xl-2: shallowCopy(x[j], x[j+1])
for j in i..xl-2: shallowCopy(x[j], x[j+1])
setLen(x, xl-1)
proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
## inserts `item` into `x` at position `i`.
let xl = x.len
setLen(x, xl+1)
@@ -1233,7 +1233,7 @@ type # these work for most platforms:
## This is binary compatible to the type ``char**`` in *C*. The array's
## high value is large enough to disable bounds checking in practice.
## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
PFloat32* = ptr float32 ## an alias for ``ptr float32``
PFloat64* = ptr float64 ## an alias for ``ptr float64``
PInt64* = ptr int64 ## an alias for ``ptr int64``
@@ -1280,7 +1280,7 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
proc copy*(s: string, first = 0): string {.
magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.}
proc copy*(s: string, first, last: int): string {.
magic: "CopyStrLast", importc: "copyStrLast", noSideEffect,
magic: "CopyStrLast", importc: "copyStrLast", noSideEffect,
deprecated.}
## copies a slice of `s` into a new string and returns this new
## string. The bounds `first` and `last` denote the indices of
@@ -1358,7 +1358,7 @@ when not defined(nimrodVM):
## The allocated memory belongs to its allocating thread!
## Use `createShared` to allocate from a shared heap.
cast[ptr T](alloc0(T.sizeof * size))
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [],
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [],
benign.}
## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least
@@ -1381,7 +1381,7 @@ when not defined(nimrodVM):
## ``realloc``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
## or other memory may be corrupted.
## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap.
proc free*[T](p: ptr T) {.inline, benign.} =
@@ -1390,30 +1390,30 @@ when not defined(nimrodVM):
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline,
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline,
benign.} =
## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
cast[ptr T](allocShared(T.sizeof * size))
proc allocShared0*(size: int): pointer {.noconv, rtl, benign.}
## allocates a new memory block on the shared heap with at
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``.
proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at
## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``createSharedU``.
cast[ptr T](allocShared0(T.sizeof * size))
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl,
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl,
benign.}
## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at
@@ -1525,7 +1525,7 @@ const
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
## is the version of Nim as a string.
{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion,
{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion,
NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].}
# GC interface:
@@ -1805,7 +1805,7 @@ proc `==` *[I, T](x, y: array[I, T]): bool =
return
result = true
proc `@`*[T](a: openArray[T]): seq[T] =
proc `@`*[T](a: openArray[T]): seq[T] =
## turns an openarray into a sequence. This is not as efficient as turning
## a fixed length array into a sequence as it always copies every element
## of `a`.
@@ -1853,7 +1853,7 @@ when not defined(NimrodVM):
else:
proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
asm """return `x`"""
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
## Generic equals operator for sequences: relies on a equals operator for
## the element type `T`.
@@ -1879,7 +1879,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
## for ``find(a, item) >= 0``.
return find(a, item) >= 0
proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
## returns the last item of `s` and decreases ``s.len`` by one. This treats
## `s` as a stack and implements the common *pop* operation.
var L = s.len-1
@@ -1941,7 +1941,7 @@ iterator fields*[T: tuple|object](x: T): RootObj {.
iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {.
magic: "Fields", noSideEffect.}
## iterates over every field of `x` and `y`.
## Warning: This is really transforms the 'for' and unrolls the loop.
## Warning: This is really transforms the 'for' and unrolls the loop.
## The current implementation also has a bug that affects symbol binding
## in the loop body.
iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
@@ -1982,18 +1982,18 @@ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
a, b: expr] {.
magic: "FieldPairs", noSideEffect.}
## iterates over every field of `x` and `y`.
## Warning: This really transforms the 'for' and unrolls the loop.
## Warning: This really transforms the 'for' and unrolls the loop.
## The current implementation also has a bug that affects symbol binding
## in the loop body.
proc `==`*[T: tuple|object](x, y: T): bool =
proc `==`*[T: tuple|object](x, y: T): bool =
## generic ``==`` operator for tuples that is lifted from the components
## of `x` and `y`.
for a, b in fields(x, y):
if a != b: return false
return true
proc `<=`*[T: tuple](x, y: T): bool =
proc `<=`*[T: tuple](x, y: T): bool =
## generic ``<=`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y):
@@ -2002,7 +2002,7 @@ proc `<=`*[T: tuple](x, y: T): bool =
if c > 0: return false
return true
proc `<`*[T: tuple](x, y: T): bool =
proc `<`*[T: tuple](x, y: T): bool =
## generic ``<`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y):
@@ -2011,7 +2011,7 @@ proc `<`*[T: tuple](x, y: T): bool =
if c > 0: return false
return false
proc `$`*[T: tuple|object](x: T): string =
proc `$`*[T: tuple|object](x: T): string =
## generic ``$`` operator for tuples that is lifted from the components
## of `x`. Example:
##
@@ -2021,13 +2021,13 @@ proc `$`*[T: tuple|object](x: T): string =
result = "("
var firstElement = true
for name, value in fieldPairs(x):
if not(firstElement): result.add(", ")
if not firstElement: result.add(", ")
result.add(name)
result.add(": ")
result.add($value)
firstElement = false
result.add(")")
proc collectionToString[T](x: T, b, e: string): string =
result = b
var firstElement = true
@@ -2037,7 +2037,7 @@ proc collectionToString[T](x: T, b, e: string): string =
firstElement = false
result.add(e)
proc `$`*[T](x: set[T]): string =
proc `$`*[T](x: set[T]): string =
## generic ``$`` operator for sets that is lifted from the components
## of `x`. Example:
##
@@ -2045,7 +2045,7 @@ proc `$`*[T](x: set[T]): string =
## ${23, 45} == "{23, 45}"
collectionToString(x, "{", "}")
proc `$`*[T](x: seq[T]): string =
proc `$`*[T](x: seq[T]): string =
## generic ``$`` operator for seqs that is lifted from the components
## of `x`. Example:
##
@@ -2056,7 +2056,7 @@ proc `$`*[T](x: seq[T]): string =
when false:
# causes bootstrapping to fail as we use array of chars and cstring should
# match better ...
proc `$`*[T, IDX](x: array[IDX, T]): string =
proc `$`*[T, IDX](x: array[IDX, T]): string =
collectionToString(x, "[", "]")
# ----------------- GC interface ---------------------------------------------
@@ -2098,14 +2098,14 @@ when not defined(nimrodVM) and hostOS != "standalone":
proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", benign.}
## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`.
## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.}
@@ -2141,19 +2141,19 @@ var
## application code should never set this hook! You better know what you
## do when setting this. If ``localRaiseHook`` returns false, the exception
## is caught and does not propagate further through the call stack.
outOfMemHook*: proc () {.nimcall, tags: [], benign.}
## set this variable to provide a procedure that should be called
## set this variable to provide a procedure that should be called
## in case of an `out of memory`:idx: event. The standard handler
## writes an error message and terminates the program. `outOfMemHook` can
## be used to raise an exception in case of OOM like so:
##
##
## .. code-block:: nim
##
## var gOutOfMem: ref EOutOfMemory
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
## gOutOfMem.msg = "out of memory"
##
##
## proc handleOOM() =
## raise gOutOfMem
##
@@ -2210,7 +2210,7 @@ proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_
## instead.
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].}
## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho``
## pretends to be free of side effects, so that it can be used for debugging
@@ -2262,7 +2262,7 @@ proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} =
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} =
if x < 0: -x else: x
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} =
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
## is -MININT for its type), an overflow exception is thrown (if overflow
## checking is turned on).
if x < 0: -x else: x
@@ -2318,14 +2318,14 @@ when not defined(JS): #and not defined(NimrodVM):
# we use binary mode in Windows:
setmode(fileno(c_stdin), O_BINARY)
setmode(fileno(c_stdout), O_BINARY)
when defined(endb):
proc endbStep()
# ----------------- IO Part ------------------------------------------------
when hostOS != "standalone":
type
CFile {.importc: "FILE", header: "<stdio.h>",
CFile {.importc: "FILE", header: "<stdio.h>",
final, incompletestruct.} = object
File* = ptr CFile ## The type representing a file handle.
@@ -2375,9 +2375,9 @@ when not defined(JS): #and not defined(NimrodVM):
## Creates a ``TFile`` from a `filehandle` with given `mode`.
##
## Default mode is readonly. Returns true iff the file could be opened.
proc open*(filename: string,
mode: FileMode = fmRead, bufSize: int = -1): File =
mode: FileMode = fmRead, bufSize: int = -1): File =
## Opens a file named `filename` with given `mode`.
##
## Default mode is readonly. Raises an ``IO`` exception if the file
@@ -2387,7 +2387,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
tags: [], benign.}
## reopens the file `f` with given `filename` and `mode`. This
## reopens the file `f` with given `filename` and `mode`. This
## is often used to redirect the `stdin`, `stdout` or `stderr`
## file variables.
##
@@ -2398,7 +2398,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc endOfFile*(f: File): bool {.tags: [], benign.}
## Returns true iff `f` is at the end.
proc readChar*(f: File): char {.
importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].}
## Reads a single character from the stream `f`.
@@ -2411,7 +2411,7 @@ when not defined(JS): #and not defined(NimrodVM):
##
## Raises an IO exception in case of an error. It is an error if the
## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
## Opens a file named `filename` for reading.
##
@@ -2440,8 +2440,8 @@ when not defined(JS): #and not defined(NimrodVM):
## reads a line of text from the file `f`. May throw an IO exception.
## A line of text may be delimited by ``CR``, ``LF`` or
## ``CRLF``. The newline character(s) are not part of the returned string.
proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
benign.}
## reads a line of text from the file `f` into `line`. `line` must not be
## ``nil``! May throw an IO exception.
@@ -2450,7 +2450,7 @@ when not defined(JS): #and not defined(NimrodVM):
## Returns ``false`` if the end of the file has been reached, ``true``
## otherwise. If ``false`` is returned `line` contains no new data.
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
tags: [WriteIOEffect], benign.}
## writes the values `x` to `f` and then writes "\n".
## May throw an IO exception.
@@ -2544,11 +2544,11 @@ when not defined(JS): #and not defined(NimrodVM):
dealloc(a)
when not defined(NimrodVM):
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
discardable, benign.}
## atomic increment of `memLoc`. Returns the value after the operation.
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline,
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline,
discardable, benign.}
## atomic decrement of `memLoc`. Returns the value after the operation.
@@ -2562,7 +2562,7 @@ when not defined(JS): #and not defined(NimrodVM):
context: C_JmpBuf
hasRaiseAction: bool
raiseAction: proc (e: ref Exception): bool {.closure.}
when declared(initAllocator):
initAllocator()
when hasThreadSupport:
@@ -2576,7 +2576,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc setControlCHook*(hook: proc () {.noconv.} not nil)
## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
proc writeStackTrace*() {.tags: [WriteIOEffect].}
## writes the current stack trace to ``stderr``. This is only works
## for debug builds.
@@ -2587,20 +2587,20 @@ when not defined(JS): #and not defined(NimrodVM):
proc getStackTrace*(e: ref Exception): string
## gets the stack trace associated with `e`, which is the stack that
## lead to the ``raise`` statement. This only works for debug builds.
{.push stack_trace: off, profiler:off.}
when hostOS == "standalone":
include "system/embedded"
else:
include "system/excpt"
include "system/chcks"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/arithm"
{.pop.} # stack trace
{.pop.} # stack trace
when hostOS != "standalone" and not defined(NimrodVM):
include "system/dyncalls"
when not defined(NimrodVM):
@@ -2608,7 +2608,7 @@ when not defined(JS): #and not defined(NimrodVM):
const
GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
var d: int
@@ -2728,7 +2728,7 @@ when not defined(JS): #and not defined(NimrodVM):
## process(value)
## else:
## echo "Value too big!"
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
## Hints the optimizer that `val` is likely going to be false.
##
@@ -2742,7 +2742,7 @@ when not defined(JS): #and not defined(NimrodVM):
## echo "Value too big!"
## else:
## process(value)
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw proc pointer of the closure `x`. This is
## useful for interfacing closures with C.
@@ -2774,7 +2774,7 @@ elif defined(JS):
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
@@ -2797,7 +2797,7 @@ elif defined(JS):
if x == y: return 0
if x < y: return -1
return 1
when defined(nimffi):
include "system/sysio"
@@ -2831,14 +2831,14 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
# cut down:
setLen(s, newLen)
# fill the hole:
for i in 0 .. <b.len: s[i+a] = b[i]
for i in 0 .. <b.len: s[i+a] = b[i]
when hostOS != "standalone":
proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
## slice operation for strings. Negative indexes are supported.
result = s.substr(x.a-|s, x.b-|s)
proc `[]=`*(s: var string, x: Slice[int], b: string) =
proc `[]=`*(s: var string, x: Slice[int], b: string) =
## slice assignment for strings. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed:
@@ -2880,7 +2880,7 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
var L = ord(x.b) - ord(x.a) + 1
newSeq(result, L)
var j = x.a
for i in 0.. <L:
for i in 0.. <L:
result[i] = a[j]
inc(j)
@@ -2890,23 +2890,23 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
var L = ord(x.b) - ord(x.a) + 1
if L == b.len:
var j = x.a
for i in 0 .. <L:
for i in 0 .. <L:
a[j] = b[i]
inc(j)
else:
sysFatal(RangeError, "different lengths for slice assignment")
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
## slice operation for sequences. Negative indexes are supported.
var a = x.a-|s
var L = x.b-|s - a + 1
newSeq(result, L)
for i in 0.. <L: result[i] = s[i + a]
proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
## slice assignment for sequences. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed.
## by `x`, a `splice`:idx: is performed.
var a = x.a-|s
var L = x.b-|s - a + 1
if L == b.len:
@@ -2937,7 +2937,7 @@ proc staticExec*(command: string, input = ""): string {.
## to the executed program.
##
## .. code-block:: nim
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v")
##
## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
@@ -2979,7 +2979,7 @@ proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful
## for debugging.
proc instantiationInfo*(index = -1, fullPaths = false): tuple[
filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.}
## provides access to the compiler's instantiation stack line information.
@@ -3090,16 +3090,16 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
## Sets an assertion failure handler that will intercept any assert
## statements following `onFailedAssert` in the current lexical scope.
## Can be defined multiple times in a single function.
##
##
## .. code-block:: nim
##
## proc example(x: int): TErrorCode =
## onFailedAssert(msg):
## log msg
## return E_FAIL
##
##
## assert(...)
##
##
## onFailedAssert(msg):
## raise newException(EMyException, msg)
##
@@ -3111,7 +3111,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
## perform deep copies of `s`. This is only useful for optimization
## perform deep copies of `s`. This is only useful for optimization
## purposes.
when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s)
@@ -3119,7 +3119,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
proc shallow*(s: var string) {.noSideEffect, inline.} =
## marks a string `s` as `shallow`:idx:. Subsequent assignments will not
## perform deep copies of `s`. This is only useful for optimization
## perform deep copies of `s`. This is only useful for optimization
## purposes.
when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s)
@@ -3141,13 +3141,13 @@ else:
when false:
template eval*(blk: stmt): stmt =
## executes a block of code at compile time just as if it was a macro
## optionally, the block can return an AST tree that will replace the
## optionally, the block can return an AST tree that will replace the
## eval expression
macro payload: stmt {.gensym.} = blk
payload()
when hostOS != "standalone":
proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} =
proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} =
## inserts `item` into `x` at position `i`.
var xl = x.len
setLen(x, xl+item.len)

View File

@@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -48,7 +48,7 @@ type
TWalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
@@ -61,9 +61,9 @@ type
maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size
maxStackCells: int # max stack cells in ``decStack``
cycleTableSize: int # max entries in cycle table
cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table
stackBottom: pointer
@@ -88,11 +88,11 @@ var
when not defined(useNimRtl):
instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) =
template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock)
template release(gch: TGcHeap) =
template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock)
@@ -163,7 +163,7 @@ when hasThreadSupport and hasSharedHeap:
template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
else:
template `--`(x: expr): expr =
template `--`(x: expr): expr =
dec(x, rcIncrement)
x <% rcIncrement
template `++`(x: expr): stmt = inc(x, rcIncrement)
@@ -181,7 +181,7 @@ proc prepareDealloc(cell: PCell) =
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries!
when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock)
@@ -211,7 +211,7 @@ proc decRef(c: PCell) {.inline.} =
rtlAddCycleRoot(c)
#writeCell("decRef", c)
proc incRef(c: PCell) {.inline.} =
proc incRef(c: PCell) {.inline.} =
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
c.refcount = c.refcount +% rcIncrement
# and not colorMask
@@ -246,12 +246,12 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
dest[] = src
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc if it is known at compile time that no
# the code generator calls this proc if it is known at compile time that no
# cycle is possible.
if src != nil:
var c = usrToCell(src)
++c.refcount
if dest[] != nil:
if dest[] != nil:
var c = usrToCell(dest[])
if --c.refcount:
rtlAddZCT(c)
@@ -269,7 +269,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
else:
# can't be an interior pointer if it's a stack location!
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
"stack loc AND interior pointer")
dest[] = src
@@ -321,7 +321,7 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables"
quit 1
proc cellsetReset(s: var TCellSet) =
proc cellsetReset(s: var TCellSet) =
deinit(s)
init(s)
@@ -336,7 +336,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
else:
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
n.sons[i].typ, op)
else:
forAllSlotsAux(dest, n.sons[i], op)
@@ -384,7 +384,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down.
#
#
# Slots to try cache hit
# 1 32%
# 4 59%
@@ -481,7 +481,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@@ -510,7 +510,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
acquire(gch)
collectCT(gch)
@@ -522,7 +522,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
@@ -536,7 +536,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
writeCell("growObj new cell", res)
gcTrace(ol, csZctFreed)
gcTrace(res, csAllocated)
when reallyDealloc:
when reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1:
# free immediately to save space:
@@ -580,7 +580,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
prepareDealloc(c)
gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c)
when reallyDealloc:
when reallyDealloc:
sysAssert(allocInv(gch.region), "free cyclic cell")
rawDealloc(gch.region, c)
else:
@@ -767,7 +767,7 @@ proc collectCycles(gch: var TGcHeap) =
gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
if canBeCycleRoot(c):
#if c notin gch.cycleRoots:
#if c notin gch.cycleRoots:
inc cycleRootsLen
incl(gch.cycleRoots, c)
gcAssert c.typ != nil, "addBackStackRoots 2"
@@ -794,12 +794,12 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) =
proc markThreadStacks(gch: var TGcHeap) =
when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".}
var it = threadList
while it != nil:
# mark registers:
# mark registers:
for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
var sp = cast[TAddress](it.stackBottom)
var max = cast[TAddress](it.stackTop)
@@ -933,7 +933,7 @@ else:
while sp <=% max:
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark)
@@ -946,13 +946,13 @@ when useMarkForDebug or useBackupGc:
# ----------------------------------------------------------------------------
proc collectZCT(gch: var TGcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to
# Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small.
# This is performance critical!
const workPackage = 100
var L = addr(gch.zct.len)
when withRealTime:
var steps = workPackage
var t0: TTicks
@@ -962,15 +962,15 @@ proc collectZCT(gch: var TGcHeap): bool =
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
# remove from ZCT:
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
c.refcount = c.refcount and not ZctFlag
gch.zct.d[0] = gch.zct.d[L[] - 1]
dec(L[])
when withRealTime: dec steps
if c.refcount <% rcIncrement:
if c.refcount <% rcIncrement:
# It may have a RC > 0, if it is in the hardware stack or
# it has not been removed yet from the ZCT. This is because
# ``incref`` does not bother to remove the cell from the ZCT
# ``incref`` does not bother to remove the cell from the ZCT
# as this might be too slow.
# In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
@@ -983,7 +983,7 @@ proc collectZCT(gch: var TGcHeap): bool =
# access invalid memory. This is done by prepareDealloc():
prepareDealloc(c)
forAllChildren(c, waZctDecRef)
when reallyDealloc:
when reallyDealloc:
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
rawDealloc(gch.region, c)
else:
@@ -994,7 +994,7 @@ proc collectZCT(gch: var TGcHeap): bool =
steps = workPackage
if gch.maxPause > 0:
let duration = getticks() - t0
# the GC's measuring is not accurate and needs some cleanup actions
# the GC's measuring is not accurate and needs some cleanup actions
# (stack unmarking), so subtract some short amount of time in
# order to miss deadlines less often:
if duration >= gch.maxPause - 50_000:
@@ -1017,7 +1017,7 @@ proc collectCTBody(gch: var TGcHeap) =
when withRealTime:
let t0 = getticks()
sysAssert(allocInv(gch.region), "collectCT: begin")
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
sysAssert(gch.decStack.len == 0, "collectCT")
prepareForInteriorPointerChecking(gch.region)
@@ -1036,7 +1036,7 @@ proc collectCTBody(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
unmarkStackAndRegisters(gch)
sysAssert(allocInv(gch.region), "collectCT: end")
when withRealTime:
let duration = getticks() - t0
gch.stat.maxPause = max(gch.stat.maxPause, duration)
@@ -1050,8 +1050,12 @@ when useMarkForDebug or useBackupGc:
markGlobals(gch)
proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
# stackMarkCosts prevents some pathological behaviour: Stack marking
# becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack.
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
if (gch.zct.len >= stackMarkCosts or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0:
when useMarkForDebug:
prepareForInteriorPointerChecking(gch.region)
@@ -1070,7 +1074,7 @@ when withRealTime:
acquire(gch)
gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
strongAdvice:
collectCTBody(gch)
release(gch)
@@ -1078,13 +1082,13 @@ when withRealTime:
proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
when not defined(useNimRtl):
proc GC_disable() =
proc GC_disable() =
when hasThreadSupport and hasSharedHeap:
discard atomicInc(gch.recGcLock, 1)
else:
inc(gch.recGcLock)
proc GC_enable() =
if gch.recGcLock > 0:
if gch.recGcLock > 0:
when hasThreadSupport and hasSharedHeap:
discard atomicDec(gch.recGcLock, 1)
else:

View File

@@ -53,7 +53,6 @@ Bugs
- VM: Pegs do not work at compile-time
- VM: ptr/ref T cannot work in general
- scopes are still broken for generic instantiation!
- compilation of niminst takes way too long. looks like a regression
- blocks can "export" an identifier but the CCG generates {} for them ...