--newruntime: work in progress

This commit is contained in:
Andreas Rumpf 2019-03-14 07:59:35 +01:00
commit 2ab6b2c657
13 changed files with 566 additions and 233 deletions

View file

@ -1165,40 +1165,45 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
assert refType.kind == tyRef assert refType.kind == tyRef
let bt = refType.lastSon let bt = refType.lastSon
if sizeExpr.isNil: if sizeExpr.isNil:
sizeExpr = "sizeof($1)" % sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
[getTypeDesc(p.module, bt)]
let ti = genTypeInfo(p.module, typ, a.lode.info) if optNimV2 in p.config.globalOptions:
if bt.destructor != nil: b.r = ropecg(p.module, "($1) #nimNewObj($2)",
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a [getTypeDesc(p.module, typ), sizeExpr])
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling genAssignment(p, a, b, {})
# convention at least:
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault:
localError(p.module.config, a.lode.info,
"the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention")
var f: TLoc
initLocExpr(p, newSymNode(bt.destructor), f)
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
let args = [getTypeDesc(p.module, typ), ti, sizeExpr]
if a.storage == OnHeap and usesWriteBarrier(p.config):
if canFormAcycle(a.t):
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
else:
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
if p.config.selectedGC == gcGo:
# newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
# implement the write barrier
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc)
else:
# use newObjRC1 as an optimization
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
else: else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) let ti = genTypeInfo(p.module, typ, a.lode.info)
genAssignment(p, a, b, {}) # set the object type: if bt.destructor != nil:
# 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
# convention at least:
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault:
localError(p.module.config, a.lode.info,
"the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention")
var f: TLoc
initLocExpr(p, newSymNode(bt.destructor), f)
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
let args = [getTypeDesc(p.module, typ), ti, sizeExpr]
if a.storage == OnHeap and usesWriteBarrier(p.config):
if canFormAcycle(a.t):
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
else:
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
if p.config.selectedGC == gcGo:
# newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
# implement the write barrier
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc)
else:
# use newObjRC1 as an optimization
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
genAssignment(p, a, b, {})
# set the object type:
genObjectInit(p, cpsStmts, bt, a, false) genObjectInit(p, cpsStmts, bt, a, false)
proc genNew(p: BProc, e: PNode) = proc genNew(p: BProc, e: PNode) =
@ -1433,22 +1438,26 @@ proc genNewFinalize(p: BProc, e: PNode) =
gcUsage(p.config, e) gcUsage(p.config, e)
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
# unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we if optNimV2 in p.config.globalOptions:
# have to call it here first:
let ti = genTypeInfo(p.module, dest, info)
if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
tfObjHasKids notin dest.flags):
result = "$1.m_type == $2" % [a, ti]
else:
discard cgsym(p.module, "TNimType")
inc p.module.labels
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
when false:
# former version:
result = ropecg(p.module, "#isObj($1.m_type, $2)", result = ropecg(p.module, "#isObj($1.m_type, $2)",
a, genTypeInfo(p.module, dest, info)) a, genTypeInfo2Name(p.module, dest))
else:
# unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
# have to call it here first:
let ti = genTypeInfo(p.module, dest, info)
if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
tfObjHasKids notin dest.flags):
result = "$1.m_type == $2" % [a, ti]
else:
discard cgsym(p.module, "TNimType")
inc p.module.labels
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
when false:
# former version:
result = ropecg(p.module, "#isObj($1.m_type, $2)",
a, genTypeInfo(p.module, dest, info))
proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
var a: TLoc var a: TLoc
@ -1925,11 +1934,57 @@ proc genWasMoved(p: BProc; n: PNode) =
# addrLoc(p.config, a), getTypeDesc(p.module, a.t)) # addrLoc(p.config, a), getTypeDesc(p.module, a.t))
proc genMove(p: BProc; n: PNode; d: var TLoc) = proc genMove(p: BProc; n: PNode; d: var TLoc) =
if d.k == locNone: getTemp(p, n.typ, d)
var a: TLoc var a: TLoc
initLocExpr(p, n[1].skipAddr, a) initLocExpr(p, n[1].skipAddr, a)
genAssignment(p, d, a, {}) if n.len == 4:
resetLoc(p, a) # generated by liftdestructors:
var src, destroyCall: TLoc
initLocExpr(p, n[2], src)
initLocExpr(p, n[3], destroyCall)
linefmt(p, cpsStmts, "if ($1.len && $1.p != $2.p) { $3; }$n" &
"$1.len = $2.len; $1.p = $2.p;$n",
[rdLoc(a), rdLoc(src), rdLoc(destroyCall)])
else:
if d.k == locNone: getTemp(p, n.typ, d)
genAssignment(p, d, a, {})
resetLoc(p, a)
proc genDestroy(p: BProc; n: PNode) =
if optNimV2 in p.config.globalOptions:
let t = n[1].typ.skipTypes(abstractInst)
case t.kind
of tyString:
var a: TLoc
initLocExpr(p, n[1].skipAddr, a)
linefmt(p, cpsStmts, "if ($1.len && $1.region) {$n" &
" $1.region->dealloc($1.region, $1.p, $1.p->cap + 1 + sizeof(NI) + sizeof(void*)); }$n",
[rdLoc(a)])
of tySequence:
var a: TLoc
initLocExpr(p, n[1].skipAddr, a)
linefmt(p, cpsStmts, "if ($1.len && $1.region) {$n" &
" $1.region->dealloc($1.region, $1.p, ($1.p->cap * sizeof($2)) + sizeof(NI) + sizeof(void*)); }$n",
[rdLoc(a), getTypeDesc(p.module, t.lastSon)])
else: discard "nothing to do"
else:
discard "ignore calls to the default destructor"
proc genDispose(p: BProc; n: PNode) =
when false:
let elemType = n[1].typ.skipTypes(abstractVarInst).lastSon
var a: TLoc
initLocExpr(p, n[1].skipAddr, a)
if isFinal(elemType):
if elemType.destructor != nil:
var destroyCall = newNodeI(nkCall, n.info)
genStmts(p, destroyCall)
lineCg(p, cpsStmts, "#nimRawDispose($#)", rdLoc(a))
else:
# ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
# destructor, but it uses the runtime type. Afterwards the memory is freed:
lineCg(p, cpsStmts, "#nimDestroyAndDispose($#)", rdLoc(a))
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
@ -2090,7 +2145,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mDotDot, mEqCString: genCall(p, e, d) of mDotDot, mEqCString: genCall(p, e, d)
of mWasMoved: genWasMoved(p, e) of mWasMoved: genWasMoved(p, e)
of mMove: genMove(p, e, d) of mMove: genMove(p, e, d)
of mDestroy: discard "ignore calls to the default destructor" of mDestroy: genDestroy(p, e)
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")
@ -2250,12 +2305,16 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
while t.kind == tyObject and t.sons[0] != nil: while t.kind == tyObject and t.sons[0] != nil:
add(r, ".Sup") add(r, ".Sup")
t = skipTypes(t.sons[0], skipPtrs) t = skipTypes(t.sons[0], skipPtrs)
let checkFor = if optNimV2 in p.config.globalOptions:
genTypeInfo2Name(p.module, dest)
else:
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.m_type, $3);$n",
nilCheck, r, genTypeInfo(p.module, dest, n.info)) nilCheck, r, checkFor)
else: else:
linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
r, genTypeInfo(p.module, dest, n.info)) r, checkFor)
if n.sons[0].typ.kind != tyObject: if n.sons[0].typ.kind != tyObject:
putIntoDest(p, d, n, putIntoDest(p, d, n,
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)

View file

@ -1039,8 +1039,12 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
let isObjFormat = if not p.module.compileToCpp: let isObjFormat = if not p.module.compileToCpp:
"#isObj(#getCurrentException()->Sup.m_type, $1)" "#isObj(#getCurrentException()->Sup.m_type, $1)"
else: "#isObj(#getCurrentException()->m_type, $1)" else: "#isObj(#getCurrentException()->m_type, $1)"
appcg(p.module, orExpr, isObjFormat,
[genTypeInfo(p.module, t[i][j].typ, t[i][j].info)]) let checkFor = if optNimV2 in p.config.globalOptions:
genTypeInfo2Name(p.module, t[i][j].typ)
else:
genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, isObjFormat, [checkFor])
if i > 1: line(p, cpsStmts, "else ") if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr]) startBlock(p, "if ($1) {$n", [orExpr])
if not quirkyExceptions: if not quirkyExceptions:

View file

@ -980,7 +980,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
discard cgsym(m, "nimTypeRoot") discard cgsym(m, "nimTypeRoot")
addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n", addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
[nameHcr]) [nameHcr])
if m.hcrOn: if m.hcrOn:
addf(m.s[cfsVars], "static TNimType* $1;$n", [name]) addf(m.s[cfsVars], "static TNimType* $1;$n", [name])
addf(m.hcrCreateTypeInfosProc, "\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n", addf(m.hcrCreateTypeInfosProc, "\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
@ -1204,6 +1204,60 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
[result, s.loc.r]) [result, s.loc.r])
proc declareNimType(m: BModule, str: Rope, ownerModule: PSym) =
if m.hcrOn:
addf(m.s[cfsVars], "static TNimType* $1;$n", [str])
addf(m.s[cfsTypeInit1], "\t$1 = (TNimType*)hcrGetGlobal($2, \"$1\");$n",
[str, getModuleDllPath(m, ownerModule)])
else:
addf(m.s[cfsVars], "extern TNimType $1;$n", [str])
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
var res = "|"
var it = t
while it != nil:
it = it.skipTypes(skipPtrs)
if it.sym != nil:
var m = t.sym.owner
while m != nil and m.kind != skModule: m = m.owner
if m == nil or sfSystemModule in m.flags:
# produce short names for system types:
res.add it.sym.name.s
else:
var p = m.owner
if p != nil and p.kind == skPackage:
res.add p.name.s & "."
res.add m.name.s & "."
res.add it.sym.name.s
else:
res.add $hashType(it)
res.add "|"
it = it.sons[0]
result = makeCString(res)
proc genObjectInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
assert t.kind == tyObject
if incompleteType(t):
localError(m.config, info, "request for RTTI generation for incomplete object: " &
typeToString(t))
var d: Rope
if t.destructor != nil:
# 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
# convention at least:
if t.destructor.typ == nil or t.destructor.typ.callConv != ccDefault:
localError(m.config, info,
"the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention")
genProc(m, t.destructor)
d = t.destructor.loc.r
else:
d = rope("NIM_NIL")
addf(m.s[cfsTypeInit3], "$1.destructor = $2; $1.size = sizeof($3); $1.name = $4;$n", [
name, d, getTypeDesc(m, t), genTypeInfo2Name(m, t)])
proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
let origType = t let origType = t
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
@ -1215,14 +1269,6 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
if result != nil: if result != nil:
return prefixTI.rope & result & ")".rope return prefixTI.rope & result & ")".rope
proc declareNimType(m: BModule, str: Rope, ownerModule: PSym) =
if m.hcrOn:
addf(m.s[cfsVars], "static TNimType* $1;$n", [str])
addf(m.s[cfsTypeInit1], "\t$1 = (TNimType*)hcrGetGlobal($2, \"$1\");$n",
[str, getModuleDllPath(m, ownerModule)])
else:
addf(m.s[cfsVars], "extern TNimType $1;$n", [str])
let marker = m.g.typeInfoMarker.getOrDefault(sig) let marker = m.g.typeInfoMarker.getOrDefault(sig)
if marker.str != nil: if marker.str != nil:
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
@ -1278,7 +1324,11 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
of tyArray: genArrayInfo(m, t, result, info) of tyArray: genArrayInfo(m, t, result, info)
of tySet: genSetInfo(m, t, result, info) of tySet: genSetInfo(m, t, result, info)
of tyEnum: genEnumInfo(m, t, result, info) of tyEnum: genEnumInfo(m, t, result, info)
of tyObject: genObjectInfo(m, t, origType, result, info) of tyObject:
if optNimV2 in m.config.globalOptions:
genObjectInfoV2(m, t, origType, result, info)
else:
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:

View file

@ -293,7 +293,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
includeHeader(p.module, "<new>") includeHeader(p.module, "<new>")
linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t)) linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
if optNimV2 in p.config.globalOptions: return #if optNimV2 in p.config.globalOptions: return
case analyseObjectWithTypeField(t) case analyseObjectWithTypeField(t)
of frNone: of frNone:
discard discard

View file

@ -740,6 +740,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
incl(conf.features, destructor) incl(conf.features, destructor)
incl(conf.globalOptions, optNimV2) incl(conf.globalOptions, optNimV2)
defineSymbol(conf.symbols, "nimV2") defineSymbol(conf.symbols, "nimV2")
conf.selectedGC = gcDestructors
defineSymbol(conf.symbols, "gcdestructors")
of "stylecheck": of "stylecheck":
case arg.normalize case arg.normalize
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError} of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}

View file

@ -113,9 +113,13 @@ proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newOpCall(op, y)) result = newAsgnStmt(x, newOpCall(op, y))
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
result = optNimV2 in c.graph.config.globalOptions and
(tfHasGCedMem in t.flags or t.isGCedMem)
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
field: PSym): bool = field: PSym): bool =
if tfHasAsgn in t.flags: if tfHasAsgn in t.flags or useNoGc(c, t):
var op: PSym var op: PSym
if sameType(t, c.asgnForType): if sameType(t, c.asgnForType):
# generate recursive call: # generate recursive call:
@ -136,15 +140,28 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
body.add newAsgnCall(c.graph, op, x, y) body.add newAsgnCall(c.graph, op, x, y)
result = true result = true
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
case c.kind let op = t.destructor
of attachedDestructor: if op != nil:
let op = t.destructor markUsed(c.graph.config, c.info, op, c.graph.usageSym)
if op != nil: onUse(c.info, op)
body.add destructorCall(c.graph, op, x)
result = true
elif useNoGc(c, t):
if sameType(t, c.asgnForType) and c.kind == attachedDestructor:
let op = c.fn
markUsed(c.graph.config, c.info, op, c.graph.usageSym) markUsed(c.graph.config, c.info, op, c.graph.usageSym)
onUse(c.info, op) onUse(c.info, op)
body.add destructorCall(c.graph, op, x) body.add destructorCall(c.graph, op, x)
result = true result = true
else:
internalError(c.graph.config, c.info,
"type-bound operator could not be resolved")
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
case c.kind
of attachedDestructor:
result = addDestructorCall(c, t, body, x)
of attachedAsgn: of attachedAsgn:
result = considerAsgnOrSink(c, t, body, x, y, t.assignment) result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
of attachedSink: of attachedSink:
@ -185,12 +202,15 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2) result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c.graph, mLeI, "<=", i) let cmp = genBuiltin(c.graph, mLtI, "<", i)
cmp.add genHigh(c.graph, dest) cmp.add genLen(c.graph, dest)
cmp.typ = getSysType(c.graph, c.info, tyBool) cmp.typ = getSysType(c.graph, c.info, tyBool)
result.sons[0] = cmp result.sons[0] = cmp
result.sons[1] = newNodeI(nkStmtList, c.info) result.sons[1] = newNodeI(nkStmtList, c.info)
proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
proc addIncStmt(c: var TLiftCtx; body, i: PNode) = proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
let incCall = genBuiltin(c.graph, mInc, "inc", i) let incCall = genBuiltin(c.graph, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.graph, c.info, 1) incCall.add lowerings.newIntLit(c.graph, c.info, 1)
@ -203,42 +223,166 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
lenCall.typ = getSysType(g, x.info, tyInt) lenCall.typ = getSysType(g, x.info, tyInt)
result.add lenCall result.add lenCall
proc setLenCall(g: ModuleGraph; x, y: PNode): PNode =
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
lenCall.typ = getSysType(g, x.info, tyInt)
result = genBuiltin(g, mSetLengthSeq, "setLen", genAddr(g, x))
result.add lenCall
proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(c, whileLoop.sons[1], i)
body.add whileLoop
proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedAsgn, attachedDeepCopy:
# we generate:
# setLen(dest, y.len)
# var i = 0
# while i < y.len: dest[i] = y[i]; inc(i)
# This is usually more efficient than a destroy/create pair.
body.add setLenCall(c.graph, x, y)
forallElements(c, t, body, x, y)
of attachedSink:
let moveCall = genBuiltin(c.graph, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c.graph, t.destructor, x)
body.add moveCall
when false:
# we generate:
# if a.len != 0 and a.p != b.p:
# `=destroy`(x)
# a.len = b.len
# a.p = b.p
# Note: '@' is either '.' or '->'.
body.add genIf(c, genVerbatim("dest@len != 0 && dest@p != src.p", c.info),
destructorCall(c.graph, t.destructor, x))
body.add genVerbatim("dest@len=src.len; dest@p=src.p;", c.info)
of attachedDestructor:
# destroy all elements:
forallElements(c, t, body, x, y)
body.add genBuiltin(c.graph, mDestroy, "destroy", x)
when false:
var deallocStmt = genVerbatim("dest@region->dealloc(dest@region, dest@p, " &
"(dest@p->cap * sizeof($)) + sizeof(NI) + sizeof(void*)); dest@len = 0;", c.info)
deallocStmt.typ = t.lastSon
body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
seqOp(c, t, body, x, y)
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedSink:
# we 'nil' y out afterwards so we *need* to take over its reference
# count value:
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
body.add newAsgnStmt(x, y)
of attachedAsgn:
body.add callCodegenProc(c.graph, "nimIncWeakRef", c.info, y)
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
body.add newAsgnStmt(x, y)
of attachedDestructor:
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
of attachedDeepCopy: assert(false, "cannot happen")
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.lastSon
#liftBodyAux(c, elemType, actions, genDeref(x), genDeref(y))
#var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
if isFinal(elemType):
discard addDestructorCall(c, elemType, actions, x)
actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
else:
discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), x)
actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
case c.kind
of attachedSink, attachedAsgn:
body.add genIf(c, x, actions)
body.add newAsgnStmt(x, y)
of attachedDestructor:
body.add genIf(c, x, actions)
of attachedDeepCopy: assert(false, "cannot happen")
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind == attachedDeepCopy:
# a big problem is that we don't know the enviroment's type here, so we
# have to go through some indirection; we delegate this to the codegen:
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
call.sons[1] = y
body.add newAsgnStmt(x, call)
elif optNimV2 in c.graph.config.globalOptions:
case c.kind
of attachedSink, attachedAsgn: discard
of attachedDestructor: discard
of attachedDeepCopy: assert(false, "cannot happen")
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard "to implement"
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind case t.kind
of tyNone, tyEmpty, tyVoid: discard of tyNone, tyEmpty, tyVoid: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
tyPtr, tyRef, tyOpt, tyUncheckedArray: tyPtr, tyOpt, tyUncheckedArray:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyRef:
if optNimV2 in c.graph.config.globalOptions:
weakrefOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyProc:
if t.callConv == ccClosure:
closureOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyOwned:
let base = t.skipTypes(abstractInstOwned)
if optNimV2 in c.graph.config.globalOptions:
case base.kind
of tyRef:
ownedRefOp(c, base, body, x, y)
return
of tyProc:
if base.callConv == ccClosure:
ownedClosureOp(c, base, body, x, y)
return
else: discard
defaultOp(c, base, body, x, y)
of tyArray: of tyArray:
if tfHasAsgn in t.flags: if tfHasAsgn in t.flags or useNoGc(c, t):
let i = declareCounter(c, body, firstOrd(c.graph.config, t)) forallElements(c, t, body, x, y)
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(c, whileLoop.sons[1], i)
body.add whileLoop
else: else:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tySequence: of tySequence:
# note that tfHasAsgn is propagated so we need the check on if useNoGc(c, t):
# 'selectedGC' here to determine if we have the new runtime. seqOp(c, t, body, x, y)
if c.graph.config.selectedGC == gcDestructors: elif c.graph.config.selectedGC == gcDestructors:
# note that tfHasAsgn is propagated so we need the check on
# 'selectedGC' here to determine if we have the new runtime.
discard considerOverloadedOp(c, t, body, x, y) discard considerOverloadedOp(c, t, body, x, y)
elif tfHasAsgn in t.flags: elif tfHasAsgn in t.flags:
if c.kind != attachedDestructor: if c.kind != attachedDestructor:
body.add newSeqCall(c.graph, x, y) body.add newSeqCall(c.graph, x, y)
let i = declareCounter(c, body, firstOrd(c.graph.config, t)) forallElements(c, t, body, x, y)
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(c, whileLoop.sons[1], i)
body.add whileLoop
else: else:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyString: of tyString:
if tfHasAsgn in t.flags: if useNoGc(c, t):
strOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
discard considerOverloadedOp(c, t, body, x, y) discard considerOverloadedOp(c, t, body, x, y)
else: else:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
@ -250,17 +394,6 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y) liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
of tyTuple: of tyTuple:
liftBodyTup(c, t, body, x, y) liftBodyTup(c, t, body, x, y)
of tyProc:
if t.callConv != ccClosure or c.kind != attachedDeepCopy:
defaultOp(c, t, body, x, y)
else:
# a big problem is that we don't know the enviroment's type here, so we
# have to go through some indirection; we delegate this to the codegen:
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
call.sons[1] = y
body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray: of tyVarargs, tyOpenArray:
localError(c.graph.config, c.info, "cannot copy openArray") localError(c.graph.config, c.info, "cannot copy openArray")
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@ -269,7 +402,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyTypeDesc, tyGenericInvocation, tyForward: tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t)) internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange, tyInferred, of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink, tyOwned: tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
liftBodyAux(c, lastSon(t), body, x, y) liftBodyAux(c, lastSon(t), body, x, y)
proc newProcType(info: TLineInfo; owner: PSym): PType = proc newProcType(info: TLineInfo; owner: PSym): PType =
@ -290,26 +423,26 @@ proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; inf
assert typ.kind == tyDistinct assert typ.kind == tyDistinct
let baseType = typ[0] let baseType = typ[0]
case kind case kind
of attachedAsgn: of attachedAsgn:
if baseType.assignment == nil: if baseType.assignment == nil:
discard liftBody(g, baseType, kind, info) discard liftBody(g, baseType, kind, info)
typ.assignment = baseType.assignment typ.assignment = baseType.assignment
result = typ.assignment result = typ.assignment
of attachedSink: of attachedSink:
if baseType.sink == nil: if baseType.sink == nil:
discard liftBody(g, baseType, kind, info) discard liftBody(g, baseType, kind, info)
typ.sink = baseType.sink typ.sink = baseType.sink
result = typ.sink result = typ.sink
of attachedDeepCopy: of attachedDeepCopy:
if baseType.deepCopy == nil: if baseType.deepCopy == nil:
discard liftBody(g, baseType, kind, info) discard liftBody(g, baseType, kind, info)
typ.deepCopy = baseType.deepCopy typ.deepCopy = baseType.deepCopy
result = typ.deepCopy result = typ.deepCopy
of attachedDestructor: of attachedDestructor:
if baseType.destructor == nil: if baseType.destructor == nil:
discard liftBody(g, baseType, kind, info) discard liftBody(g, baseType, kind, info)
typ.destructor = baseType.destructor typ.destructor = baseType.destructor
result = typ.destructor result = typ.destructor
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym = info: TLineInfo): PSym =

View file

@ -1600,7 +1600,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mSet: result = semSet(c, n, prev) of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: of mSeq:
if c.config.selectedGc == gcDestructors: if c.config.selectedGc == gcDestructors and optNimV2 notin c.config.globalOptions:
let s = c.graph.sysTypes[tySequence] let s = c.graph.sysTypes[tySequence]
assert s != nil assert s != nil
assert prev == nil assert prev == nil

View file

@ -593,7 +593,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
of tySequence: of tySequence:
if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and
result[0].kind != tyEmpty: result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
let s = cl.c.graph.sysTypes[tySequence] let s = cl.c.graph.sysTypes[tySequence]
var old = copyType(s, s.owner, keepId=false) var old = copyType(s, s.owner, keepId=false)
# Remove the 'T' parameter from tySequence: # Remove the 'T' parameter from tySequence:

78
lib/core/runtime_v2.nim Normal file
View file

@ -0,0 +1,78 @@
#[
In this new runtime we simply the object layouts a bit: The runtime type
information is only accessed for the objects that have it and it's always
at offset 0 then. The ``ref`` object header is independent from the
runtime type and only contains a reference count.
Object subtyping is checked via the generated 'name'. This should have
comparable overhead to the old pointer chasing approach but has the benefit
that it works across DLL boundaries.
The generated name is a concatenation of the object names in the hierarchy
so that a subtype check becomes a substring check. For example::
type
ObjectA = object of RootObj
ObjectB = object of ObjectA
ObjectA's ``name`` is "|ObjectA|RootObj|".
ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
Now to check for ``x of ObjectB`` we need to check
for ``x.typ.name.hasSubstring("|ObjectB|")``. In the actual implementation,
however, we could also use a
hash of ``package & "." & module & "." & name`` to save space.
]#
type
TNimNode {.compilerProc.} = object # to keep the code generator simple
TNimType {.compilerProc.} = object
destructor: proc (p: pointer) {.nimcall, benign.}
size: int
name: cstring
PNimType = ptr TNimType
ObjHeader = object
rc: int # the object header is now a single RC field.
# we could remove it in non-debug builds but this seems
# unwise.
proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
result = strstr(obj.name, subclass) != nil
proc chckObj(obj: PNimType, subclass: cstring) {.compilerproc.} =
# checks if obj is of type subclass:
if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")
template `+!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) +% s)
template `-!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) -% s)
template head(p: pointer): ptr ObjHeader =
cast[ptr ObjHeader](cast[int](p) -% sizeof(ObjHeader))
proc nimNewObj(size: int): pointer {.compilerRtl.} =
result = alloc0(size + sizeof(ObjHeader)) +! sizeof(ObjHeader)
# XXX Respect defined(useMalloc) here!
proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
dec head(p).rc
proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
inc head(p).rc
proc nimRawDispose(p: pointer) {.compilerRtl.} =
if head(p).rc != 0:
cstderr.rawWrite "[FATAL] dangling references exist\n"
quit 1
dealloc(p -! sizeof(ObjHeader))
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
let d = cast[ptr PNimType](p)[].destructor
if d != nil: d(p)
nimRawDispose(p)

