move moves

This commit is contained in:
Andrii Riabushenko 2018-11-29 23:33:48 +00:00
commit 4b31d7bb83
2 changed files with 161 additions and 63 deletions

View file

@ -410,7 +410,10 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
result.add tmp result.add tmp
proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
if isSink: if arg.typ == nil:
# typ is nil if we are in if/case branch with noreturn
result = copyTree(arg)
elif isSink:
if arg.kind in nkCallKinds: if arg.kind in nkCallKinds:
# recurse but skip the call expression in order to prevent # recurse but skip the call expression in order to prevent
# destructor injections: Rule 5.1 is different from rule 5.4! # destructor injections: Rule 5.1 is different from rule 5.4!
@ -420,7 +423,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
result.add arg[0] result.add arg[0]
for i in 1..<arg.len: for i in 1..<arg.len:
result.add pArg(arg[i], c, i < L and parameters[i].kind == tySink) result.add pArg(arg[i], c, i < L and parameters[i].kind == tySink)
elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}: elif arg.kind in {nkObjConstr, nkTupleConstr, nkCharLit..nkFloat128Lit}:
discard "object construction to sink parameter: nothing to do" discard "object construction to sink parameter: nothing to do"
result = arg result = arg
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c): elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
@ -431,6 +434,37 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
elif arg.kind == nkSym and isSinkParam(arg.sym): elif arg.kind == nkSym and isSinkParam(arg.sym):
# mark the sink parameter as used: # mark the sink parameter as used:
result = destructiveMoveSink(arg, c) result = destructiveMoveSink(arg, c)
elif arg.kind in {nkStmtListExpr, nkBlockExpr, nkBlockStmt}:
result = copyNode(arg)
for i in 0..arg.len-2:
result.add p(arg[i], c)
result.add pArg(arg[^1], c, isSink)
elif arg.kind in {nkIfExpr, nkIfStmt}:
result = copyNode(arg)
for i in 0..<arg.len:
var branch = copyNode(arg[i])
if arg[i].kind in {nkElifBranch, nkElifExpr}:
branch.add p(arg[i][0], c)
branch.add pArg(arg[i][1], c, isSink)
else:
branch.add pArg(arg[i][0], c, isSink)
result.add branch
elif arg.kind == nkCaseStmt:
result = copyNode(arg)
result.add p(arg[0], c)
for i in 1..<arg.len:
var branch: PNode
if arg[i].kind == nkOfbranch:
branch = arg[i] # of branch conditions are constants
branch[^1] = pArg(arg[i][^1], c, isSink)
elif arg[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(arg[i])
branch.add p(arg[i][0], c)
branch.add pArg(arg[i][1], c, isSink)
else:
branch = copyNode(arg[i])
branch.add pArg(arg[i][0], c, isSink)
result.add branch
else: else:
# an object that is not temporary but passed to a 'sink' parameter # an object that is not temporary but passed to a 'sink' parameter
# results in a copy. # results in a copy.
@ -439,64 +473,100 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
result = p(arg, c) result = p(arg, c)
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
case ri.kind if ri.typ == nil:
of nkCallKinds: # typ is nil if we are in if/case branch with noreturn
result = genSink(c, dest.typ, dest, ri) result = copyTree(ri)
# watch out and no not transform 'ri' twice if it's a call: else:
let ri2 = copyNode(ri) case ri.kind
let parameters = ri[0].typ of nkCallKinds:
let L = if parameters != nil: parameters.len else: 0
ri2.add ri[0]
for i in 1..<ri.len:
ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink)
#recurse(ri, ri2)
result.add ri2
of nkBracketExpr:
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
# unpacking of tuple: move out the elements
result = genSink(c, dest.typ, dest, ri) result = genSink(c, dest.typ, dest, ri)
else: # watch out and no not transform 'ri' twice if it's a call:
result = genCopy(c, dest.typ, dest, ri) let ri2 = copyNode(ri)
result.add p(ri, c) let parameters = ri[0].typ
of nkStmtListExpr: let L = if parameters != nil: parameters.len else: 0
result = newNodeI(nkStmtList, ri.info) ri2.add ri[0]
for i in 0..ri.len-2: for i in 1..<ri.len:
result.add p(ri[i], c) ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink)
result.add moveOrCopy(dest, ri[^1], c) #recurse(ri, ri2)
of nkObjConstr: result.add ri2
result = genSink(c, dest.typ, dest, ri) of nkBracketExpr:
let ri2 = copyTree(ri) if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
for i in 1..<ri.len: # unpacking of tuple: move out the elements
# everything that is passed to an object constructor is consumed, result = genSink(c, dest.typ, dest, ri)
# so these all act like 'sink' parameters:
ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
result.add ri2
of nkTupleConstr:
result = genSink(c, dest.typ, dest, ri)
let ri2 = copyTree(ri)
for i in 0..<ri.len:
# everything that is passed to an tuple constructor is consumed,
# so these all act like 'sink' parameters:
if ri[i].kind == nkExprColonExpr:
ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
else: else:
ri2[i] = pArg(ri[i], c, isSink = true) result = genCopy(c, dest.typ, dest, ri)
result.add ri2 result.add p(ri, c)
of nkSym: of nkStmtListExpr:
if ri.sym.kind != skParam and isLastRead(ri, c): result = newNodeI(nkStmtList, ri.info)
# Rule 3: `=sink`(x, z); wasMoved(z) for i in 0..ri.len-2:
var snk = genSink(c, dest.typ, dest, ri) result.add p(ri[i], c)
snk.add p(ri, c) result.add moveOrCopy(dest, ri[^1], c)
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) of nkBlockExpr, nkBlockStmt:
elif isSinkParam(ri.sym): result = newNodeI(nkBlockStmt, ri.info)
result.add ri[0] ## add label
for i in 1..ri.len-2:
result.add p(ri[i], c)
result.add moveOrCopy(dest, ri[^1], c)
of nkIfExpr, nkIfStmt:
result = newNodeI(nkIfStmt, ri.info)
for i in 0..<ri.len:
var branch = copyNode(ri[i])
if ri[i].kind in {nkElifBranch, nkElifExpr}:
branch.add p(ri[i][0], c)
branch.add moveOrCopy(dest, ri[i][1], c)
else:
branch.add moveOrCopy(dest, ri[i][0], c)
result.add branch
of nkCaseStmt:
result = newNodeI(nkCaseStmt, ri.info)
result.add p(ri[0], c)
for i in 1..<ri.len:
var branch: PNode
if ri[i].kind == nkOfbranch:
branch = ri[i] # of branch conditions are constants
branch[^1] = moveOrCopy(dest, ri[i][^1], c)
elif ri[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(ri[i])
branch.add p(ri[i][0], c)
branch.add moveOrCopy(dest, ri[i][1], c)
else:
branch = copyNode(ri[i])
branch.add moveOrCopy(dest, ri[i][0], c)
result.add branch
of nkObjConstr:
result = genSink(c, dest.typ, dest, ri) result = genSink(c, dest.typ, dest, ri)
result.add destructiveMoveSink(ri, c) let ri2 = copyTree(ri)
for i in 1..<ri.len:
# everything that is passed to an object constructor is consumed,
# so these all act like 'sink' parameters:
ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
result.add ri2
of nkTupleConstr:
result = genSink(c, dest.typ, dest, ri)
let ri2 = copyTree(ri)
for i in 0..<ri.len:
# everything that is passed to an tuple constructor is consumed,
# so these all act like 'sink' parameters:
if ri[i].kind == nkExprColonExpr:
ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
else:
ri2[i] = pArg(ri[i], c, isSink = true)
result.add ri2
of nkSym:
if ri.sym.kind != skParam and isLastRead(ri, c):
# Rule 3: `=sink`(x, z); wasMoved(z)
var snk = genSink(c, dest.typ, dest, ri)
snk.add p(ri, c)
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
elif isSinkParam(ri.sym):
result = genSink(c, dest.typ, dest, ri)
result.add destructiveMoveSink(ri, c)
else:
result = genCopy(c, dest.typ, dest, ri)
result.add p(ri, c)
else: else:
result = genCopy(c, dest.typ, dest, ri) result = genCopy(c, dest.typ, dest, ri)
result.add p(ri, c) result.add p(ri, c)
else:
result = genCopy(c, dest.typ, dest, ri)
result.add p(ri, c)
proc p(n: PNode; c: var Con): PNode = proc p(n: PNode; c: var Con): PNode =
case n.kind case n.kind
@ -561,7 +631,7 @@ proc p(n: PNode; c: var Con): PNode =
recurse(n, result) recurse(n, result)
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
when false: # defined(nimDebugDestroys): when true: # defined(nimDebugDestroys):
echo "injecting into ", n echo "injecting into ", n
var c: Con var c: Con
c.owner = owner c.owner = owner
@ -597,8 +667,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
else: else:
result.add body result.add body
when defined(nimDebugDestroys): when true:
if true: echo "------------------------------------"
echo "------------------------------------" echo owner.name.s, " transformed to: "
echo owner.name.s, " transformed to: " echo result
echo result

View file

@ -109,7 +109,14 @@ proc myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) =
proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] = proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] =
var cc = newMySeq(y, 5.0) var cc = newMySeq(y, 5.0)
(a: newMySeq(x, 1.0), b:0, c: cc) (a: case x:
of 1:
let (z1, z2) = myfunc(x,y)
z2
elif x > 5: raise newException(ValueError, "new error")
else: newMySeq(x, 1.0),
b:0,
c: if y > 0: move(cc) else: newMySeq(1, 3.0))
let (seq1, seq2) = myfunc(2, 3) let (seq1, seq2) = myfunc(2, 3)
doAssert seq1.len == 2 doAssert seq1.len == 2
@ -123,3 +130,25 @@ doAssert seq3[0] == 1.0
var seq4, seq5: MySeqNonCopyable var seq4, seq5: MySeqNonCopyable
(seq4, i, seq5) = myfunc2(2, 3) (seq4, i, seq5) = myfunc2(2, 3)
seq4 = block:
var tmp = newMySeq(4, 1.0)
tmp[0] = 3.0
tmp
doAssert seq4[0] == 3.0
import macros
seq4 =
if i > 0: newMySeq(2, 5.0)
elif i < -100: raise newException(ValueError, "Parse Error")
else: newMySeq(2, 3.0)
seq4 =
case (char) i:
of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
of 'B': quit(-1)
else:
let (x1, x2, x3) = myfunc2(2, 3)
x3