mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-04 12:07:51 +00:00
implement =dup hook eliminating wasMoved and =copy pairs (#21586)
* import `=dup` hook eliminating `wasMoved` and `=copy` pairs * add dup * add a test for dup * fixes documentation * fixes signature * resolve comments * fixes tests * fixes tests * clean up
This commit is contained in:
@@ -687,7 +687,7 @@ type
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mMove, mWasMoved, mDestroy, mTrace,
|
||||
mMove, mWasMoved, mDup, mDestroy, mTrace,
|
||||
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
@@ -944,7 +944,8 @@ type
|
||||
attachedAsgn,
|
||||
attachedSink,
|
||||
attachedTrace,
|
||||
attachedDeepCopy
|
||||
attachedDeepCopy,
|
||||
attachedDup
|
||||
|
||||
TType* {.acyclic.} = object of TIdObj # \
|
||||
# types are identical iff they have the
|
||||
@@ -1515,7 +1516,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||
|
||||
const
|
||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
|
||||
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy", "=dup"]
|
||||
|
||||
proc `$`*(s: PSym): string =
|
||||
if s != nil:
|
||||
|
||||
@@ -2346,6 +2346,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
|
||||
proc genDup(p: BProc; src: TLoc; d: var TLoc; n: PNode) =
|
||||
if d.k == locNone: getTemp(p, n.typ, d)
|
||||
linefmt(p, cpsStmts, "#nimDupRef((void**)$1, (void*)$2);$n",
|
||||
[addrLoc(p.config, d), rdLoc(src)])
|
||||
|
||||
proc genDestroy(p: BProc; n: PNode) =
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
let arg = n[1].skipAddr
|
||||
@@ -2597,6 +2602,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mAccessTypeField: genAccessTypeField(p, e, d)
|
||||
of mSlice: genSlice(p, e, d)
|
||||
of mTrace: discard "no code to generate"
|
||||
of mDup:
|
||||
var a: TLoc
|
||||
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
|
||||
initLocExpr(p, x, a)
|
||||
genDup(p, a, d, e)
|
||||
else:
|
||||
when defined(debugMagics):
|
||||
echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
|
||||
|
||||
@@ -154,5 +154,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasGenericDefine")
|
||||
defineSymbol("nimHasDefineAliases")
|
||||
defineSymbol("nimHasWarnBareExcept")
|
||||
|
||||
defineSymbol("nimHasDup")
|
||||
defineSymbol("nimHasChecksums")
|
||||
|
||||
|
||||
@@ -425,11 +425,28 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info)
|
||||
if hasDestructor(c, n.typ):
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil:
|
||||
let src = p(n, c, s, normal)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
genOp(c, op, src)
|
||||
)
|
||||
elif typ.kind == tyRef:
|
||||
let src = p(n, c, s, normal)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(createMagic(c.graph, c.idgen, "`=dup`", mDup)),
|
||||
src)
|
||||
)
|
||||
else:
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
("passing '$1' to a sink parameter introduces an implicit copy; " &
|
||||
|
||||
@@ -2174,6 +2174,13 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
|
||||
genReset(p, n)
|
||||
#lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
|
||||
|
||||
proc genDup(p: PProc; n: PNode; r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.kind = resVal
|
||||
r.res = p.getTemp()
|
||||
gen(p, n[1], a)
|
||||
lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
|
||||
|
||||
proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.res = rope("[")
|
||||
@@ -2368,6 +2375,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.kind = resExpr
|
||||
of mMove:
|
||||
genMove(p, n, r)
|
||||
of mDup:
|
||||
genDup(p, n, r)
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
#else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
|
||||
|
||||
@@ -463,6 +463,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
body.add genWasMovedCall(c, op, x)
|
||||
result = true
|
||||
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = getSysType(c.g, body.info, tyInt)
|
||||
@@ -546,6 +549,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
@@ -584,6 +589,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -600,6 +607,8 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc cyclicType*(t: PType): bool =
|
||||
case t.kind
|
||||
@@ -699,7 +708,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
||||
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
@@ -749,6 +759,8 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedTrace:
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -774,6 +786,8 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
@@ -800,6 +814,8 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind == attachedDeepCopy:
|
||||
@@ -835,6 +851,8 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
@@ -851,6 +869,8 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
assert false, "cannot happen"
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
@@ -966,7 +986,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
|
||||
result.typ = newProcType(info, nextTypeId(idgen), owner)
|
||||
result.typ.addParam dest
|
||||
if kind notin {attachedDestructor, attachedWasMoved}:
|
||||
if kind notin {attachedDestructor, attachedWasMoved, attachedDup}:
|
||||
result.typ.addParam src
|
||||
|
||||
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
||||
@@ -1006,7 +1026,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
|
||||
let dest = result.typ.n[1].sym
|
||||
let d = newDeref(newSymNode(dest))
|
||||
let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
let src = if kind in {attachedDestructor, attachedWasMoved, attachedDup}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
else: newSymNode(result.typ.n[2].sym)
|
||||
|
||||
# register this operation already:
|
||||
|
||||
@@ -1815,9 +1815,12 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = if op in {attachedDestructor, attachedWasMoved}:
|
||||
let cond = case op
|
||||
of {attachedDestructor, attachedWasMoved}:
|
||||
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||
elif op == attachedTrace:
|
||||
of attachedDup:
|
||||
t.len == 2 and t[0] != nil and t[1].kind == tyVar
|
||||
of attachedTrace:
|
||||
t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer
|
||||
else:
|
||||
t.len >= 2 and t[0] == nil
|
||||
@@ -1843,9 +1846,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
if not noError and sfSystemModule notin s.owner.flags:
|
||||
if op == attachedTrace:
|
||||
case op
|
||||
of attachedTrace:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
|
||||
of attachedDup:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=dup' must be proc[T: object](x: var T): T")
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||
@@ -1938,6 +1945,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
of "=wasmoved":
|
||||
if s.magic != mWasMoved:
|
||||
bindTypeHook(c, s, n, attachedWasMoved)
|
||||
of "=dup":
|
||||
if s.magic != mDup:
|
||||
bindTypeHook(c, s, n, attachedDup)
|
||||
else:
|
||||
if sfOverriden in s.flags:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
|
||||
@@ -1401,6 +1401,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
# c.gABx(n, opcNodeToReg, a, a)
|
||||
# c.genAsgnPatch(arg, a)
|
||||
c.freeTemp(a)
|
||||
of mDup:
|
||||
let arg = n[1]
|
||||
let a = c.genx(arg)
|
||||
if dest < 0: dest = c.getTemp(arg.typ)
|
||||
gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a)
|
||||
c.freeTemp(a)
|
||||
of mNodeId:
|
||||
c.genUnaryABC(n, dest, opcNodeId)
|
||||
else:
|
||||
|
||||
@@ -347,6 +347,12 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I;
|
||||
proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
|
||||
## Generic `destructor`:idx: implementation that can be overridden.
|
||||
discard
|
||||
|
||||
when defined(nimHasDup):
|
||||
proc `=dup`*[T](x: ref T): ref T {.inline, magic: "Dup".} =
|
||||
## Generic `dup` implementation that can be overridden.
|
||||
discard
|
||||
|
||||
proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} =
|
||||
## Generic `sink`:idx: implementation that can be overridden.
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
|
||||
@@ -185,6 +185,10 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
|
||||
when traceCollector:
|
||||
cprintf("[DeCREF] %p\n", cell)
|
||||
|
||||
proc nimDupRef(dest: ptr pointer, src: pointer) {.compilerRtl, inl.} =
|
||||
dest[] = src
|
||||
if src != nil: nimIncRef src
|
||||
|
||||
proc GC_unref*[T](x: ref T) =
|
||||
## New runtime only supports this operation for 'ref T'.
|
||||
var y {.cursor.} = x
|
||||
|
||||
70
tests/arc/tdup.nim
Normal file
70
tests/arc/tdup.nim
Normal file
@@ -0,0 +1,70 @@
|
||||
discard """
|
||||
cmd: "nim c --mm:arc --expandArc:foo --hints:off $file"
|
||||
nimout: '''
|
||||
--expandArc: foo
|
||||
|
||||
var
|
||||
x
|
||||
:tmpD
|
||||
s
|
||||
:tmpD_1
|
||||
x = Ref(id: 8)
|
||||
inc:
|
||||
:tmpD = `=dup`(x)
|
||||
:tmpD
|
||||
inc:
|
||||
let blitTmp = x
|
||||
blitTmp
|
||||
var id_1 = 777
|
||||
s = RefCustom(id_2: addr(id_1))
|
||||
inc_1 :
|
||||
:tmpD_1 = `=dup`(s)
|
||||
:tmpD_1
|
||||
inc_1 :
|
||||
let blitTmp_1 = s
|
||||
blitTmp_1
|
||||
-- end of expandArc ------------------------
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
Ref = ref object
|
||||
id: int
|
||||
|
||||
RefCustom = object
|
||||
id: ptr int
|
||||
|
||||
proc inc(x: sink Ref) =
|
||||
inc x.id
|
||||
|
||||
proc inc(x: sink RefCustom) =
|
||||
inc x.id[]
|
||||
|
||||
proc `=dup`(x: var RefCustom): RefCustom =
|
||||
result.id = x.id
|
||||
|
||||
proc foo =
|
||||
var x = Ref(id: 8)
|
||||
inc(x)
|
||||
inc(x)
|
||||
var id = 777
|
||||
var s = RefCustom(id: addr id)
|
||||
inc s
|
||||
inc s
|
||||
|
||||
foo()
|
||||
|
||||
proc foo2 =
|
||||
var x = Ref(id: 8)
|
||||
inc(x)
|
||||
doAssert x.id == 9
|
||||
inc(x)
|
||||
doAssert x.id == 10
|
||||
var id = 777
|
||||
var s = RefCustom(id: addr id)
|
||||
inc s
|
||||
doAssert s.id[] == 778
|
||||
inc s
|
||||
doAssert s.id[] == 779
|
||||
|
||||
foo2()
|
||||
@@ -113,8 +113,7 @@ block :tmp:
|
||||
var :tmpD
|
||||
sym = shadowScope.symbols[i]
|
||||
addInterfaceDecl(c):
|
||||
`=wasMoved`(:tmpD)
|
||||
`=copy_1`(:tmpD, sym)
|
||||
:tmpD = `=dup`(sym)
|
||||
:tmpD
|
||||
inc(i, 1)
|
||||
`=destroy`(shadowScope)
|
||||
|
||||
Reference in New Issue
Block a user