View file

@ -44,43 +44,44 @@ This means the check for whether ``s.p`` needs to be freed should
be ``s.len == 0`` even though that feels slightly more awkward. be ``s.len == 0`` even though that feels slightly more awkward.
]# ]#
proc `=destroy`[T](s: var seq[T]) = when not defined(nimV2):
var x = cast[ptr NimSeqV2[T]](addr s) proc `=destroy`[T](s: var seq[T]) =
var p = x.p var x = cast[ptr NimSeqV2[T]](addr s)
if p != nil: var p = x.p
if p != nil:
mixin `=destroy`
when not supportsCopyMem(T):
for i in 0..<x.len: `=destroy`(p.data[i])
if p.region != nil:
p.region.dealloc(p.region, p, payloadSize(p.cap))
x.p = nil
x.len = 0
proc `=`[T](x: var seq[T]; y: seq[T]) =
mixin `=destroy` mixin `=destroy`
when not supportsCopyMem(T): var a = cast[ptr NimSeqV2[T]](addr x)
for i in 0..<x.len: `=destroy`(p.data[i]) var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if p.region != nil:
p.region.dealloc(p.region, p, payloadSize(p.cap))
x.p = nil
x.len = 0
proc `=`[T](x: var seq[T]; y: seq[T]) = if a.p == b.p: return
mixin `=destroy`
var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if a.p == b.p: return
`=destroy`(x)
a.len = b.len
if b.p != nil:
a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
when supportsCopyMem(T):
if a.len > 0:
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
else:
for i in 0..<a.len:
a.p.data[i] = b.p.data[i]
proc `=sink`[T](x: var seq[T]; y: seq[T]) =
mixin `=destroy`
var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if a.p != nil and a.p != b.p:
`=destroy`(x) `=destroy`(x)
a.len = b.len a.len = b.len
a.p = b.p if b.p != nil:
a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
when supportsCopyMem(T):
if a.len > 0:
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
else:
for i in 0..<a.len:
a.p.data[i] = b.p.data[i]
proc `=sink`[T](x: var seq[T]; y: seq[T]) =
mixin `=destroy`
var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if a.p != nil and a.p != b.p:
`=destroy`(x)
a.len = b.len
a.p = b.p
type type

