diff options
-rw-r--r-- | compiler/ccgexprs.nim | 165 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 8 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 70 | ||||
-rw-r--r-- | compiler/cgen.nim | 2 | ||||
-rw-r--r-- | compiler/commands.nim | 2 | ||||
-rw-r--r-- | compiler/liftdestructors.nim | 253 | ||||
-rw-r--r-- | compiler/semtypes.nim | 2 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 2 | ||||
-rw-r--r-- | lib/core/runtime_v2.nim | 78 | ||||
-rw-r--r-- | lib/core/seqs.nim | 69 | ||||
-rw-r--r-- | lib/core/strs.nim | 74 | ||||
-rw-r--r-- | lib/system/chcks.nim | 88 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 |
13 files changed, 574 insertions, 241 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 406528845..e080ca746 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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)] - - 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) + sizeExpr = "sizeof($1)" % [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) + 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: - 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: + 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) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6dc10db3d..e2790f4f6 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -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: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 063c02df9..ed051170f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 4a10a4d2b..a7c1895f6 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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 diff --git a/compiler/commands.nim b/compiler/commands.nim index 404be3df3..ee16702a2 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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} diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 6b36dd6fb..aea244a78 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -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 = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0fabd5531..52b236883 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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 diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index e3380c0e3..152a75ee3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -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: diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim new file mode 100644 index 000000000..8a9068c37 --- /dev/null +++ b/lib/core/runtime_v2.nim @@ -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) diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index fd46b96eb..08c10962b 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -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 - -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] + var a = cast[ptr NimSeqV2[T]](addr x) + var b = cast[ptr NimSeqV2[T]](unsafeAddr y) -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 diff --git a/lib/core/strs.nim b/lib/core/strs.nim index 406efe5a1..540765de3 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -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)) - -proc `=destroy`(s: var string) = - var a = cast[ptr NimStringV2](addr s) - frees(a) - a.len = 0 - a.p = nil - -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 `=`(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: +when not defined(nimV2): + + template frees(s) = + if not isLiteral(s): + s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap)) + + proc `=destroy`(s: var string) = + var a = cast[ptr NimStringV2](addr s) + frees(a) + a.len = 0 + a.p = nil + + 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 diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 8821e32f8..d108e09f1 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -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 - -proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = - if a != b: - sysFatal(ObjectAssignmentError, "invalid object assignment") - -type ObjCheckCache = array[0..1, PNimType] - -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 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 +when not defined(nimV2): + + 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 + + proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = + if a != b: + sysFatal(ObjectAssignmentError, "invalid object assignment") + + type ObjCheckCache = array[0..1, PNimType] + + 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 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 diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 9284f07d2..a2cddc472 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -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" |