ARC: cycle detector (#12823)
* first implementation of the =trace and =dispose hooks for the cycle collector * a cycle collector for ARC: progress * manual: the .acyclic pragma is a thing once again * gcbench: adaptations for --gc:arc * enable valgrind tests for the strutils tests * testament: better valgrind support * ARC refactoring: growable jumpstacks * ARC cycle detector: non-recursive algorithm * moved and renamed core/ files back to system/ * refactoring: --gc:arc vs --gc:orc since 'orc' is even more experimental and we want to ship --gc:arc soonish
This commit is contained in:
parent
5848f0042c
commit
83a736a34a
45 changed files with 939 additions and 229 deletions
|
|
@ -478,7 +478,7 @@ type
|
||||||
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~38)
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~39)
|
||||||
tfVarargs, # procedure has C styled varargs
|
tfVarargs, # procedure has C styled varargs
|
||||||
# tyArray type represeting a varargs list
|
# tyArray type represeting a varargs list
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
|
|
@ -537,6 +537,7 @@ type
|
||||||
tfContravariant # contravariant generic param
|
tfContravariant # contravariant generic param
|
||||||
tfCheckedForDestructor # type was checked for having a destructor.
|
tfCheckedForDestructor # type was checked for having a destructor.
|
||||||
# If it has one, t.destructor is not nil.
|
# If it has one, t.destructor is not nil.
|
||||||
|
tfAcyclic # object type was annotated as .acyclic
|
||||||
|
|
||||||
TTypeFlags* = set[TTypeFlag]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -635,7 +636,7 @@ type
|
||||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||||
mMove, mWasMoved, mDestroy,
|
mMove, mWasMoved, mDestroy,
|
||||||
mDefault, mUnown, mAccessEnv, mReset,
|
mDefault, mUnown, mAccessEnv, mAccessTypeInfo, mReset,
|
||||||
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
||||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||||
mOrdinal,
|
mOrdinal,
|
||||||
|
|
@ -870,6 +871,8 @@ type
|
||||||
attachedDestructor,
|
attachedDestructor,
|
||||||
attachedAsgn,
|
attachedAsgn,
|
||||||
attachedSink,
|
attachedSink,
|
||||||
|
attachedTrace,
|
||||||
|
attachedDispose,
|
||||||
attachedDeepCopy
|
attachedDeepCopy
|
||||||
|
|
||||||
TType* {.acyclic.} = object of TIdObj # \
|
TType* {.acyclic.} = object of TIdObj # \
|
||||||
|
|
@ -1298,7 +1301,8 @@ const
|
||||||
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
||||||
MaxLockLevel* = 1000'i16
|
MaxLockLevel* = 1000'i16
|
||||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"]
|
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||||
|
"=destroy", "=", "=sink", "=trace", "=dispose", "=deepcopy"]
|
||||||
|
|
||||||
proc `$`*(x: TLockLevel): string =
|
proc `$`*(x: TLockLevel): string =
|
||||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||||
|
|
|
||||||
|
|
@ -1210,7 +1210,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||||
genAssignment(p, a, b, {})
|
genAssignment(p, a, b, {})
|
||||||
else:
|
else:
|
||||||
let ti = genTypeInfo(p.module, typ, a.lode.info)
|
let ti = genTypeInfo(p.module, typ, a.lode.info)
|
||||||
if bt.destructor != nil and not trivialDestructor(bt.destructor):
|
if bt.destructor != nil and not isTrivialProc(bt.destructor):
|
||||||
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
||||||
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
||||||
# convention at least:
|
# convention at least:
|
||||||
|
|
@ -1584,6 +1584,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
gcUsage(p.config, e)
|
gcUsage(p.config, e)
|
||||||
|
|
||||||
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
|
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
|
||||||
|
discard cgsym(p.module, "TNimType")
|
||||||
let t = e[1].typ
|
let t = e[1].typ
|
||||||
putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info))
|
putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info))
|
||||||
|
|
||||||
|
|
@ -2077,6 +2078,21 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
n[0] = newSymNode(toStrProc)
|
n[0] = newSymNode(toStrProc)
|
||||||
expr(p, n, d)
|
expr(p, n, d)
|
||||||
|
|
||||||
|
proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope): Rope =
|
||||||
|
result = rdLoc(a)
|
||||||
|
var t = skipTypes(a.t, abstractInst)
|
||||||
|
while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
|
||||||
|
if t.kind notin {tyVar, tyLent}: nilCheck = result
|
||||||
|
if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
|
||||||
|
result = "(*$1)" % [result]
|
||||||
|
t = skipTypes(t.lastSon, abstractInst)
|
||||||
|
discard getTypeDesc(p.module, t)
|
||||||
|
if not p.module.compileToCpp:
|
||||||
|
while t.kind == tyObject and t[0] != nil:
|
||||||
|
result.add(".Sup")
|
||||||
|
t = skipTypes(t[0], skipPtrs)
|
||||||
|
result.add ".m_type"
|
||||||
|
|
||||||
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
case op
|
case op
|
||||||
of mOr, mAnd: genAndOr(p, e, d, op)
|
of mOr, mAnd: genAndOr(p, e, d, op)
|
||||||
|
|
@ -2260,6 +2276,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mMove: genMove(p, e, d)
|
of mMove: genMove(p, e, d)
|
||||||
of mDestroy: genDestroy(p, e)
|
of mDestroy: genDestroy(p, e)
|
||||||
of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
|
of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
|
||||||
|
of mAccessTypeInfo:
|
||||||
|
var a: TLoc
|
||||||
|
var dummy: Rope
|
||||||
|
initLocExpr(p, e[1], a)
|
||||||
|
putIntoDest(p, d, e, rdMType(p, a, dummy))
|
||||||
of mSlice:
|
of mSlice:
|
||||||
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
||||||
"'toOpenArray' is only valid within a call expression")
|
"'toOpenArray' is only valid within a call expression")
|
||||||
|
|
@ -2407,28 +2428,17 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||||
initLocExpr(p, n[0], a)
|
initLocExpr(p, n[0], a)
|
||||||
let dest = skipTypes(n.typ, abstractPtrs)
|
let dest = skipTypes(n.typ, abstractPtrs)
|
||||||
if optObjCheck in p.options and not isObjLackingTypeField(dest):
|
if optObjCheck in p.options and not isObjLackingTypeField(dest):
|
||||||
var r = rdLoc(a)
|
var nilCheck = Rope(nil)
|
||||||
var nilCheck: Rope = nil
|
let r = rdMType(p, a, nilCheck)
|
||||||
var t = skipTypes(a.t, abstractInst)
|
|
||||||
while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
|
|
||||||
if t.kind notin {tyVar, tyLent}: nilCheck = r
|
|
||||||
if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
|
|
||||||
r = "(*$1)" % [r]
|
|
||||||
t = skipTypes(t.lastSon, abstractInst)
|
|
||||||
discard getTypeDesc(p.module, t)
|
|
||||||
if not p.module.compileToCpp:
|
|
||||||
while t.kind == tyObject and t[0] != nil:
|
|
||||||
r.add(".Sup")
|
|
||||||
t = skipTypes(t[0], skipPtrs)
|
|
||||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||||
genTypeInfo2Name(p.module, dest)
|
genTypeInfo2Name(p.module, dest)
|
||||||
else:
|
else:
|
||||||
genTypeInfo(p.module, dest, n.info)
|
genTypeInfo(p.module, dest, n.info)
|
||||||
if nilCheck != nil:
|
if nilCheck != nil:
|
||||||
linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
|
linefmt(p, cpsStmts, "if ($1) #chckObj($2, $3);$n",
|
||||||
[nilCheck, r, checkFor])
|
[nilCheck, r, checkFor])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
|
linefmt(p, cpsStmts, "#chckObj($1, $2);$n",
|
||||||
[r, checkFor])
|
[r, checkFor])
|
||||||
if n[0].typ.kind != tyObject:
|
if n[0].typ.kind != tyObject:
|
||||||
putIntoDest(p, d, n,
|
putIntoDest(p, d, n,
|
||||||
|
|
|
||||||
|
|
@ -1273,30 +1273,44 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
|
||||||
it = it[0]
|
it = it[0]
|
||||||
result = makeCString(res)
|
result = makeCString(res)
|
||||||
|
|
||||||
proc trivialDestructor(s: PSym): bool {.inline.} = s.ast[bodyPos].len == 0
|
proc isTrivialProc(s: PSym): bool {.inline.} = s.ast[bodyPos].len == 0
|
||||||
|
|
||||||
proc genObjectInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
|
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
|
||||||
assert t.kind == tyObject
|
let theProc = t.attachedOps[op]
|
||||||
if incompleteType(t):
|
if theProc != nil and not isTrivialProc(theProc):
|
||||||
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
||||||
typeToString(t))
|
|
||||||
|
|
||||||
var d: Rope
|
|
||||||
if t.destructor != nil and not trivialDestructor(t.destructor):
|
|
||||||
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
||||||
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
||||||
# convention at least:
|
# convention at least:
|
||||||
if t.destructor.typ == nil or t.destructor.typ.callConv != ccDefault:
|
if theProc.typ == nil or theProc.typ.callConv != ccDefault:
|
||||||
localError(m.config, info,
|
localError(m.config, info,
|
||||||
"the destructor that is turned into a finalizer needs " &
|
theProc.name.s & " needs to have the 'nimcall' calling convention")
|
||||||
"to have the 'nimcall' calling convention")
|
|
||||||
genProc(m, t.destructor)
|
genProc(m, theProc)
|
||||||
d = t.destructor.loc.r
|
result = theProc.loc.r
|
||||||
else:
|
else:
|
||||||
d = rope("NIM_NIL")
|
result = rope("NIM_NIL")
|
||||||
|
|
||||||
|
proc genTypeInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
|
||||||
|
var typeName: Rope
|
||||||
|
if t.kind == tyObject:
|
||||||
|
if incompleteType(t):
|
||||||
|
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
||||||
|
typeToString(t))
|
||||||
|
typeName = genTypeInfo2Name(m, t)
|
||||||
|
else:
|
||||||
|
typeName = rope("NIM_NIL")
|
||||||
|
|
||||||
m.s[cfsData].addf("TNimType $1;$n", [name])
|
m.s[cfsData].addf("TNimType $1;$n", [name])
|
||||||
m.s[cfsTypeInit3].addf("$1.destructor = (void*)$2; $1.size = sizeof($3); $1.name = $4;$n", [
|
let destroyImpl = genHook(m, t, info, attachedDestructor)
|
||||||
name, d, getTypeDesc(m, t), genTypeInfo2Name(m, t)])
|
let traceImpl = genHook(m, t, info, attachedTrace)
|
||||||
|
let disposeImpl = genHook(m, t, info, attachedDispose)
|
||||||
|
|
||||||
|
m.s[cfsTypeInit3].addf("$1.destructor = (void*)$2; $1.size = sizeof($3);$n" &
|
||||||
|
"$1.name = $4;$n" &
|
||||||
|
"$1.traceImpl = (void*)$5;$n" &
|
||||||
|
"$1.disposeImpl = (void*)$6;$n", [
|
||||||
|
name, destroyImpl, getTypeDesc(m, t), typeName,
|
||||||
|
traceImpl, disposeImpl])
|
||||||
|
|
||||||
proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
let origType = t
|
let origType = t
|
||||||
|
|
@ -1333,49 +1347,51 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
m.g.typeInfoMarker[sig] = (str: result, owner: owner)
|
m.g.typeInfoMarker[sig] = (str: result, owner: owner)
|
||||||
case t.kind
|
|
||||||
of tyEmpty, tyVoid: result = rope"0"
|
if optTinyRtti in m.config.globalOptions:
|
||||||
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent:
|
genTypeInfoV2(m, t, origType, result, info)
|
||||||
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
|
else:
|
||||||
of tyStatic:
|
case t.kind
|
||||||
if t.n != nil: result = genTypeInfo(m, lastSon t, info)
|
of tyEmpty, tyVoid: result = rope"0"
|
||||||
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent:
|
||||||
of tyUserTypeClasses:
|
|
||||||
internalAssert m.config, t.isResolvedUserTypeClass
|
|
||||||
return genTypeInfo(m, t.lastSon, info)
|
|
||||||
of tyProc:
|
|
||||||
if t.callConv != ccClosure:
|
|
||||||
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
|
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
|
||||||
else:
|
of tyStatic:
|
||||||
let x = fakeClosureType(m, t.owner)
|
if t.n != nil: result = genTypeInfo(m, lastSon t, info)
|
||||||
genTupleInfo(m, x, x, result, info)
|
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
||||||
of tySequence:
|
of tyUserTypeClasses:
|
||||||
genTypeInfoAux(m, t, t, result, info)
|
internalAssert m.config, t.isResolvedUserTypeClass
|
||||||
if optSeqDestructors notin m.config.globalOptions:
|
return genTypeInfo(m, t.lastSon, info)
|
||||||
|
of tyProc:
|
||||||
|
if t.callConv != ccClosure:
|
||||||
|
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
|
||||||
|
else:
|
||||||
|
let x = fakeClosureType(m, t.owner)
|
||||||
|
genTupleInfo(m, x, x, result, info)
|
||||||
|
of tySequence:
|
||||||
|
genTypeInfoAux(m, t, t, result, info)
|
||||||
|
if optSeqDestructors notin m.config.globalOptions:
|
||||||
|
if m.config.selectedGC >= gcMarkAndSweep:
|
||||||
|
let markerProc = genTraverseProc(m, origType, sig)
|
||||||
|
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||||
|
of tyRef:
|
||||||
|
genTypeInfoAux(m, t, t, result, info)
|
||||||
if m.config.selectedGC >= gcMarkAndSweep:
|
if m.config.selectedGC >= gcMarkAndSweep:
|
||||||
let markerProc = genTraverseProc(m, origType, sig)
|
let markerProc = genTraverseProc(m, origType, sig)
|
||||||
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||||
of tyRef:
|
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
||||||
genTypeInfoAux(m, t, t, result, info)
|
of tyArray: genArrayInfo(m, t, result, info)
|
||||||
if m.config.selectedGC >= gcMarkAndSweep:
|
of tySet: genSetInfo(m, t, result, info)
|
||||||
let markerProc = genTraverseProc(m, origType, sig)
|
of tyEnum: genEnumInfo(m, t, result, info)
|
||||||
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
of tyObject:
|
||||||
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
|
||||||
of tyArray: genArrayInfo(m, t, result, info)
|
|
||||||
of tySet: genSetInfo(m, t, result, info)
|
|
||||||
of tyEnum: genEnumInfo(m, t, result, info)
|
|
||||||
of tyObject:
|
|
||||||
if optTinyRtti in m.config.globalOptions:
|
|
||||||
genObjectInfoV2(m, t, origType, result, info)
|
|
||||||
else:
|
|
||||||
genObjectInfo(m, t, origType, result, info)
|
genObjectInfo(m, t, origType, result, info)
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
# if t.n != nil: genObjectInfo(m, t, result)
|
# if t.n != nil: genObjectInfo(m, t, result)
|
||||||
# else:
|
# else:
|
||||||
# BUGFIX: use consistently RTTI without proper field names; otherwise
|
# BUGFIX: use consistently RTTI without proper field names; otherwise
|
||||||
# results are not deterministic!
|
# results are not deterministic!
|
||||||
genTupleInfo(m, t, origType, result, info)
|
genTupleInfo(m, t, origType, result, info)
|
||||||
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
||||||
|
|
||||||
if t.attachedOps[attachedDeepCopy] != nil:
|
if t.attachedOps[attachedDeepCopy] != nil:
|
||||||
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
|
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
|
||||||
elif origType.attachedOps[attachedDeepCopy] != nil:
|
elif origType.attachedOps[attachedDeepCopy] != nil:
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||||
of "v2": result = false
|
of "v2": result = false
|
||||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||||
of "generational": result = false
|
of "generational": result = false
|
||||||
of "destructors", "arc": result = conf.selectedGC == gcDestructors
|
of "destructors", "arc": result = conf.selectedGC == gcArc
|
||||||
|
of "orc": result = conf.selectedGC == gcOrc
|
||||||
of "hooks": result = conf.selectedGC == gcHooks
|
of "hooks": result = conf.selectedGC == gcHooks
|
||||||
of "go": result = conf.selectedGC == gcGo
|
of "go": result = conf.selectedGC == gcGo
|
||||||
of "none": result = conf.selectedGC == gcNone
|
of "none": result = conf.selectedGC == gcNone
|
||||||
|
|
@ -455,8 +456,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
conf.selectedGC = gcMarkAndSweep
|
conf.selectedGC = gcMarkAndSweep
|
||||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||||
of "destructors", "arc":
|
of "destructors", "arc":
|
||||||
conf.selectedGC = gcDestructors
|
conf.selectedGC = gcArc
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
|
defineSymbol(conf.symbols, "gcarc")
|
||||||
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
incl conf.globalOptions, optTinyRtti
|
||||||
|
if pass in {passCmd2, passPP}:
|
||||||
|
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||||
|
defineSymbol(conf.symbols, "nimV2")
|
||||||
|
of "orc":
|
||||||
|
conf.selectedGC = gcOrc
|
||||||
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
|
defineSymbol(conf.symbols, "gcorc")
|
||||||
incl conf.globalOptions, optSeqDestructors
|
incl conf.globalOptions, optSeqDestructors
|
||||||
incl conf.globalOptions, optTinyRtti
|
incl conf.globalOptions, optTinyRtti
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode =
|
||||||
|
|
||||||
proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
||||||
let t = dest.typ
|
let t = dest.typ
|
||||||
if tfHasOwned in t.flags:
|
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||||
# try to improve the error message here:
|
# try to improve the error message here:
|
||||||
if c.otherRead == nil: discard isLastRead(ri, c)
|
if c.otherRead == nil: discard isLastRead(ri, c)
|
||||||
checkForErrorPragma(c, t, ri, "=")
|
checkForErrorPragma(c, t, ri, "=")
|
||||||
|
|
@ -409,7 +409,7 @@ proc isCursor(n: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc cycleCheck(n: PNode; c: var Con) =
|
proc cycleCheck(n: PNode; c: var Con) =
|
||||||
if c.graph.config.selectedGC != gcDestructors: return
|
if c.graph.config.selectedGC != gcArc: return
|
||||||
var value = n[1]
|
var value = n[1]
|
||||||
if value.kind == nkClosure:
|
if value.kind == nkClosure:
|
||||||
value = value[1]
|
value = value[1]
|
||||||
|
|
@ -512,7 +512,7 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||||
result[i] = p(n[i], c, normal)
|
result[i] = p(n[i], c, normal)
|
||||||
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
||||||
result[0] = copyTree(n[0])
|
result[0] = copyTree(n[0])
|
||||||
if c.graph.config.selectedGC in {gcHooks, gcDestructors}:
|
if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
|
||||||
let destroyOld = genDestroy(c, result[1])
|
let destroyOld = genDestroy(c, result[1])
|
||||||
result = newTree(nkStmtList, destroyOld, result)
|
result = newTree(nkStmtList, destroyOld, result)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||||
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
|
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
|
||||||
# This seems to be generally correct but since it's a bit risky it's disabled
|
# This seems to be generally correct but since it's a bit risky it's disabled
|
||||||
# for now.
|
# for now.
|
||||||
|
# XXX This is wrong for the 'hamming' test, so remove this logic again.
|
||||||
let fieldType = if isDefined(c.graph.config, "nimCycleBreaker"):
|
let fieldType = if isDefined(c.graph.config, "nimCycleBreaker"):
|
||||||
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
|
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||||
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
for i in 0..<t.len:
|
for i in 0..<t.len:
|
||||||
let lit = lowerings.newIntLit(c.g, x.info, i)
|
let lit = lowerings.newIntLit(c.g, x.info, i)
|
||||||
fillBody(c, t[i], body, x.at(lit, t[i]), y.at(lit, t[i]))
|
let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
|
||||||
|
fillBody(c, t[i], body, x.at(lit, t[i]), b)
|
||||||
|
|
||||||
proc dotField(x: PNode, f: PSym): PNode =
|
proc dotField(x: PNode, f: PSym): PNode =
|
||||||
result = newNodeI(nkDotExpr, x.info, 2)
|
result = newNodeI(nkDotExpr, x.info, 2)
|
||||||
|
|
@ -58,18 +59,19 @@ proc newAsgnStmt(le, ri: PNode): PNode =
|
||||||
result[1] = ri
|
result[1] = ri
|
||||||
|
|
||||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if c.kind != attachedDestructor:
|
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
|
|
||||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let f = n.sym
|
let f = n.sym
|
||||||
|
let b = if c.kind == attachedTrace: y else: y.dotField(f)
|
||||||
if sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
if sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
||||||
c.g.config.selectedGC in {gcDestructors, gcHooks}:
|
c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
|
||||||
defaultOp(c, f.typ, body, x.dotField(f), y.dotField(f))
|
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||||
else:
|
else:
|
||||||
fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
|
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||||
of nkNilLit: discard
|
of nkNilLit: discard
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
|
if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
|
||||||
|
|
@ -117,13 +119,17 @@ proc genAddr(g: ModuleGraph; x: PNode): PNode =
|
||||||
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
|
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
|
||||||
result.add x
|
result.add x
|
||||||
|
|
||||||
proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
|
proc newHookCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
|
||||||
#if sfError in op.flags:
|
#if sfError in op.flags:
|
||||||
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
|
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
|
||||||
result = newNodeI(nkCall, x.info)
|
result = newNodeI(nkCall, x.info)
|
||||||
result.add newSymNode(op)
|
result.add newSymNode(op)
|
||||||
result.add genAddr(g, x)
|
if op.typ.sons[1].kind == tyVar:
|
||||||
result.add y
|
result.add genAddr(g, x)
|
||||||
|
else:
|
||||||
|
result.add x
|
||||||
|
if y != nil:
|
||||||
|
result.add y
|
||||||
|
|
||||||
proc newOpCall(op: PSym; x: PNode): PNode =
|
proc newOpCall(op: PSym; x: PNode): PNode =
|
||||||
result = newNodeIT(nkCall, x.info, op.typ[0])
|
result = newNodeIT(nkCall, x.info, op.typ[0])
|
||||||
|
|
@ -142,6 +148,10 @@ proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||||
result = optSeqDestructors in c.g.config.globalOptions and
|
result = optSeqDestructors in c.g.config.globalOptions and
|
||||||
({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
|
({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
|
||||||
|
|
||||||
|
proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||||
|
result = optSeqDestructors in c.g.config.globalOptions and
|
||||||
|
containsGarbageCollectedRef(t)
|
||||||
|
|
||||||
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||||
if c.c != nil and typeInst != nil:
|
if c.c != nil and typeInst != nil:
|
||||||
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
|
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
|
||||||
|
|
@ -160,7 +170,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
#else:
|
#else:
|
||||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
body.add newAsgnCall(c.g, op, x, y)
|
body.add newHookCall(c.g, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
elif tfHasAsgn in t.flags:
|
elif tfHasAsgn in t.flags:
|
||||||
var op: PSym
|
var op: PSym
|
||||||
|
|
@ -189,7 +199,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
#debug(t)
|
#debug(t)
|
||||||
#return false
|
#return false
|
||||||
assert op.ast[genericParamsPos].kind == nkEmpty
|
assert op.ast[genericParamsPos].kind == nkEmpty
|
||||||
body.add newAsgnCall(c.g, op, x, y)
|
body.add newHookCall(c.g, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
||||||
|
|
@ -202,7 +212,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
||||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||||
t.attachedOps[attachedDestructor] = op
|
t.attachedOps[attachedDestructor] = op
|
||||||
|
|
||||||
if op == nil and useNoGc(c, t):
|
if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
|
||||||
op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
|
op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
|
||||||
doAssert op != nil
|
doAssert op != nil
|
||||||
doAssert op == t.destructor
|
doAssert op == t.destructor
|
||||||
|
|
@ -231,10 +241,10 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
body.add destructorCall(c.g, op, x)
|
body.add destructorCall(c.g, op, x)
|
||||||
result = true
|
result = true
|
||||||
#result = addDestructorCall(c, t, body, x)
|
#result = addDestructorCall(c, t, body, x)
|
||||||
of attachedAsgn:
|
of attachedAsgn, attachedSink, attachedTrace:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
result = considerAsgnOrSink(c, t, body, x, y, t.attachedOps[c.kind])
|
||||||
of attachedSink:
|
of attachedDispose:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.asink)
|
result = considerAsgnOrSink(c, t, body, x, nil, t.attachedOps[c.kind])
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
let op = t.attachedOps[attachedDeepCopy]
|
let op = t.attachedOps[attachedDeepCopy]
|
||||||
if op != nil:
|
if op != nil:
|
||||||
|
|
@ -305,8 +315,8 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
|
let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
|
||||||
let whileLoop = genWhileLoop(c, i, x)
|
let whileLoop = genWhileLoop(c, i, x)
|
||||||
let elemType = t.lastSon
|
let elemType = t.lastSon
|
||||||
fillBody(c, elemType, whileLoop[1], x.at(i, elemType),
|
let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
|
||||||
y.at(i, elemType))
|
fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
|
||||||
addIncStmt(c, whileLoop[1], i)
|
addIncStmt(c, whileLoop[1], i)
|
||||||
body.add whileLoop
|
body.add whileLoop
|
||||||
|
|
||||||
|
|
@ -330,6 +340,11 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# destroy all elements:
|
# destroy all elements:
|
||||||
forallElements(c, t, body, x, y)
|
forallElements(c, t, body, x, y)
|
||||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||||
|
of attachedTrace:
|
||||||
|
# follow all elements:
|
||||||
|
forallElements(c, t, body, x, y)
|
||||||
|
of attachedDispose:
|
||||||
|
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||||
|
|
||||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
createTypeBoundOps(c.g, c.c, t, body.info)
|
createTypeBoundOps(c.g, c.c, t, body.info)
|
||||||
|
|
@ -344,7 +359,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedAsgn, attachedDeepCopy:
|
of attachedAsgn, attachedDeepCopy:
|
||||||
doAssert t.assignment != nil
|
doAssert t.assignment != nil
|
||||||
body.add newAsgnCall(c.g, t.assignment, x, y)
|
body.add newHookCall(c.g, t.assignment, x, y)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
# we always inline the move for better performance:
|
# we always inline the move for better performance:
|
||||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||||
|
|
@ -355,10 +370,14 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# alternatively we could do this:
|
# alternatively we could do this:
|
||||||
when false:
|
when false:
|
||||||
doAssert t.asink != nil
|
doAssert t.asink != nil
|
||||||
body.add newAsgnCall(c.g, t.asink, x, y)
|
body.add newHookCall(c.g, t.asink, x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
doAssert t.destructor != nil
|
doAssert t.destructor != nil
|
||||||
body.add destructorCall(c.g, t.destructor, x)
|
body.add destructorCall(c.g, t.destructor, x)
|
||||||
|
of attachedTrace:
|
||||||
|
body.add newHookCall(c.g, t.attachedOps[c.kind], x, y)
|
||||||
|
of attachedDispose:
|
||||||
|
body.add newHookCall(c.g, t.attachedOps[c.kind], x, nil)
|
||||||
|
|
||||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -370,8 +389,10 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
doAssert t.destructor != nil
|
doAssert t.destructor != nil
|
||||||
moveCall.add destructorCall(c.g, t.destructor, x)
|
moveCall.add destructorCall(c.g, t.destructor, x)
|
||||||
body.add moveCall
|
body.add moveCall
|
||||||
of attachedDestructor:
|
of attachedDestructor, attachedDispose:
|
||||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||||
|
of attachedTrace:
|
||||||
|
discard "strings are atomic and have no inner elements that are to trace"
|
||||||
|
|
||||||
proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
|
|
@ -384,7 +405,18 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
||||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
|
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
|
||||||
|
|
||||||
let cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(t)
|
||||||
|
|
||||||
|
var cond: PNode
|
||||||
|
if isCyclic:
|
||||||
|
if isFinal(elemType):
|
||||||
|
let typInfo = genBuiltin(c.g, mGetTypeInfo, "getTypeInfo", newNodeIT(nkType, x.info, elemType))
|
||||||
|
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, x, typInfo)
|
||||||
|
else:
|
||||||
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, x)
|
||||||
|
else:
|
||||||
|
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -392,7 +424,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
|
body.add genIf(c, y, callCodegenProc(c.g,
|
||||||
|
if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
|
|
@ -401,6 +434,22 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t))
|
actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace:
|
||||||
|
if isFinal(elemType):
|
||||||
|
let typInfo = genBuiltin(c.g, mGetTypeInfo, "getTypeInfo", newNodeIT(nkType, x.info, elemType))
|
||||||
|
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
|
body.add callCodegenProc(c.g, "nimTraceRef", c.info, x, typInfo, y)
|
||||||
|
else:
|
||||||
|
# If the ref is polymorphic we have to account for this
|
||||||
|
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, x, y)
|
||||||
|
of attachedDispose:
|
||||||
|
# this is crucial! dispose is like =destroy but we don't follow refs
|
||||||
|
# as that is dealt within the cycle collector.
|
||||||
|
when false:
|
||||||
|
let cond = copyTree(x)
|
||||||
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
|
||||||
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
## Closures are really like refs except they always use a virtual destructor
|
## Closures are really like refs except they always use a virtual destructor
|
||||||
|
|
@ -411,7 +460,10 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv)
|
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv)
|
||||||
|
|
||||||
let cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, xenv)
|
let decRefProc =
|
||||||
|
if c.g.config.selectedGC == gcOrc: "nimDecRefIsLastCyclicDyn"
|
||||||
|
else: "nimDecRefIsLast"
|
||||||
|
let cond = callCodegenProc(c.g, decRefProc, c.info, xenv)
|
||||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -421,7 +473,10 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
||||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
|
let incRefProc =
|
||||||
|
if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic"
|
||||||
|
else: "nimIncRef"
|
||||||
|
body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
|
|
@ -430,6 +485,16 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
actions.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ))
|
actions.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace:
|
||||||
|
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, xenv, y)
|
||||||
|
of attachedDispose:
|
||||||
|
# this is crucial! dispose is like =destroy but we don't follow refs
|
||||||
|
# as that is dealt within the cycle collector.
|
||||||
|
when false:
|
||||||
|
let cond = copyTree(xenv)
|
||||||
|
cond.typ = getSysType(c.g, xenv.info, tyBool)
|
||||||
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, xenv)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
|
||||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -451,6 +516,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
else:
|
else:
|
||||||
body.sons.insert(des, 0)
|
body.sons.insert(des, 0)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace, attachedDispose: discard
|
||||||
|
|
||||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
|
|
@ -473,6 +539,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, x, actions)
|
body.add genIf(c, x, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace, attachedDispose: discard
|
||||||
|
|
||||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if c.kind == attachedDeepCopy:
|
if c.kind == attachedDeepCopy:
|
||||||
|
|
@ -484,7 +551,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
call[1] = y
|
call[1] = y
|
||||||
body.add newAsgnStmt(x, call)
|
body.add newAsgnStmt(x, call)
|
||||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC == gcDestructors:
|
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -506,6 +573,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
else:
|
else:
|
||||||
body.sons.insert(des, 0)
|
body.sons.insert(des, 0)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace, attachedDispose: discard
|
||||||
|
|
||||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||||
|
|
@ -520,6 +588,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, xx, actions)
|
body.add genIf(c, xx, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
of attachedTrace, attachedDispose: discard
|
||||||
|
|
||||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
@ -528,7 +597,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
tyPtr, tyOpt, tyUncheckedArray, tyVar, tyLent:
|
tyPtr, tyOpt, tyUncheckedArray, tyVar, tyLent:
|
||||||
defaultOp(c, t, body, x, y)
|
defaultOp(c, t, body, x, y)
|
||||||
of tyRef:
|
of tyRef:
|
||||||
if c.g.config.selectedGC == gcDestructors:
|
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||||
atomicRefOp(c, t, body, x, y)
|
atomicRefOp(c, t, body, x, y)
|
||||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||||
optRefCheck in c.g.config.options):
|
optRefCheck in c.g.config.options):
|
||||||
|
|
@ -537,7 +606,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
defaultOp(c, t, body, x, y)
|
defaultOp(c, t, body, x, y)
|
||||||
of tyProc:
|
of tyProc:
|
||||||
if t.callConv == ccClosure:
|
if t.callConv == ccClosure:
|
||||||
if c.g.config.selectedGC == gcDestructors:
|
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||||
atomicClosureOp(c, t, body, x, y)
|
atomicClosureOp(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
closureOp(c, t, body, x, y)
|
closureOp(c, t, body, x, y)
|
||||||
|
|
@ -569,7 +638,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# 'selectedGC' here to determine if we have the new runtime.
|
# 'selectedGC' here to determine if we have the new runtime.
|
||||||
discard considerUserDefinedOp(c, t, body, x, y)
|
discard considerUserDefinedOp(c, t, body, x, y)
|
||||||
elif tfHasAsgn in t.flags:
|
elif tfHasAsgn in t.flags:
|
||||||
if c.kind != attachedDestructor:
|
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
|
||||||
body.add newSeqCall(c.g, x, y)
|
body.add newSeqCall(c.g, x, y)
|
||||||
forallElements(c, t, body, x, y)
|
forallElements(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
|
|
@ -632,30 +701,40 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
a.asgnForType = typ
|
a.asgnForType = typ
|
||||||
|
|
||||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
|
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
|
||||||
let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
|
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), result, info)
|
||||||
|
var d: PNode
|
||||||
|
#if kind notin {attachedTrace, attachedDispose}:
|
||||||
dest.typ = makeVarType(typ.owner, typ)
|
dest.typ = makeVarType(typ.owner, typ)
|
||||||
src.typ = typ
|
d = newDeref(newSymNode(dest))
|
||||||
|
#else:
|
||||||
|
# dest.typ = typ
|
||||||
|
# d = newSymNode(dest)
|
||||||
|
|
||||||
|
if kind == attachedTrace:
|
||||||
|
src.typ = getSysType(g, info, tyPointer)
|
||||||
|
else:
|
||||||
|
src.typ = typ
|
||||||
|
|
||||||
result.typ = newProcType(info, typ.owner)
|
result.typ = newProcType(info, typ.owner)
|
||||||
result.typ.addParam dest
|
result.typ.addParam dest
|
||||||
if kind != attachedDestructor:
|
if kind notin {attachedDestructor, attachedDispose}:
|
||||||
result.typ.addParam src
|
result.typ.addParam src
|
||||||
|
|
||||||
# register this operation already:
|
# register this operation already:
|
||||||
typ.attachedOps[kind] = result
|
typ.attachedOps[kind] = result
|
||||||
|
|
||||||
var tk: TTypeKind
|
var tk: TTypeKind
|
||||||
if g.config.selectedGC in {gcDestructors, gcHooks}:
|
if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
|
||||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||||
else:
|
else:
|
||||||
tk = tyNone # no special casing for strings and seqs
|
tk = tyNone # no special casing for strings and seqs
|
||||||
case tk
|
case tk
|
||||||
of tySequence:
|
of tySequence:
|
||||||
fillSeqOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
fillSeqOp(a, typ, body, d, newSymNode(src))
|
||||||
of tyString:
|
of tyString:
|
||||||
fillStrOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
fillStrOp(a, typ, body, d, newSymNode(src))
|
||||||
else:
|
else:
|
||||||
fillBody(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
fillBody(a, typ, body, d, newSymNode(src))
|
||||||
|
|
||||||
var n = newNodeI(nkProcDef, info, bodyPos+1)
|
var n = newNodeI(nkProcDef, info, bodyPos+1)
|
||||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
||||||
|
|
@ -731,8 +810,13 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||||
# 4. We have a custom destructor.
|
# 4. We have a custom destructor.
|
||||||
# 5. We have a (custom) generic destructor.
|
# 5. We have a (custom) generic destructor.
|
||||||
|
|
||||||
|
# we do not generate '=trace' nor '=dispose' procs if we
|
||||||
|
# have the cycle detection disabled, saves code size.
|
||||||
|
let lastAttached = if g.config.selectedGC == gcOrc: attachedDispose
|
||||||
|
else: attachedSink
|
||||||
|
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
for k in attachedDestructor..attachedSink:
|
for k in attachedDestructor..lastAttached:
|
||||||
if canon.attachedOps[k] == nil:
|
if canon.attachedOps[k] == nil:
|
||||||
discard produceSym(g, c, canon, k, info)
|
discard produceSym(g, c, canon, k, info)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ type
|
||||||
cmdJsonScript # compile a .json build file
|
cmdJsonScript # compile a .json build file
|
||||||
TStringSeq* = seq[string]
|
TStringSeq* = seq[string]
|
||||||
TGCMode* = enum # the selected GC
|
TGCMode* = enum # the selected GC
|
||||||
gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcDestructors,
|
gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcArc, gcOrc,
|
||||||
gcHooks,
|
gcHooks,
|
||||||
gcRefc, gcV2, gcGo
|
gcRefc, gcV2, gcGo
|
||||||
# gcRefc and the GCs that follow it use a write barrier,
|
# gcRefc and the GCs that follow it use a write barrier,
|
||||||
|
|
|
||||||
|
|
@ -940,7 +940,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wAcyclic:
|
of wAcyclic:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
# now: ignored
|
else: incl(sym.typ.flags, tfAcyclic)
|
||||||
of wShallow:
|
of wShallow:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import strutils, intsets, tables, ropes, db_sqlite, msgs, options,
|
||||||
## - Dependency computation should use *signature* hashes in order to
|
## - Dependency computation should use *signature* hashes in order to
|
||||||
## avoid recompiling dependent modules.
|
## avoid recompiling dependent modules.
|
||||||
## - Patch the rest of the compiler to do lazy loading of proc bodies.
|
## - Patch the rest of the compiler to do lazy loading of proc bodies.
|
||||||
|
## - serialize the AST in a smarter way (avoid storing some ASTs twice!)
|
||||||
|
|
||||||
template db(): DbConn = g.incr.db
|
template db(): DbConn = g.incr.db
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1601,7 +1601,7 @@ proc borrowCheck(c: PContext, n, le, ri: PNode) =
|
||||||
proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
|
proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
|
||||||
let n = getRoot(ri, followDeref = false)
|
let n = getRoot(ri, followDeref = false)
|
||||||
result = (ri.kind in nkCallKinds+{nkObjConstr}) or
|
result = (ri.kind in nkCallKinds+{nkObjConstr}) or
|
||||||
(n.kind == nkSym and n.sym.owner == c.p.owner)
|
(n.kind == nkSym and n.sym.owner == c.p.owner and n.sym.kind != skResult)
|
||||||
|
|
||||||
proc escapes(c: PContext; le: PNode): bool {.inline.} =
|
proc escapes(c: PContext; le: PNode): bool {.inline.} =
|
||||||
# param[].foo[] = self definitely escapes, we don't need to
|
# param[].foo[] = self definitely escapes, we don't need to
|
||||||
|
|
|
||||||
|
|
@ -872,6 +872,9 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
createTypeBoundOps(tracked, x[1].typ, n.info)
|
createTypeBoundOps(tracked, x[1].typ, n.info)
|
||||||
setLen(tracked.guards.s, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
if tracked.owner.kind != skMacro:
|
if tracked.owner.kind != skMacro:
|
||||||
|
# XXX n.typ can be nil in runnableExamples, we need to do something about it.
|
||||||
|
if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef:
|
||||||
|
createTypeBoundOps(tracked, n.typ.lastSon, n.info)
|
||||||
createTypeBoundOps(tracked, n.typ, n.info)
|
createTypeBoundOps(tracked, n.typ, n.info)
|
||||||
of nkTupleConstr:
|
of nkTupleConstr:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
|
|
|
||||||
|
|
@ -1594,44 +1594,52 @@ proc canonType(c: PContext, t: PType): PType =
|
||||||
else:
|
else:
|
||||||
result = t
|
result = t
|
||||||
|
|
||||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||||
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
||||||
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
if sfOverriden notin prevOp.flags:
|
||||||
if sfOverriden notin prevOp.flags:
|
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
||||||
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
else:
|
||||||
else:
|
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
||||||
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
localError(c.config, info, errGenerated, msg)
|
||||||
localError(c.config, n.info, errGenerated, msg)
|
|
||||||
|
|
||||||
|
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||||
|
let t = s.typ
|
||||||
|
var noError = false
|
||||||
|
let cond = if op == attachedDestructor:
|
||||||
|
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||||
|
else:
|
||||||
|
t.len >= 2 and t[0] == nil
|
||||||
|
|
||||||
|
if cond:
|
||||||
|
var obj = t[1].skipTypes({tyVar})
|
||||||
|
while true:
|
||||||
|
incl(obj.flags, tfHasAsgn)
|
||||||
|
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
|
||||||
|
elif obj.kind == tyGenericInvocation: obj = obj[0]
|
||||||
|
else: break
|
||||||
|
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||||
|
obj = canonType(c, obj)
|
||||||
|
if obj.attachedOps[op] == s:
|
||||||
|
discard "forward declared destructor"
|
||||||
|
elif obj.attachedOps[op].isNil and tfCheckedForDestructor notin obj.flags:
|
||||||
|
obj.attachedOps[op] = s
|
||||||
|
else:
|
||||||
|
prevDestructor(c, obj.attachedOps[op], obj, n.info)
|
||||||
|
noError = true
|
||||||
|
if obj.owner.getModule != s.getModule:
|
||||||
|
localError(c.config, n.info, errGenerated,
|
||||||
|
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||||
|
if not noError and sfSystemModule notin s.owner.flags:
|
||||||
|
localError(c.config, n.info, errGenerated,
|
||||||
|
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||||
|
incl(s.flags, sfUsed)
|
||||||
|
incl(s.flags, sfOverriden)
|
||||||
|
|
||||||
|
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
let name = s.name.s.normalize
|
let name = s.name.s.normalize
|
||||||
case name
|
case name
|
||||||
of "=destroy":
|
of "=destroy":
|
||||||
let t = s.typ
|
bindTypeHook(c, s, n, attachedDestructor)
|
||||||
var noError = false
|
|
||||||
if t.len == 2 and t[0] == nil and t[1].kind == tyVar:
|
|
||||||
var obj = t[1][0]
|
|
||||||
while true:
|
|
||||||
incl(obj.flags, tfHasAsgn)
|
|
||||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
|
|
||||||
elif obj.kind == tyGenericInvocation: obj = obj[0]
|
|
||||||
else: break
|
|
||||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
|
||||||
obj = canonType(c, obj)
|
|
||||||
if obj.attachedOps[attachedDestructor] == s:
|
|
||||||
discard "forward declared destructor"
|
|
||||||
elif obj.destructor.isNil and tfCheckedForDestructor notin obj.flags:
|
|
||||||
obj.attachedOps[attachedDestructor] = s
|
|
||||||
else:
|
|
||||||
prevDestructor(c, obj.destructor, obj, n.info)
|
|
||||||
noError = true
|
|
||||||
if obj.owner.getModule != s.getModule:
|
|
||||||
localError(c.config, n.info, errGenerated,
|
|
||||||
"type bound operation `=destroy` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
|
||||||
if not noError and sfSystemModule notin s.owner.flags:
|
|
||||||
localError(c.config, n.info, errGenerated,
|
|
||||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
|
||||||
incl(s.flags, sfUsed)
|
|
||||||
incl(s.flags, sfOverriden)
|
|
||||||
of "deepcopy", "=deepcopy":
|
of "deepcopy", "=deepcopy":
|
||||||
if s.typ.len == 2 and
|
if s.typ.len == 2 and
|
||||||
s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
|
s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
|
||||||
|
|
@ -1698,6 +1706,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
if sfSystemModule notin s.owner.flags:
|
if sfSystemModule notin s.owner.flags:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
|
"signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
|
||||||
|
of "=trace":
|
||||||
|
bindTypeHook(c, s, n, attachedTrace)
|
||||||
|
of "=dispose":
|
||||||
|
bindTypeHook(c, s, n, attachedDispose)
|
||||||
else:
|
else:
|
||||||
if sfOverriden in s.flags:
|
if sfOverriden in s.flags:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,9 @@ proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool =
|
||||||
proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
|
proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
|
||||||
result = false
|
result = false
|
||||||
if typ == nil: return
|
if typ == nil: return
|
||||||
|
if tfAcyclic in typ.flags: return
|
||||||
var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
|
var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
|
||||||
|
if tfAcyclic in t.flags: return
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
|
of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
|
||||||
if not containsOrIncl(marker, t.id):
|
if not containsOrIncl(marker, t.id):
|
||||||
|
|
|
||||||
|
|
@ -6017,8 +6017,32 @@ The ``noreturn`` pragma is used to mark a proc that never returns.
|
||||||
|
|
||||||
acyclic pragma
|
acyclic pragma
|
||||||
--------------
|
--------------
|
||||||
The ``acyclic`` pragma applies to type declarations. It is deprecated and
|
The ``acyclic`` pragma can be used for object types to mark them as acyclic
|
||||||
ignored.
|
even though they seem to be cyclic. This is an **optimization** for the garbage
|
||||||
|
collector to not consider objects of this type as part of a cycle:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
type
|
||||||
|
Node = ref NodeObj
|
||||||
|
NodeObj {.acyclic.} = object
|
||||||
|
left, right: Node
|
||||||
|
data: string
|
||||||
|
|
||||||
|
Or if we directly use a ref object:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
type
|
||||||
|
Node {.acyclic.} = ref object
|
||||||
|
left, right: Node
|
||||||
|
data: string
|
||||||
|
|
||||||
|
In the example a tree structure is declared with the ``Node`` type. Note that
|
||||||
|
the type definition is recursive and the GC has to assume that objects of
|
||||||
|
this type may form a cyclic graph. The ``acyclic`` pragma passes the
|
||||||
|
information that this cannot happen to the GC. If the programmer uses the
|
||||||
|
``acyclic`` pragma for data types that are in reality cyclic, the memory leaks
|
||||||
|
can be the result, but memory safety is preserved.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final pragma
|
final pragma
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,8 @@ This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``.
|
||||||
If nimcache already contains compiled code from a different compiler for the same project,
|
If nimcache already contains compiled code from a different compiler for the same project,
|
||||||
add the ``-f`` flag to force all files to be recompiled.
|
add the ``-f`` flag to force all files to be recompiled.
|
||||||
|
|
||||||
The default compiler is defined at the top of ``config\nim.cfg``. Changing this setting
|
The default compiler is defined at the top of ``config\nim.cfg``.
|
||||||
affects the compiler used by ``koch`` to (re)build Nim.
|
Changing this setting affects the compiler used by ``koch`` to (re)build Nim.
|
||||||
|
|
||||||
|
|
||||||
Cross compilation
|
Cross compilation
|
||||||
|
|
@ -557,9 +557,9 @@ For example, to generate code for an `AVR`:idx: processor use this command::
|
||||||
For the ``standalone`` target one needs to provide
|
For the ``standalone`` target one needs to provide
|
||||||
a file ``panicoverride.nim``.
|
a file ``panicoverride.nim``.
|
||||||
See ``tests/manyloc/standalone/panicoverride.nim`` for an example
|
See ``tests/manyloc/standalone/panicoverride.nim`` for an example
|
||||||
implementation. Additionally, users should specify the
|
implementation. Additionally, users should specify the
|
||||||
amount of heap space to use with the ``-d:StandaloneHeapSize=<size>``
|
amount of heap space to use with the ``-d:StandaloneHeapSize=<size>``
|
||||||
command line switch. Note that the total heap size will be
|
command line switch. Note that the total heap size will be
|
||||||
``<size> * sizeof(float64)``.
|
``<size> * sizeof(float64)``.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3112,11 +3112,13 @@ when not defined(js):
|
||||||
destructor: pointer
|
destructor: pointer
|
||||||
size: int
|
size: int
|
||||||
name: cstring
|
name: cstring
|
||||||
|
traceImpl: pointer
|
||||||
|
disposeImpl: pointer
|
||||||
PNimType = ptr TNimType
|
PNimType = ptr TNimType
|
||||||
|
|
||||||
when defined(nimSeqsV2) and not defined(nimscript):
|
when defined(nimSeqsV2) and not defined(nimscript):
|
||||||
include "core/strs"
|
include "system/strs_v2"
|
||||||
include "core/seqs"
|
include "system/seqs_v2"
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
@ -3139,7 +3141,7 @@ when not defined(JS) and not defined(nimscript):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
when defined(nimV2):
|
when defined(nimV2):
|
||||||
include core/runtime_v2
|
include system/refs_v2
|
||||||
|
|
||||||
import system/assertions
|
import system/assertions
|
||||||
export assertions
|
export assertions
|
||||||
|
|
|
||||||
232
lib/system/cyclicrefs_v2.nim
Normal file
232
lib/system/cyclicrefs_v2.nim
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2019 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Cycle collector based on Lins' Jump Stack and other ideas,
|
||||||
|
# see for example:
|
||||||
|
# https://pdfs.semanticscholar.org/f2b2/0d168acf38ff86305809a55ef2c5d6ebc787.pdf
|
||||||
|
# Further refinement in 2008 by the notion of "critical links", see
|
||||||
|
# "Cyclic reference counting" by Rafael Dueire Lins
|
||||||
|
# R.D. Lins / Information Processing Letters 109 (2008) 71–78
|
||||||
|
|
||||||
|
const
|
||||||
|
colGreen = 0b000
|
||||||
|
colYellow = 0b001
|
||||||
|
colRed = 0b010
|
||||||
|
jumpStackFlag = 0b100 # stored in jumpstack
|
||||||
|
rcShift = 3 # shift by rcShift to get the reference counter
|
||||||
|
colorMask = 0b011
|
||||||
|
|
||||||
|
type
|
||||||
|
TraceProc = proc (p, env: pointer) {.nimcall, benign.}
|
||||||
|
DisposeProc = proc (p: pointer) {.nimcall, benign.}
|
||||||
|
|
||||||
|
template color(c): untyped = c.rc and colorMask
|
||||||
|
template setColor(c, col) =
|
||||||
|
when col == colGreen:
|
||||||
|
c.rc = c.rc and not colorMask
|
||||||
|
else:
|
||||||
|
c.rc = c.rc and not colorMask or col
|
||||||
|
|
||||||
|
proc nimIncRefCyclic(p: pointer) {.compilerRtl, inl.} =
|
||||||
|
let h = head(p)
|
||||||
|
inc h.rc, rcIncrement
|
||||||
|
h.setColor colYellow # mark as potential cycle!
|
||||||
|
|
||||||
|
proc markCyclic*[T](x: ref T) {.inline.} =
|
||||||
|
## Mark the underlying object as a candidate for cycle collections.
|
||||||
|
## Experimental API. Do not use!
|
||||||
|
let h = head(cast[pointer](x))
|
||||||
|
h.setColor colYellow
|
||||||
|
type
|
||||||
|
CellTuple = (Cell, PNimType)
|
||||||
|
CellArray = ptr UncheckedArray[CellTuple]
|
||||||
|
CellSeq = object
|
||||||
|
len, cap: int
|
||||||
|
d: CellArray
|
||||||
|
|
||||||
|
GcEnv = object
|
||||||
|
traceStack: CellSeq
|
||||||
|
jumpStack: CellSeq
|
||||||
|
|
||||||
|
# ------------------- cell seq handling --------------------------------------
|
||||||
|
|
||||||
|
proc add(s: var CellSeq, c: Cell; t: PNimType) {.inline.} =
|
||||||
|
if s.len >= s.cap:
|
||||||
|
s.cap = s.cap * 3 div 2
|
||||||
|
when defined(useMalloc):
|
||||||
|
var d = cast[CellArray](c_malloc(uint(s.cap * sizeof(CellTuple))))
|
||||||
|
else:
|
||||||
|
var d = cast[CellArray](alloc(s.cap * sizeof(CellTuple)))
|
||||||
|
copyMem(d, s.d, s.len * sizeof(CellTuple))
|
||||||
|
when defined(useMalloc):
|
||||||
|
c_free(s.d)
|
||||||
|
else:
|
||||||
|
dealloc(s.d)
|
||||||
|
s.d = d
|
||||||
|
# XXX: realloc?
|
||||||
|
s.d[s.len] = (c, t)
|
||||||
|
inc(s.len)
|
||||||
|
|
||||||
|
proc init(s: var CellSeq, cap: int = 1024) =
|
||||||
|
s.len = 0
|
||||||
|
s.cap = cap
|
||||||
|
when defined(useMalloc):
|
||||||
|
s.d = cast[CellArray](c_malloc(uint(s.cap * sizeof(CellTuple))))
|
||||||
|
else:
|
||||||
|
s.d = cast[CellArray](alloc(s.cap * sizeof(CellTuple)))
|
||||||
|
|
||||||
|
proc deinit(s: var CellSeq) =
|
||||||
|
when defined(useMalloc):
|
||||||
|
c_free(s.d)
|
||||||
|
else:
|
||||||
|
dealloc(s.d)
|
||||||
|
s.d = nil
|
||||||
|
s.len = 0
|
||||||
|
s.cap = 0
|
||||||
|
|
||||||
|
proc pop(s: var CellSeq): (Cell, PNimType) =
|
||||||
|
result = s.d[s.len-1]
|
||||||
|
dec s.len
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc trace(s: Cell; desc: PNimType; j: var GcEnv) {.inline.} =
|
||||||
|
if desc.traceImpl != nil:
|
||||||
|
var p = s +! sizeof(RefHeader)
|
||||||
|
cast[TraceProc](desc.traceImpl)(p, addr(j))
|
||||||
|
|
||||||
|
proc free(s: Cell; desc: PNimType) {.inline.} =
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[From ] %p rc %ld color %ld in jumpstack %ld\n", s, s.rc shr rcShift,
|
||||||
|
s.color, s.rc and jumpStackFlag)
|
||||||
|
var p = s +! sizeof(RefHeader)
|
||||||
|
if desc.disposeImpl != nil:
|
||||||
|
cast[DisposeProc](desc.disposeImpl)(p)
|
||||||
|
nimRawDispose(p)
|
||||||
|
|
||||||
|
proc collect(s: Cell; desc: PNimType; j: var GcEnv) =
|
||||||
|
if s.color == colRed:
|
||||||
|
s.setColor colGreen
|
||||||
|
trace(s, desc, j)
|
||||||
|
while j.traceStack.len > 0:
|
||||||
|
let (t, desc) = j.traceStack.pop()
|
||||||
|
if t.color == colRed:
|
||||||
|
t.setColor colGreen
|
||||||
|
trace(t, desc, j)
|
||||||
|
free(t, desc)
|
||||||
|
free(s, desc)
|
||||||
|
#cprintf("[Cycle free] %p %ld\n", s, s.rc shr rcShift)
|
||||||
|
|
||||||
|
proc markRed(s: Cell; desc: PNimType; j: var GcEnv) =
|
||||||
|
if s.color != colRed:
|
||||||
|
s.setColor colRed
|
||||||
|
trace(s, desc, j)
|
||||||
|
while j.traceStack.len > 0:
|
||||||
|
let (t, desc) = j.traceStack.pop()
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[Cycle dec] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag)
|
||||||
|
dec t.rc, rcIncrement
|
||||||
|
if (t.rc and not rcMask) >= 0 and (t.rc and jumpStackFlag) == 0:
|
||||||
|
t.rc = t.rc or jumpStackFlag
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[Now in jumpstack] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag)
|
||||||
|
j.jumpStack.add(t, desc)
|
||||||
|
if t.color != colRed:
|
||||||
|
t.setColor colRed
|
||||||
|
trace(t, desc, j)
|
||||||
|
|
||||||
|
proc scanGreen(s: Cell; desc: PNimType; j: var GcEnv) =
|
||||||
|
s.setColor colGreen
|
||||||
|
trace(s, desc, j)
|
||||||
|
while j.traceStack.len > 0:
|
||||||
|
let (t, desc) = j.traceStack.pop()
|
||||||
|
if t.color != colGreen:
|
||||||
|
t.setColor colGreen
|
||||||
|
trace(t, desc, j)
|
||||||
|
inc t.rc, rcIncrement
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[Cycle inc] %p %ld color %ld\n", t, t.rc shr rcShift, t.color)
|
||||||
|
|
||||||
|
proc nimTraceRef(p: pointer; desc: PNimType; env: pointer) {.compilerRtl.} =
|
||||||
|
if p != nil:
|
||||||
|
var t = head(p)
|
||||||
|
var j = cast[ptr GcEnv](env)
|
||||||
|
j.traceStack.add(t, desc)
|
||||||
|
|
||||||
|
proc nimTraceRefDyn(p: pointer; env: pointer) {.compilerRtl.} =
|
||||||
|
if p != nil:
|
||||||
|
let desc = cast[ptr PNimType](p)[]
|
||||||
|
var t = head(p)
|
||||||
|
var j = cast[ptr GcEnv](env)
|
||||||
|
j.traceStack.add(t, desc)
|
||||||
|
|
||||||
|
proc scan(s: Cell; desc: PNimType; j: var GcEnv) =
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[doScanGreen] %p %ld\n", s, s.rc shr rcShift)
|
||||||
|
# even after trial deletion, `s` is still alive, so undo
|
||||||
|
# the decrefs by calling `scanGreen`:
|
||||||
|
if (s.rc and not rcMask) >= 0:
|
||||||
|
scanGreen(s, desc, j)
|
||||||
|
s.setColor colYellow
|
||||||
|
else:
|
||||||
|
# first we have to repair all the nodes we have seen
|
||||||
|
# that are still alive; we also need to mark what they
|
||||||
|
# refer to as alive:
|
||||||
|
while j.jumpStack.len > 0:
|
||||||
|
let (t, desc) = j.jumpStack.pop
|
||||||
|
# not in jump stack anymore!
|
||||||
|
t.rc = t.rc and not jumpStackFlag
|
||||||
|
if t.color == colRed and (t.rc and not rcMask) >= 0:
|
||||||
|
scanGreen(t, desc, j)
|
||||||
|
t.setColor colYellow
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift)
|
||||||
|
# we have proven that `s` and its subgraph are dead, so we can
|
||||||
|
# collect these nodes:
|
||||||
|
collect(s, desc, j)
|
||||||
|
|
||||||
|
proc traceCycle(s: Cell; desc: PNimType) {.noinline.} =
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[traceCycle] %p %ld\n", s, s.rc shr rcShift)
|
||||||
|
var j: GcEnv
|
||||||
|
init j.jumpStack
|
||||||
|
init j.traceStack
|
||||||
|
markRed(s, desc, j)
|
||||||
|
scan(s, desc, j)
|
||||||
|
while j.jumpStack.len > 0:
|
||||||
|
let (t, desc) = j.jumpStack.pop
|
||||||
|
# not in jump stack anymore!
|
||||||
|
t.rc = t.rc and not jumpStackFlag
|
||||||
|
deinit j.jumpStack
|
||||||
|
deinit j.traceStack
|
||||||
|
|
||||||
|
proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} =
|
||||||
|
if p != nil:
|
||||||
|
var cell = head(p)
|
||||||
|
if (cell.rc and not rcMask) == 0:
|
||||||
|
result = true
|
||||||
|
#cprintf("[DESTROY] %p\n", p)
|
||||||
|
else:
|
||||||
|
dec cell.rc, rcIncrement
|
||||||
|
if cell.color == colYellow:
|
||||||
|
let desc = cast[ptr PNimType](p)[]
|
||||||
|
traceCycle(cell, desc)
|
||||||
|
# According to Lins it's correct to do nothing else here.
|
||||||
|
#cprintf("[DeCREF] %p\n", p)
|
||||||
|
|
||||||
|
proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl, inl.} =
|
||||||
|
if p != nil:
|
||||||
|
var cell = head(p)
|
||||||
|
if (cell.rc and not rcMask) == 0:
|
||||||
|
result = true
|
||||||
|
#cprintf("[DESTROY] %p %s\n", p, desc.name)
|
||||||
|
else:
|
||||||
|
dec cell.rc, rcIncrement
|
||||||
|
if cell.color == colYellow: traceCycle(cell, desc)
|
||||||
|
#cprintf("[DeCREF] %p %s %ld\n", p, desc.name, cell.rc)
|
||||||
|
|
@ -499,7 +499,8 @@ else:
|
||||||
when not defined(gcRegions):
|
when not defined(gcRegions):
|
||||||
include "system/alloc"
|
include "system/alloc"
|
||||||
|
|
||||||
include "system/cellsets"
|
when not usesDestructors:
|
||||||
|
include "system/cellsets"
|
||||||
when not leakDetector and not useCellIds:
|
when not leakDetector and not useCellIds:
|
||||||
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
||||||
when compileOption("gc", "v2"):
|
when compileOption("gc", "v2"):
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2019 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
#[
|
#[
|
||||||
In this new runtime we simplify the object layouts a bit: The runtime type
|
In this new runtime we simplify the object layouts a bit: The runtime type
|
||||||
information is only accessed for the objects that have it and it's always
|
information is only accessed for the objects that have it and it's always
|
||||||
|
|
@ -25,11 +34,16 @@ hash of ``package & "." & module & "." & name`` to save space.
|
||||||
|
|
||||||
]#
|
]#
|
||||||
|
|
||||||
|
const
|
||||||
|
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
||||||
|
rcMask = 0b111
|
||||||
|
|
||||||
type
|
type
|
||||||
RefHeader = object
|
RefHeader = object
|
||||||
rc: int # the object header is now a single RC field.
|
rc: int # the object header is now a single RC field.
|
||||||
# we could remove it in non-debug builds for the 'owned ref'
|
# we could remove it in non-debug builds for the 'owned ref'
|
||||||
# design but this seems unwise.
|
# design but this seems unwise.
|
||||||
|
Cell = ptr RefHeader
|
||||||
|
|
||||||
template `+!`(p: pointer, s: int): pointer =
|
template `+!`(p: pointer, s: int): pointer =
|
||||||
cast[pointer](cast[int](p) +% s)
|
cast[pointer](cast[int](p) +% s)
|
||||||
|
|
@ -37,8 +51,11 @@ template `+!`(p: pointer, s: int): pointer =
|
||||||
template `-!`(p: pointer, s: int): pointer =
|
template `-!`(p: pointer, s: int): pointer =
|
||||||
cast[pointer](cast[int](p) -% s)
|
cast[pointer](cast[int](p) -% s)
|
||||||
|
|
||||||
template head(p: pointer): ptr RefHeader =
|
template head(p: pointer): Cell =
|
||||||
cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader))
|
cast[Cell](cast[int](p) -% sizeof(RefHeader))
|
||||||
|
|
||||||
|
const
|
||||||
|
traceCollector = defined(traceArc)
|
||||||
|
|
||||||
var allocs*: int
|
var allocs*: int
|
||||||
|
|
||||||
|
|
@ -56,28 +73,22 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
||||||
atomicInc allocs
|
atomicInc allocs
|
||||||
else:
|
else:
|
||||||
inc allocs
|
inc allocs
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[Allocated] %p\n", result -! sizeof(RefHeader))
|
||||||
|
|
||||||
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
|
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
|
||||||
when hasThreadSupport:
|
dec head(p).rc, rcIncrement
|
||||||
atomicDec head(p).rc
|
|
||||||
else:
|
|
||||||
dec head(p).rc
|
|
||||||
|
|
||||||
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
|
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
|
||||||
when hasThreadSupport:
|
inc head(p).rc, rcIncrement
|
||||||
atomicInc head(p).rc
|
#cprintf("[INCREF] %p\n", p)
|
||||||
else:
|
|
||||||
inc head(p).rc
|
|
||||||
#cprintf("[INCREF] %p\n", p)
|
|
||||||
|
|
||||||
proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
|
when traceCollector:
|
||||||
|
cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
|
||||||
when defined(nimOwnedEnabled):
|
when defined(nimOwnedEnabled):
|
||||||
when hasThreadSupport:
|
if head(p).rc >= rcIncrement:
|
||||||
let hasDanglingRefs = atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) != 0
|
|
||||||
else:
|
|
||||||
let hasDanglingRefs = head(p).rc != 0
|
|
||||||
if hasDanglingRefs:
|
|
||||||
cstderr.rawWrite "[FATAL] dangling references exist\n"
|
cstderr.rawWrite "[FATAL] dangling references exist\n"
|
||||||
quit 1
|
quit 1
|
||||||
when defined(useMalloc):
|
when defined(useMalloc):
|
||||||
|
|
@ -108,21 +119,19 @@ proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
|
||||||
cstderr.rawWrite "has destructor!\n"
|
cstderr.rawWrite "has destructor!\n"
|
||||||
nimRawDispose(p)
|
nimRawDispose(p)
|
||||||
|
|
||||||
|
when defined(gcOrc):
|
||||||
|
include cyclicrefs_v2
|
||||||
|
|
||||||
proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
|
proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
|
||||||
if p != nil:
|
if p != nil:
|
||||||
when hasThreadSupport:
|
var cell = head(p)
|
||||||
if atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) == 0:
|
if (cell.rc and not rcMask) == 0:
|
||||||
result = true
|
result = true
|
||||||
else:
|
#cprintf("[DESTROY] %p\n", p)
|
||||||
discard atomicDec(head(p).rc)
|
|
||||||
else:
|
else:
|
||||||
if head(p).rc == 0:
|
dec cell.rc, rcIncrement
|
||||||
result = true
|
# According to Lins it's correct to do nothing else here.
|
||||||
#cprintf("[DESTROY] %p\n", p)
|
#cprintf("[DeCREF] %p\n", p)
|
||||||
else:
|
|
||||||
dec head(p).rc
|
|
||||||
# According to Lins it's correct to do nothing else here.
|
|
||||||
#cprintf("[DeCREF] %p\n", p)
|
|
||||||
|
|
||||||
proc GC_unref*[T](x: ref T) =
|
proc GC_unref*[T](x: ref T) =
|
||||||
## New runtime only supports this operation for 'ref T'.
|
## New runtime only supports this operation for 'ref T'.
|
||||||
|
|
@ -18,7 +18,7 @@ type
|
||||||
|
|
||||||
when defined(nimv2):
|
when defined(nimv2):
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
|
|
||||||
type
|
type
|
||||||
WideCString* = ptr UncheckedArray[Utf16Char]
|
WideCString* = ptr UncheckedArray[Utf16Char]
|
||||||
|
|
|
||||||
|
|
@ -179,9 +179,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||||
" -d:release -d:useRealtimeGC", cat)
|
" -d:release -d:useRealtimeGC", cat)
|
||||||
when filename != "gctest":
|
when filename != "gctest":
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" --gc:arc", cat)
|
" --gc:orc", cat)
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" --gc:arc -d:release", cat)
|
" --gc:orc -d:release", cat)
|
||||||
|
|
||||||
template testWithoutBoehm(filename: untyped) =
|
template testWithoutBoehm(filename: untyped) =
|
||||||
testWithoutMs filename
|
testWithoutMs filename
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,8 @@ proc parseSpec*(filename: string): TSpec =
|
||||||
of "tcolumn":
|
of "tcolumn":
|
||||||
discard parseInt(e.value, result.tcolumn)
|
discard parseInt(e.value, result.tcolumn)
|
||||||
of "output":
|
of "output":
|
||||||
result.outputCheck = ocEqual
|
if result.outputCheck != ocSubstr:
|
||||||
|
result.outputCheck = ocEqual
|
||||||
result.output = strip(e.value)
|
result.output = strip(e.value)
|
||||||
of "input":
|
of "input":
|
||||||
result.input = e.value
|
result.input = e.value
|
||||||
|
|
@ -200,6 +201,8 @@ proc parseSpec*(filename: string): TSpec =
|
||||||
when defined(linux) and sizeof(int) == 8:
|
when defined(linux) and sizeof(int) == 8:
|
||||||
result.useValgrind = parseCfgBool(e.value)
|
result.useValgrind = parseCfgBool(e.value)
|
||||||
result.unjoinable = true
|
result.unjoinable = true
|
||||||
|
if result.useValgrind:
|
||||||
|
result.outputCheck = ocSubstr
|
||||||
else:
|
else:
|
||||||
# Windows lacks valgrind. Silly OS.
|
# Windows lacks valgrind. Silly OS.
|
||||||
# Valgrind only supports OSX <= 17.x
|
# Valgrind only supports OSX <= 17.x
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,7 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, target: TTarg
|
||||||
else:
|
else:
|
||||||
exeCmd = exeFile
|
exeCmd = exeFile
|
||||||
if expected.useValgrind:
|
if expected.useValgrind:
|
||||||
args = exeCmd & args
|
args = @["--error-exitcode=1"] & exeCmd & args
|
||||||
exeCmd = "valgrind"
|
exeCmd = "valgrind"
|
||||||
var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
|
var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
|
||||||
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
output: '''leak: true'''
|
output: '''leak: false'''
|
||||||
cmd: '''nim c --gc:arc $file'''
|
cmd: '''nim c --gc:orc $file'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -19,11 +19,8 @@ proc addX(x: T; child: T) =
|
||||||
proc main(rootName: string) =
|
proc main(rootName: string) =
|
||||||
var root = create()
|
var root = create()
|
||||||
root.data = rootName
|
root.data = rootName
|
||||||
# this implies we do the refcounting wrong. We should leak memory here
|
|
||||||
# and not create a destruction cycle:
|
|
||||||
root.addX root
|
root.addX root
|
||||||
|
|
||||||
let mem = getOccupiedMem()
|
let mem = getOccupiedMem()
|
||||||
main("yeah")
|
main("yeah")
|
||||||
# since we created a retain cycle, we MUST leak memory here:
|
|
||||||
echo "leak: ", getOccupiedMem() - mem > 0
|
echo "leak: ", getOccupiedMem() - mem > 0
|
||||||
|
|
|
||||||
70
tests/destructor/tarctypesections.nim
Normal file
70
tests/destructor/tarctypesections.nim
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
discard """
|
||||||
|
output: "MEM 0"
|
||||||
|
cmd: "nim c --gc:arc $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
RefNode = ref object
|
||||||
|
le, ri: RefNode
|
||||||
|
name: char
|
||||||
|
|
||||||
|
proc edge0(a, b: RefNode) =
|
||||||
|
if a.le == nil: a.le = b
|
||||||
|
else: a.ri = b
|
||||||
|
|
||||||
|
proc createNode0(name: char): RefNode =
|
||||||
|
new result
|
||||||
|
result.name = name
|
||||||
|
|
||||||
|
proc main0 =
|
||||||
|
let r = createNode0('R')
|
||||||
|
let c = createNode0('C')
|
||||||
|
c.edge0 r
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
NodeDesc = object
|
||||||
|
le, ri: Node
|
||||||
|
name: char
|
||||||
|
Node = ref NodeDesc
|
||||||
|
|
||||||
|
proc edge(a, b: Node) =
|
||||||
|
if a.le == nil: a.le = b
|
||||||
|
else: a.ri = b
|
||||||
|
|
||||||
|
proc createNode(name: char): Node =
|
||||||
|
new result
|
||||||
|
result.name = name
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let r = createNode('R')
|
||||||
|
let c = createNode('C')
|
||||||
|
c.edge r
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
NodeB = ref NodeBo
|
||||||
|
NodeBo = object
|
||||||
|
le, ri: NodeB
|
||||||
|
name: char
|
||||||
|
|
||||||
|
proc edge(a, b: NodeB) =
|
||||||
|
if a.le == nil: a.le = b
|
||||||
|
else: a.ri = b
|
||||||
|
|
||||||
|
proc createNodeB(name: char): NodeB =
|
||||||
|
new result
|
||||||
|
result.name = name
|
||||||
|
|
||||||
|
|
||||||
|
proc mainB =
|
||||||
|
let r = createNodeB('R')
|
||||||
|
let c = createNodeB('C')
|
||||||
|
c.edge r
|
||||||
|
|
||||||
|
|
||||||
|
let memB = getOccupiedMem()
|
||||||
|
main0()
|
||||||
|
main()
|
||||||
|
mainB()
|
||||||
|
echo "MEM ", getOccupiedMem() - memB
|
||||||
54
tests/destructor/tasync_prototype_cyclic.nim
Normal file
54
tests/destructor/tasync_prototype_cyclic.nim
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
discard """
|
||||||
|
output: '''asdas
|
||||||
|
processClient end
|
||||||
|
false
|
||||||
|
MEMORY 0
|
||||||
|
'''
|
||||||
|
cmd: '''nim c --gc:orc $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
PAsyncHttpServer = ref object
|
||||||
|
value: string
|
||||||
|
PFutureBase = ref object
|
||||||
|
callback: proc () {.closure.}
|
||||||
|
value: string
|
||||||
|
failed: bool
|
||||||
|
|
||||||
|
proc accept(server: PAsyncHttpServer): PFutureBase =
|
||||||
|
new(result)
|
||||||
|
result.callback = proc () =
|
||||||
|
discard
|
||||||
|
server.value = "hahaha"
|
||||||
|
|
||||||
|
proc processClient(): PFutureBase =
|
||||||
|
new(result)
|
||||||
|
|
||||||
|
proc serve(server: PAsyncHttpServer): PFutureBase =
|
||||||
|
iterator serveIter(): PFutureBase {.closure.} =
|
||||||
|
echo server.value
|
||||||
|
while true:
|
||||||
|
var acceptAddrFut = server.accept()
|
||||||
|
yield acceptAddrFut
|
||||||
|
var fut = acceptAddrFut.value
|
||||||
|
|
||||||
|
var f = processClient()
|
||||||
|
when true:
|
||||||
|
f.callback =
|
||||||
|
proc () =
|
||||||
|
echo("processClient end")
|
||||||
|
echo(f.failed)
|
||||||
|
yield f
|
||||||
|
var x = serveIter
|
||||||
|
for i in 0 .. 1:
|
||||||
|
result = x()
|
||||||
|
if result.callback != nil:
|
||||||
|
result.callback()
|
||||||
|
|
||||||
|
let mem = getOccupiedMem()
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
discard serve(PAsyncHttpServer(value: "asdas"))
|
||||||
|
|
||||||
|
main()
|
||||||
|
echo "MEMORY ", getOccupiedMem() - mem
|
||||||
|
|
@ -4,7 +4,7 @@ discard """
|
||||||
3 3 alloc/dealloc pairs: 0'''
|
3 3 alloc/dealloc pairs: 0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
|
||||||
53
tests/destructor/tcycle1.nim
Normal file
53
tests/destructor/tcycle1.nim
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
discard """
|
||||||
|
output: "MEM 0"
|
||||||
|
cmd: "nim c --gc:orc $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
Node = ref object of RootObj
|
||||||
|
le, ri: Node
|
||||||
|
name: char
|
||||||
|
|
||||||
|
proc edge(a, b: Node) =
|
||||||
|
if a.le == nil: a.le = b
|
||||||
|
else: a.ri = b
|
||||||
|
|
||||||
|
proc createNode(name: char): Node =
|
||||||
|
new result
|
||||||
|
result.name = name
|
||||||
|
|
||||||
|
#[
|
||||||
|
|
||||||
|
+---------+ +------+
|
||||||
|
| | | |
|
||||||
|
| A +----->+ <------+-------------+
|
||||||
|
+--+------+ | | | |
|
||||||
|
| | | | C |
|
||||||
|
| | R | | |
|
||||||
|
+--v------+ | | +-------------+
|
||||||
|
| | | | ^
|
||||||
|
| B <------+ | |
|
||||||
|
| | | +--------+
|
||||||
|
+---------+ | |
|
||||||
|
+------+
|
||||||
|
|
||||||
|
]#
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let a = createNode('A')
|
||||||
|
let b = createNode('B')
|
||||||
|
let r = createNode('R')
|
||||||
|
let c = createNode('C')
|
||||||
|
|
||||||
|
a.edge b
|
||||||
|
a.edge r
|
||||||
|
|
||||||
|
r.edge b
|
||||||
|
r.edge c
|
||||||
|
|
||||||
|
c.edge r
|
||||||
|
|
||||||
|
|
||||||
|
let mem = getOccupiedMem()
|
||||||
|
main()
|
||||||
|
echo "MEM ", getOccupiedMem() - mem
|
||||||
18
tests/destructor/tcycle2.nim
Normal file
18
tests/destructor/tcycle2.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
discard """
|
||||||
|
output: "MEM 0"
|
||||||
|
cmd: "nim c --gc:orc $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
Node = ref object
|
||||||
|
kids: seq[Node]
|
||||||
|
data: string
|
||||||
|
|
||||||
|
proc main(x: int) =
|
||||||
|
var n = Node(kids: @[], data: "3" & $x)
|
||||||
|
let m = n
|
||||||
|
n.kids.add m
|
||||||
|
|
||||||
|
let mem = getOccupiedMem()
|
||||||
|
main(90)
|
||||||
|
echo "MEM ", getOccupiedMem() - mem
|
||||||
64
tests/destructor/tcycle3.nim
Normal file
64
tests/destructor/tcycle3.nim
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
discard """
|
||||||
|
output: '''BEGIN
|
||||||
|
END
|
||||||
|
END 2
|
||||||
|
0'''
|
||||||
|
cmd: '''nim c --gc:orc $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# extracted from thavlak.nim
|
||||||
|
|
||||||
|
type
|
||||||
|
BasicBlock = ref object
|
||||||
|
inEdges: seq[BasicBlock]
|
||||||
|
outEdges: seq[BasicBlock]
|
||||||
|
name: int
|
||||||
|
|
||||||
|
proc newBasicBlock(name: int): BasicBlock =
|
||||||
|
result = BasicBlock(
|
||||||
|
inEdges: newSeq[BasicBlock](),
|
||||||
|
outEdges: newSeq[BasicBlock](),
|
||||||
|
name: name
|
||||||
|
)
|
||||||
|
|
||||||
|
type
|
||||||
|
Cfg = object
|
||||||
|
basicBlockMap: seq[BasicBlock]
|
||||||
|
startNode: BasicBlock
|
||||||
|
|
||||||
|
proc newCfg(): Cfg =
|
||||||
|
result = Cfg(
|
||||||
|
basicBlockMap: newSeq[BasicBlock](),
|
||||||
|
startNode: nil)
|
||||||
|
|
||||||
|
proc createNode(cfg: var Cfg, name: int): BasicBlock =
|
||||||
|
if name < cfg.basicBlockMap.len:
|
||||||
|
result = cfg.basicBlockMap[name]
|
||||||
|
else:
|
||||||
|
result = newBasicBlock(name)
|
||||||
|
cfg.basicBlockMap.setLen name+1
|
||||||
|
cfg.basicBlockMap[name] = result
|
||||||
|
|
||||||
|
proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
|
||||||
|
echo "BEGIN"
|
||||||
|
let fr = cfg.createNode(fromName)
|
||||||
|
let to = cfg.createNode(toName)
|
||||||
|
|
||||||
|
fr.outEdges.add(to)
|
||||||
|
to.inEdges.add(fr)
|
||||||
|
|
||||||
|
proc run(cfg: var Cfg) =
|
||||||
|
cfg.startNode = cfg.createNode(0) # RC = 2
|
||||||
|
newBasicBlockEdge(cfg, 0, 1) #
|
||||||
|
echo "END"
|
||||||
|
|
||||||
|
discard cfg.createNode(1)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var c = newCfg()
|
||||||
|
c.run
|
||||||
|
echo "END 2"
|
||||||
|
|
||||||
|
let mem = getOccupiedMem()
|
||||||
|
main()
|
||||||
|
echo getOccupiedMem() - mem
|
||||||
|
|
@ -13,7 +13,7 @@ true
|
||||||
41 41'''
|
41 41'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import allocators
|
import system / allocators
|
||||||
include system / ansi_c
|
include system / ansi_c
|
||||||
|
|
||||||
proc main =
|
proc main =
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ test
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: '''nim c --newruntime $file'''
|
valgrind: true
|
||||||
|
cmd: '''nim c --newruntime -d:useMalloc $file'''
|
||||||
output: '''422 422'''
|
output: '''422 422'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import strutils, os, std / wordwrap
|
import strutils, os, std / wordwrap
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
# bug #11004
|
# bug #11004
|
||||||
|
|
|
||||||
26
tests/destructor/tselect.nim
Normal file
26
tests/destructor/tselect.nim
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
discard """
|
||||||
|
output: '''abcsuffix
|
||||||
|
xyzsuffix'''
|
||||||
|
cmd: '''nim c --gc:arc $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc select(cond: bool; a, b: sink string): string =
|
||||||
|
if cond:
|
||||||
|
result = a # moves a into result
|
||||||
|
else:
|
||||||
|
result = b # moves b into result
|
||||||
|
|
||||||
|
proc test(param: string; cond: bool) =
|
||||||
|
var x = "abc" & param
|
||||||
|
var y = "xyz" & param
|
||||||
|
|
||||||
|
# possible self-assignment:
|
||||||
|
x = select(cond, x, y)
|
||||||
|
|
||||||
|
echo x
|
||||||
|
# 'select' must communicate what parameter has been
|
||||||
|
# consumed. We cannot simply generate:
|
||||||
|
# (select(...); wasMoved(x); wasMoved(y))
|
||||||
|
|
||||||
|
test("suffix", true)
|
||||||
|
test("suffix", false)
|
||||||
|
|
@ -8,7 +8,7 @@ hello
|
||||||
2 2 alloc/dealloc pairs: 0'''
|
2 2 alloc/dealloc pairs: 0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
proc main(): owned(proc()) =
|
proc main(): owned(proc()) =
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ discard """
|
||||||
line: 49
|
line: 49
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ discard """
|
||||||
|
|
||||||
import strutils, math
|
import strutils, math
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
import core / allocators
|
import system / allocators
|
||||||
|
|
||||||
proc mainA =
|
proc mainA =
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ clicked!
|
||||||
1 1 alloc/dealloc pairs: 0'''
|
1 1 alloc/dealloc pairs: 0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ clicked!
|
||||||
6 6 alloc/dealloc pairs: 0'''
|
6 6 alloc/dealloc pairs: 0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import core / allocators
|
import system / allocators
|
||||||
import system / ansi_c
|
import system / ansi_c
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ import
|
||||||
|
|
||||||
type
|
type
|
||||||
PNode = ref TNode
|
PNode = ref TNode
|
||||||
TNode {.final.} = object
|
TNode {.final, acyclic.} = object
|
||||||
left, right: PNode
|
left, right: PNode
|
||||||
i, j: int
|
i, j: int
|
||||||
|
|
||||||
proc newNode(L, r: PNode): PNode =
|
proc newNode(L, r: sink PNode): PNode =
|
||||||
new(result)
|
new(result)
|
||||||
result.left = L
|
result.left = L
|
||||||
result.right = r
|
result.right = r
|
||||||
|
|
@ -166,9 +166,13 @@ proc main() =
|
||||||
|
|
||||||
var elapsed = epochTime() - t
|
var elapsed = epochTime() - t
|
||||||
printDiagnostics()
|
printDiagnostics()
|
||||||
echo("Completed in " & $elapsed & "ms. Success!")
|
echo("Completed in " & $elapsed & "s. Success!")
|
||||||
|
|
||||||
when defined(GC_setMaxPause):
|
when defined(GC_setMaxPause):
|
||||||
GC_setMaxPause 2_000
|
GC_setMaxPause 2_000
|
||||||
|
|
||||||
|
when defined(gcDestructors):
|
||||||
|
let mem = getOccupiedMem()
|
||||||
main()
|
main()
|
||||||
|
when defined(gcDestructors):
|
||||||
|
doAssert getOccupiedMem() == mem
|
||||||
|
|
|
||||||
|
|
@ -394,18 +394,20 @@ proc run(self: var LoopTesterApp) =
|
||||||
echo "Constructing CFG..."
|
echo "Constructing CFG..."
|
||||||
var n = 2
|
var n = 2
|
||||||
|
|
||||||
for parlooptrees in 1..10:
|
when not defined(gcOrc):
|
||||||
discard self.cfg.createNode(n + 1)
|
# currently cycle detection is so slow that we disable this part
|
||||||
self.buildConnect(2, n + 1)
|
for parlooptrees in 1..10:
|
||||||
n += 1
|
discard self.cfg.createNode(n + 1)
|
||||||
for i in 1..100:
|
self.buildConnect(2, n + 1)
|
||||||
var top = n
|
n += 1
|
||||||
n = self.buildStraight(n, 1)
|
for i in 1..100:
|
||||||
for j in 1..25: n = self.buildBaseLoop(n)
|
var top = n
|
||||||
var bottom = self.buildStraight(n, 1)
|
n = self.buildStraight(n, 1)
|
||||||
self.buildConnect n, top
|
for j in 1..25: n = self.buildBaseLoop(n)
|
||||||
n = bottom
|
var bottom = self.buildStraight(n, 1)
|
||||||
self.buildConnect(n, 1)
|
self.buildConnect n, top
|
||||||
|
n = bottom
|
||||||
|
self.buildConnect(n, 1)
|
||||||
|
|
||||||
echo "Performing Loop Recognition\n1 Iteration"
|
echo "Performing Loop Recognition\n1 Iteration"
|
||||||
|
|
||||||
|
|
@ -428,5 +430,11 @@ proc run(self: var LoopTesterApp) =
|
||||||
echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
|
echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
|
||||||
echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
|
echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
|
||||||
|
|
||||||
var l = newLoopTesterApp()
|
proc main =
|
||||||
l.run
|
var l = newLoopTesterApp()
|
||||||
|
l.run
|
||||||
|
|
||||||
|
let mem = getOccupiedMem()
|
||||||
|
main()
|
||||||
|
when defined(gcOrc):
|
||||||
|
doAssert getOccupiedMem() == mem
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue