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:
ringabout
2023-05-07 03:36:57 +08:00
committed by GitHub
parent ba698d37c3
commit b562e1e6d8
12 changed files with 170 additions and 17 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -154,5 +154,6 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasGenericDefine")
defineSymbol("nimHasDefineAliases")
defineSymbol("nimHasWarnBareExcept")
defineSymbol("nimHasDup")
defineSymbol("nimHasChecksums")

View File

@@ -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; " &

View File

@@ -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]);

View File

@@ -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:

View File

@@ -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,

View File

@@ -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:

View File

@@ -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):

View File

@@ -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
View 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()

View File

@@ -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)