View file

@ -41,43 +41,45 @@ template isLiteral(s): bool = s.p == nil or s.p.region == nil
template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator) template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
template frees(s) = when not defined(nimV2):
if not isLiteral(s):
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
proc `=destroy`(s: var string) = template frees(s) =
var a = cast[ptr NimStringV2](addr s) if not isLiteral(s):
frees(a) s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
a.len = 0
a.p = nil
proc `=sink`(x: var string, y: string) = proc `=destroy`(s: var string) =
var a = cast[ptr NimStringV2](addr x) var a = cast[ptr NimStringV2](addr s)
var b = cast[ptr NimStringV2](unsafeAddr y) frees(a)
# we hope this is optimized away for not yet alive objects: a.len = 0
if unlikely(a.p == b.p): return a.p = nil
frees(a)
a.len = b.len
a.p = b.p
proc `=`(x: var string, y: string) = proc `=sink`(x: var string, y: string) =
var a = cast[ptr NimStringV2](addr x) var a = cast[ptr NimStringV2](addr x)
var b = cast[ptr NimStringV2](unsafeAddr y) var b = cast[ptr NimStringV2](unsafeAddr y)
if unlikely(a.p == b.p): return # we hope this is optimized away for not yet alive objects:
frees(a) if unlikely(a.p == b.p): return
a.len = b.len frees(a)
if isLiteral(b): a.len = b.len
# we can shallow copy literals:
a.p = b.p a.p = b.p
else:
let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator() proc `=`(x: var string, y: string) =
# we have to allocate the 'cap' here, consider var a = cast[ptr NimStringV2](addr x)
# 'let y = newStringOfCap(); var x = y' var b = cast[ptr NimStringV2](unsafeAddr y)
# on the other hand... These get turned into moves now. if unlikely(a.p == b.p): return
a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len))) frees(a)
a.p.region = region a.len = b.len
a.p.cap = b.len if isLiteral(b):
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) # we can shallow copy literals:
a.p = b.p
else:
let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator()
# we have to allocate the 'cap' here, consider
# 'let y = newStringOfCap(); var x = y'
# on the other hand... These get turned into moves now.
a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
a.p.region = region
a.p.cap = b.len
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
proc resize(old: int): int {.inline.} = proc resize(old: int): int {.inline.} =
if old <= 0: result = 4 if old <= 0: result = 4

