--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,9 +1165,13 @@ 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)]
if optNimV2 in p.config.globalOptions:
b.r = ropecg(p.module, "($1) #nimNewObj($2)",
[getTypeDesc(p.module, typ), sizeExpr])
genAssignment(p, a, b, {})
else:
let ti = genTypeInfo(p.module, typ, a.lode.info) let ti = genTypeInfo(p.module, typ, a.lode.info)
if bt.destructor != nil: if bt.destructor != nil:
# 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
@ -1198,7 +1202,8 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
else: else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
genAssignment(p, a, b, {}) # set the object type: 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,6 +1438,10 @@ 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 =
if optNimV2 in p.config.globalOptions:
result = ropecg(p.module, "#isObj($1.m_type, $2)",
a, genTypeInfo2Name(p.module, dest))
else:
# unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
# have to call it here first: # have to call it here first:
let ti = genTypeInfo(p.module, dest, info) let ti = genTypeInfo(p.module, dest, info)
@ -1925,12 +1934,58 @@ 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)
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, {}) genAssignment(p, d, a, {})
resetLoc(p, 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
of mOr, mAnd: genAndOr(p, e, d, op) of mOr, mAnd: genAndOr(p, e, d, 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

@ -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
of attachedDestructor:
let op = t.destructor let op = t.destructor
if op != nil: if op != nil:
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
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: 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:
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 # note that tfHasAsgn is propagated so we need the check on
# 'selectedGC' here to determine if we have the new runtime. # 'selectedGC' here to determine if we have the new runtime.
if c.graph.config.selectedGC == gcDestructors:
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 =

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,7 +44,8 @@ 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):
proc `=destroy`[T](s: var seq[T]) =
var x = cast[ptr NimSeqV2[T]](addr s) var x = cast[ptr NimSeqV2[T]](addr s)
var p = x.p var p = x.p
if p != nil: if p != nil:
@ -56,7 +57,7 @@ proc `=destroy`[T](s: var seq[T]) =
x.p = nil x.p = nil
x.len = 0 x.len = 0
proc `=`[T](x: var seq[T]; y: seq[T]) = proc `=`[T](x: var seq[T]; y: seq[T]) =
mixin `=destroy` mixin `=destroy`
var a = cast[ptr NimSeqV2[T]](addr x) var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y) var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
@ -73,7 +74,7 @@ proc `=`[T](x: var seq[T]; y: seq[T]) =
for i in 0..<a.len: for i in 0..<a.len:
a.p.data[i] = b.p.data[i] a.p.data[i] = b.p.data[i]
proc `=sink`[T](x: var seq[T]; y: seq[T]) = proc `=sink`[T](x: var seq[T]; y: seq[T]) =
mixin `=destroy` mixin `=destroy`
var a = cast[ptr NimSeqV2[T]](addr x) var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y) var b = cast[ptr NimSeqV2[T]](unsafeAddr y)

View file

@ -41,17 +41,19 @@ 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):
template frees(s) =
if not isLiteral(s): if not isLiteral(s):
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap)) s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
proc `=destroy`(s: var string) = proc `=destroy`(s: var string) =
var a = cast[ptr NimStringV2](addr s) var a = cast[ptr NimStringV2](addr s)
frees(a) frees(a)
a.len = 0 a.len = 0
a.p = nil a.p = nil
proc `=sink`(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)
# we hope this is optimized away for not yet alive objects: # we hope this is optimized away for not yet alive objects:
@ -60,7 +62,7 @@ proc `=sink`(x: var string, y: string) =
a.len = b.len a.len = b.len
a.p = b.p a.p = b.p
proc `=`(x: var string, y: string) = proc `=`(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 if unlikely(a.p == b.p): return

View file

@ -63,7 +63,9 @@ 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):
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
# checks if obj is of type subclass: # checks if obj is of type subclass:
var x = obj var x = obj
if x == subclass: return # optimized fast path if x == subclass: return # optimized fast path
@ -72,13 +74,13 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
sysFatal(ObjectConversionError, "invalid object conversion") sysFatal(ObjectConversionError, "invalid object conversion")
x = x.base x = x.base
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
if a != b: if a != b:
sysFatal(ObjectAssignmentError, "invalid object assignment") sysFatal(ObjectAssignmentError, "invalid object assignment")
type ObjCheckCache = array[0..1, PNimType] type ObjCheckCache = array[0..1, PNimType]
proc isObjSlowPath(obj, subclass: PNimType; proc isObjSlowPath(obj, subclass: PNimType;
cache: var ObjCheckCache): bool {.noinline.} = cache: var ObjCheckCache): bool {.noinline.} =
# checks if obj is of type subclass: # checks if obj is of type subclass:
var x = obj.base var x = obj.base
@ -90,7 +92,7 @@ proc isObjSlowPath(obj, subclass: PNimType;
cache[1] = obj cache[1] = obj
return true return true
proc isObjWithCache(obj, subclass: PNimType; proc isObjWithCache(obj, subclass: PNimType;
cache: var ObjCheckCache): bool {.compilerProc, inline.} = cache: var ObjCheckCache): bool {.compilerProc, inline.} =
if obj == subclass: return true if obj == subclass: return true
if obj.base == subclass: return true if obj.base == subclass: return true
@ -98,7 +100,7 @@ proc isObjWithCache(obj, subclass: PNimType;
if cache[1] == obj: return true if cache[1] == obj: return true
return isObjSlowPath(obj, subclass, cache) return isObjSlowPath(obj, subclass, cache)
proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
# checks if obj is of type subclass: # checks if obj is of type subclass:
var x = obj var x = obj
if x == subclass: return true # optimized fast path if x == subclass: return true # optimized fast path

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"