fixes the tcontrolflow regression, clen idea of an escaping expression

This commit is contained in:
Araq 2020-07-10 10:58:16 +02:00 • committed by Andreas Rumpf
commit 112511084d

View file

@ -40,6 +40,7 @@ type
escapingSyms: IntSet # a construct like (block: let x = f(); x) escapingSyms: IntSet # a construct like (block: let x = f(); x)
# means that 'x' escapes. We then destroy it # means that 'x' escapes. We then destroy it
# in the parent's scope (and also allocate it there). # in the parent's scope (and also allocate it there).
escapingExpr: PNode
type type
Con = object Con = object
@ -129,6 +130,9 @@ type
producesValue producesValue
proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode =
proc isComplexStmtListExpr(n: PNode): bool =
n.kind == nkStmtListExpr and (n.len == 0 or n[^1].kind != nkSym)
#if not s.needsTry: optimize(s) #if not s.needsTry: optimize(s)
assert ret != nil assert ret != nil
if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0:
@ -139,7 +143,7 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode
var r = PNode(nil) var r = PNode(nil)
if isExpr: if isExpr:
result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
if ret.kind in nkCallKinds + {nkStmtListExpr}: if ret.kind in nkCallKinds or isComplexStmtListExpr(ret):
r = c.getTemp(s, ret.typ, ret.info) r = c.getTemp(s, ret.typ, ret.info)
else: else:
result = newNodeI(nkStmtList, ret.info) result = newNodeI(nkStmtList, ret.info)
@ -167,12 +171,20 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode
result.add newTree(nkFastAsgn, r, ret[last]) result.add newTree(nkFastAsgn, r, ret[last])
else: else:
result.add newTree(nkFastAsgn, r, ret) result.add newTree(nkFastAsgn, r, ret)
for m in s.wasMoved: result.add m
for i in countdown(s.final.high, 0): result.add s.final[i]
result.add r
elif ret.kind == nkStmtListExpr:
# simplify it a bit further by merging the nkStmtListExprs
let last = ret.len - 1
for i in 0 ..< last: result.add ret[i]
for m in s.wasMoved: result.add m
for i in countdown(s.final.high, 0): result.add s.final[i]
result.add ret[last]
else: else:
result.add ret result.add ret
for m in s.wasMoved: result.add m for m in s.wasMoved: result.add m
for i in countdown(s.final.high, 0): result.add s.final[i] for i in countdown(s.final.high, 0): result.add s.final[i]
if r != nil:
result.add r
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode
@ -534,18 +546,23 @@ proc containsConstSeq(n: PNode): bool =
if containsConstSeq(son): return true if containsConstSeq(son): return true
else: discard else: discard
proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# it can happen that we need to destroy expression contructors # it can happen that we need to destroy expression contructors
# like [], (), closures explicitly in order to not leak them. # like [], (), closures explicitly in order to not leak them.
if arg.typ != nil and hasDestructor(arg.typ): if arg.typ != nil and hasDestructor(arg.typ):
# produce temp creation for (fn, env). But we need to move 'env'? # produce temp creation for (fn, env). But we need to move 'env'?
# This was already done in the sink parameter handling logic. # This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
if orig == s.escapingExpr and s.parent != nil:
let tmp = c.getTemp(s, arg.typ, arg.info) let tmp = c.getTemp(s.parent[], arg.typ, arg.info)
result.add c.genSink(s, tmp, arg, isDecl = true) result.add c.genSink(s.parent[], tmp, arg, isDecl = true)
result.add tmp result.add tmp
s.final.add c.genDestroy(tmp) s.parent[].final.add c.genDestroy(tmp)
else:
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(s, tmp, arg, isDecl = true)
result.add tmp
s.final.add c.genDestroy(tmp)
else: else:
result = arg result = arg
@ -590,28 +607,35 @@ proc cycleCheck(n: PNode; c: var Con) =
message(c.graph.config, n.info, warnCycleCreated, msg) message(c.graph.config, n.info, warnCycleCreated, msg)
break break
proc markEscapingVars(n: PNode; s: var Scope) = proc markEscapingVarsRec(n: PNode; s: var Scope) =
case n.kind case n.kind
of nkSym: of nkSym:
s.escapingSyms.incl n.sym.id s.escapingSyms.incl n.sym.id
of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref, of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr, nkStringToCString, nkCStringToString, nkObjDownConv, nkAddr, nkHiddenAddr, nkStringToCString, nkCStringToString, nkObjDownConv,
nkObjUpConv: nkObjUpConv:
markEscapingVars(n[0], s) markEscapingVarsRec(n[0], s)
of nkCast, nkHiddenSubConv, nkHiddenStdConv, nkConv: of nkCast, nkHiddenSubConv, nkHiddenStdConv, nkConv:
markEscapingVars(n[1], s) markEscapingVarsRec(n[1], s)
of nkTupleConstr, nkBracket, nkPar, nkClosure: of nkTupleConstr, nkBracket, nkPar, nkClosure:
for i in 0 ..< n.len: for i in 0 ..< n.len:
markEscapingVars(n[i], s) markEscapingVarsRec(n[i], s)
of nkObjConstr: of nkObjConstr:
for i in 1 ..< n.len: for i in 1 ..< n.len:
markEscapingVars(n[i], s) markEscapingVarsRec(n[i], s)
of nkStmtList, nkStmtListExpr: of nkStmtList, nkStmtListExpr:
if n.len > 0: if n.len > 0:
markEscapingVars(n[^1], s) markEscapingVarsRec(n[^1], s)
else: else:
discard "no arbitrary tree traversal here" discard "no arbitrary tree traversal here"
proc markEscapingVars(n: PNode; s: var Scope) =
markEscapingVarsRec(n, s)
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
it = it[^1]
s.escapingExpr = it
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) =
# move the variable declaration to the top of the frame: # move the variable declaration to the top of the frame:
let owningScope = c.addTopVar(s, v) let owningScope = c.addTopVar(s, v)
@ -820,7 +844,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
else: else:
result[i] = p(n[i], c, s, m) result[i] = p(n[i], c, s, m)
if mode == normal and isRefConstr: if mode == normal and isRefConstr:
result = ensureDestruction(result, c, s) result = ensureDestruction(result, n, c, s)
of nkCallKinds: of nkCallKinds:
let inSpawn = c.inSpawn let inSpawn = c.inSpawn
if n[0].kind == nkSym and n[0].sym.magic == mSpawn: if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
@ -859,7 +883,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
result[0] = p(n[0], c, s, normal) result[0] = p(n[0], c, s, normal)
if canRaise(n[0]): s.needsTry = true if canRaise(n[0]): s.needsTry = true
if mode == normal: if mode == normal:
result = ensureDestruction(result, c, s) result = ensureDestruction(result, n, c, s)
of nkDiscardStmt: # Small optimization of nkDiscardStmt: # Small optimization
result = shallowCopy(n) result = shallowCopy(n)
if n[0].kind != nkEmpty: if n[0].kind != nkEmpty:
@ -927,7 +951,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
result[i] = p(n[i], c, s, normal) result[i] = p(n[i], c, s, normal)
if n.typ != nil and hasDestructor(n.typ): if n.typ != nil and hasDestructor(n.typ):
if mode == normal: if mode == normal:
result = ensureDestruction(result, c, s) result = ensureDestruction(result, n, c, s)
of nkHiddenSubConv, nkHiddenStdConv, nkConv: of nkHiddenSubConv, nkHiddenStdConv, nkConv:
# we have an "ownership invariance" for all constructors C(x). # we have an "ownership invariance" for all constructors C(x).