mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-13 14:53:46 +00:00
--newruntime: work in progress
This commit is contained in:
@@ -1165,40 +1165,45 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||
assert refType.kind == tyRef
|
||||
let bt = refType.lastSon
|
||||
if sizeExpr.isNil:
|
||||
sizeExpr = "sizeof($1)" %
|
||||
[getTypeDesc(p.module, bt)]
|
||||
sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
|
||||
|
||||
let ti = genTypeInfo(p.module, typ, a.lode.info)
|
||||
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)
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
b.r = ropecg(p.module, "($1) #nimNewObj($2)",
|
||||
[getTypeDesc(p.module, typ), sizeExpr])
|
||||
genAssignment(p, a, b, {})
|
||||
else:
|
||||
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
|
||||
genAssignment(p, a, b, {}) # set the object type:
|
||||
let ti = genTypeInfo(p.module, typ, a.lode.info)
|
||||
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)
|
||||
|
||||
proc genNew(p: BProc, e: PNode) =
|
||||
@@ -1433,22 +1438,26 @@ proc genNewFinalize(p: BProc, e: PNode) =
|
||||
gcUsage(p.config, e)
|
||||
|
||||
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
|
||||
# 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:
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
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) =
|
||||
var a: TLoc
|
||||
@@ -1925,11 +1934,57 @@ proc genWasMoved(p: BProc; n: PNode) =
|
||||
# addrLoc(p.config, a), getTypeDesc(p.module, a.t))
|
||||
|
||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
if d.k == locNone: getTemp(p, n.typ, d)
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[1].skipAddr, a)
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
if n.len == 4:
|
||||
# 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) =
|
||||
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 mWasMoved: genWasMoved(p, e)
|
||||
of mMove: genMove(p, e, d)
|
||||
of mDestroy: discard "ignore calls to the default destructor"
|
||||
of mDestroy: genDestroy(p, e)
|
||||
of mSlice:
|
||||
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
||||
" '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:
|
||||
add(r, ".Sup")
|
||||
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:
|
||||
linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
|
||||
nilCheck, r, genTypeInfo(p.module, dest, n.info))
|
||||
nilCheck, r, checkFor)
|
||||
else:
|
||||
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:
|
||||
putIntoDest(p, d, n,
|
||||
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
|
||||
@@ -1039,8 +1039,12 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
let isObjFormat = if not p.module.compileToCpp:
|
||||
"#isObj(#getCurrentException()->Sup.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 ")
|
||||
startBlock(p, "if ($1) {$n", [orExpr])
|
||||
if not quirkyExceptions:
|
||||
|
||||
@@ -980,7 +980,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||
discard cgsym(m, "nimTypeRoot")
|
||||
addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
||||
[nameHcr])
|
||||
|
||||
|
||||
if m.hcrOn:
|
||||
addf(m.s[cfsVars], "static TNimType* $1;$n", [name])
|
||||
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",
|
||||
[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 =
|
||||
let origType = t
|
||||
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
|
||||
@@ -1215,14 +1269,6 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
if result != nil:
|
||||
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)
|
||||
if marker.str != nil:
|
||||
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 tySet: genSetInfo(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:
|
||||
# if t.n != nil: genObjectInfo(m, t, result)
|
||||
# else:
|
||||
|
||||
@@ -293,7 +293,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
|
||||
includeHeader(p.module, "<new>")
|
||||
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)
|
||||
of frNone:
|
||||
discard
|
||||
|
||||
@@ -740,6 +740,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
incl(conf.features, destructor)
|
||||
incl(conf.globalOptions, optNimV2)
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
of "stylecheck":
|
||||
case arg.normalize
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
|
||||
@@ -113,9 +113,13 @@ proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
|
||||
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||
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;
|
||||
field: PSym): bool =
|
||||
if tfHasAsgn in t.flags:
|
||||
if tfHasAsgn in t.flags or useNoGc(c, t):
|
||||
var op: PSym
|
||||
if sameType(t, c.asgnForType):
|
||||
# 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)
|
||||
result = true
|
||||
|
||||
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
case c.kind
|
||||
of attachedDestructor:
|
||||
let op = t.destructor
|
||||
if op != nil:
|
||||
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
|
||||
let op = t.destructor
|
||||
if op != nil:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
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)
|
||||
onUse(c.info, op)
|
||||
body.add destructorCall(c.graph, op, x)
|
||||
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:
|
||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
||||
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 =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c.graph, mLeI, "<=", i)
|
||||
cmp.add genHigh(c.graph, dest)
|
||||
let cmp = genBuiltin(c.graph, mLtI, "<", i)
|
||||
cmp.add genLen(c.graph, dest)
|
||||
cmp.typ = getSysType(c.graph, c.info, tyBool)
|
||||
result.sons[0] = cmp
|
||||
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) =
|
||||
let incCall = genBuiltin(c.graph, mInc, "inc", i)
|
||||
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)
|
||||
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) =
|
||||
case t.kind
|
||||
of tyNone, tyEmpty, tyVoid: discard
|
||||
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
|
||||
tyPtr, tyRef, tyOpt, tyUncheckedArray:
|
||||
tyPtr, tyOpt, tyUncheckedArray:
|
||||
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:
|
||||
if tfHasAsgn in t.flags:
|
||||
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
|
||||
if tfHasAsgn in t.flags or useNoGc(c, t):
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tySequence:
|
||||
# note that tfHasAsgn is propagated so we need the check on
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
if c.graph.config.selectedGC == gcDestructors:
|
||||
if useNoGc(c, t):
|
||||
seqOp(c, t, body, x, y)
|
||||
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)
|
||||
elif tfHasAsgn in t.flags:
|
||||
if c.kind != attachedDestructor:
|
||||
body.add newSeqCall(c.graph, x, y)
|
||||
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
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
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)
|
||||
else:
|
||||
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)
|
||||
of tyTuple:
|
||||
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:
|
||||
localError(c.graph.config, c.info, "cannot copy openArray")
|
||||
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
@@ -269,7 +402,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyTypeDesc, tyGenericInvocation, tyForward:
|
||||
internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
|
||||
of tyOrdinal, tyRange, tyInferred,
|
||||
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink, tyOwned:
|
||||
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
|
||||
liftBodyAux(c, lastSon(t), body, x, y)
|
||||
|
||||
proc newProcType(info: TLineInfo; owner: PSym): PType =
|
||||
@@ -290,26 +423,26 @@ proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; inf
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
case kind
|
||||
of attachedAsgn:
|
||||
if baseType.assignment == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.assignment = baseType.assignment
|
||||
result = typ.assignment
|
||||
of attachedSink:
|
||||
if baseType.sink == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.sink = baseType.sink
|
||||
result = typ.sink
|
||||
of attachedDeepCopy:
|
||||
if baseType.deepCopy == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.deepCopy = baseType.deepCopy
|
||||
result = typ.deepCopy
|
||||
of attachedDestructor:
|
||||
if baseType.destructor == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.destructor = baseType.destructor
|
||||
result = typ.destructor
|
||||
of attachedAsgn:
|
||||
if baseType.assignment == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.assignment = baseType.assignment
|
||||
result = typ.assignment
|
||||
of attachedSink:
|
||||
if baseType.sink == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.sink = baseType.sink
|
||||
result = typ.sink
|
||||
of attachedDeepCopy:
|
||||
if baseType.deepCopy == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.deepCopy = baseType.deepCopy
|
||||
result = typ.deepCopy
|
||||
of attachedDestructor:
|
||||
if baseType.destructor == nil:
|
||||
discard liftBody(g, baseType, kind, info)
|
||||
typ.destructor = baseType.destructor
|
||||
result = typ.destructor
|
||||
|
||||
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo): PSym =
|
||||
|
||||
@@ -1600,7 +1600,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSet: result = semSet(c, n, prev)
|
||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||
of mSeq:
|
||||
if c.config.selectedGc == gcDestructors:
|
||||
if c.config.selectedGc == gcDestructors and optNimV2 notin c.config.globalOptions:
|
||||
let s = c.graph.sysTypes[tySequence]
|
||||
assert s != nil
|
||||
assert prev == nil
|
||||
|
||||
@@ -593,7 +593,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
|
||||
of tySequence:
|
||||
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]
|
||||
var old = copyType(s, s.owner, keepId=false)
|
||||
# Remove the 'T' parameter from tySequence:
|
||||
|
||||
78
lib/core/runtime_v2.nim
Normal file
78
lib/core/runtime_v2.nim
Normal 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)
|
||||
@@ -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.
|
||||
]#
|
||||
|
||||
proc `=destroy`[T](s: var seq[T]) =
|
||||
var x = cast[ptr NimSeqV2[T]](addr s)
|
||||
var p = x.p
|
||||
if p != nil:
|
||||
when not defined(nimV2):
|
||||
proc `=destroy`[T](s: var seq[T]) =
|
||||
var x = cast[ptr NimSeqV2[T]](addr s)
|
||||
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`
|
||||
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
|
||||
var a = cast[ptr NimSeqV2[T]](addr x)
|
||||
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
|
||||
|
||||
proc `=`[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 == 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:
|
||||
if a.p == b.p: return
|
||||
`=destroy`(x)
|
||||
a.len = b.len
|
||||
a.p = b.p
|
||||
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)
|
||||
a.len = b.len
|
||||
a.p = b.p
|
||||
|
||||
|
||||
type
|
||||
|
||||
@@ -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 frees(s) =
|
||||
if not isLiteral(s):
|
||||
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
|
||||
when not defined(nimV2):
|
||||
|
||||
proc `=destroy`(s: var string) =
|
||||
var a = cast[ptr NimStringV2](addr s)
|
||||
frees(a)
|
||||
a.len = 0
|
||||
a.p = nil
|
||||
template frees(s) =
|
||||
if not isLiteral(s):
|
||||
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
|
||||
|
||||
proc `=sink`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
# we hope this is optimized away for not yet alive objects:
|
||||
if unlikely(a.p == b.p): return
|
||||
frees(a)
|
||||
a.len = b.len
|
||||
a.p = b.p
|
||||
proc `=destroy`(s: var string) =
|
||||
var a = cast[ptr NimStringV2](addr s)
|
||||
frees(a)
|
||||
a.len = 0
|
||||
a.p = nil
|
||||
|
||||
proc `=`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
if unlikely(a.p == b.p): return
|
||||
frees(a)
|
||||
a.len = b.len
|
||||
if isLiteral(b):
|
||||
# we can shallow copy literals:
|
||||
proc `=sink`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
# we hope this is optimized away for not yet alive objects:
|
||||
if unlikely(a.p == b.p): return
|
||||
frees(a)
|
||||
a.len = b.len
|
||||
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 `=`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
if unlikely(a.p == b.p): return
|
||||
frees(a)
|
||||
a.len = b.len
|
||||
if isLiteral(b):
|
||||
# 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.} =
|
||||
if old <= 0: result = 4
|
||||
|
||||
@@ -63,46 +63,48 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
|
||||
if p == nil:
|
||||
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
|
||||
|
||||
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||
# 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
|
||||
when not defined(nimV2):
|
||||
|
||||
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
|
||||
if a != b:
|
||||
sysFatal(ObjectAssignmentError, "invalid object assignment")
|
||||
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||
# 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
|
||||
|
||||
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;
|
||||
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
|
||||
type ObjCheckCache = array[0..1, PNimType]
|
||||
|
||||
proc isObjWithCache(obj, subclass: PNimType;
|
||||
cache: var ObjCheckCache): bool {.compilerProc, inline.} =
|
||||
if obj == subclass: return true
|
||||
if obj.base == subclass: return true
|
||||
if cache[0] == obj: return false
|
||||
if cache[1] == obj: return true
|
||||
return isObjSlowPath(obj, subclass, cache)
|
||||
proc isObjSlowPath(obj, subclass: 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 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
|
||||
proc isObjWithCache(obj, subclass: PNimType;
|
||||
cache: var ObjCheckCache): bool {.compilerProc, inline.} =
|
||||
if obj == subclass: return true
|
||||
if obj.base == subclass: return true
|
||||
if cache[0] == obj: return false
|
||||
if cache[1] == obj: return true
|
||||
return isObjSlowPath(obj, subclass, cache)
|
||||
|
||||
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
|
||||
|
||||
@@ -496,6 +496,8 @@ else:
|
||||
elif defined(gcRegions):
|
||||
# XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here
|
||||
include "system/gc_regions"
|
||||
elif defined(nimV2):
|
||||
include "core/runtime_v2"
|
||||
elif defined(gcMarkAndSweep) or defined(gcDestructors):
|
||||
# XXX use 'compileOption' here
|
||||
include "system/gc_ms"
|
||||
|
||||
Reference in New Issue
Block a user