View file

@ -63,46 +63,48 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
if p == nil: if p == nil:
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
proc chckObj(obj, subclass: PNimType) {.compilerproc.} = when not defined(nimV2):
# checks if obj is of type subclass:
var x = obj
if x == subclass: return # optimized fast path
while x != subclass:
if x == nil:
sysFatal(ObjectConversionError, "invalid object conversion")
x = x.base
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
if a != b: # checks if obj is of type subclass:
sysFatal(ObjectAssignmentError, "invalid object assignment") var x = obj
if x == subclass: return # optimized fast path
while x != subclass:
if x == nil:
sysFatal(ObjectConversionError, "invalid object conversion")
x = x.base
type ObjCheckCache = array[0..1, PNimType] proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
if a != b:
sysFatal(ObjectAssignmentError, "invalid object assignment")
proc isObjSlowPath(obj, subclass: PNimType; type ObjCheckCache = array[0..1, PNimType]
cache: var ObjCheckCache): bool {.noinline.} =
# checks if obj is of type subclass:
var x = obj.base
while x != subclass:
if x == nil:
cache[0] = obj
return false
x = x.base
cache[1] = obj
return true
proc isObjWithCache(obj, subclass: PNimType; proc isObjSlowPath(obj, subclass: PNimType;
cache: var ObjCheckCache): bool {.compilerProc, inline.} = cache: var ObjCheckCache): bool {.noinline.} =
if obj == subclass: return true # checks if obj is of type subclass:
if obj.base == subclass: return true var x = obj.base
if cache[0] == obj: return false while x != subclass:
if cache[1] == obj: return true if x == nil:
return isObjSlowPath(obj, subclass, cache) cache[0] = obj
return false
x = x.base
cache[1] = obj
return true
proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = proc isObjWithCache(obj, subclass: PNimType;
# checks if obj is of type subclass: cache: var ObjCheckCache): bool {.compilerProc, inline.} =
var x = obj if obj == subclass: return true
if x == subclass: return true # optimized fast path if obj.base == subclass: return true
while x != subclass: if cache[0] == obj: return false
if x == nil: return false if cache[1] == obj: return true
x = x.base return isObjSlowPath(obj, subclass, cache)
return true
proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
# checks if obj is of type subclass:
var x = obj
if x == subclass: return true # optimized fast path
while x != subclass:
if x == nil: return false
x = x.base
return true

View file

@ -496,6 +496,8 @@ else:
elif defined(gcRegions): elif defined(gcRegions):
# XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here
include "system/gc_regions" include "system/gc_regions"
elif defined(nimV2):
include "core/runtime_v2"
elif defined(gcMarkAndSweep) or defined(gcDestructors): elif defined(gcMarkAndSweep) or defined(gcDestructors):
# XXX use 'compileOption' here # XXX use 'compileOption' here
include "system/gc_ms" include "system/gc_ms"