mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-07 12:24:19 +00:00
arc optimizations (#13325)
* scope based destructors * handle 'or' and 'and' expressions properly, see the new test arc/tcontrolflow.nim * make this branch mergable, logic is disabled for now
This commit is contained in:
@@ -2573,6 +2573,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
else:
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of skTemp:
|
||||
if sym.loc.r == nil:
|
||||
# we now support undeclared 'skTemp' variables for easier
|
||||
# transformations in other parts of the compiler:
|
||||
assignLocalVar(p, n)
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||
#echo renderTree(p.prc.ast, {renderIds})
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements code generation for multi methods.
|
||||
## This module implements code generation for methods.
|
||||
|
||||
import
|
||||
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
||||
|
||||
@@ -154,8 +154,8 @@ type
|
||||
# is their finally. For finally it is parent finally. Otherwise -1
|
||||
|
||||
const
|
||||
nkSkip = { nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt } + procDefs
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt} + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
|
||||
## See doc/destructors.rst for a spec of the implemented rewrite rules
|
||||
|
||||
## XXX Optimization to implement: if a local variable is only assigned
|
||||
## string literals as in ``let x = conf: "foo" else: "bar"`` do not
|
||||
## produce a destructor call for ``x``. The address of ``x`` must also
|
||||
## not have been taken. ``x = "abc"; x.add(...)``
|
||||
|
||||
# Todo:
|
||||
# - eliminate 'wasMoved(x); destroy(x)' pairs as a post processing step.
|
||||
|
||||
import
|
||||
intsets, ast, msgs, renderer, magicsys, types, idents,
|
||||
@@ -20,6 +27,10 @@ import
|
||||
lineinfos, parampatterns, sighashes
|
||||
|
||||
from trees import exprStructuralEquivalent
|
||||
from algorithm import reverse
|
||||
|
||||
const
|
||||
scopeBasedDestruction = false
|
||||
|
||||
type
|
||||
Con = object
|
||||
@@ -27,10 +38,13 @@ type
|
||||
g: ControlFlowGraph
|
||||
jumpTargets: IntSet
|
||||
destroys, topLevelVars: PNode
|
||||
scopeDestroys: seq[PNode] # used as a stack that pop from
|
||||
# at strategic places which try to
|
||||
# mimic the natural scope.
|
||||
graph: ModuleGraph
|
||||
emptyNode: PNode
|
||||
otherRead: PNode
|
||||
inLoop: int
|
||||
inLoop, hasUnstructuredCf, inDangerousBranch: int
|
||||
declaredVars: IntSet # variables we already moved to the top level
|
||||
uninit: IntSet # set of uninit'ed vars
|
||||
uninitComputed: bool
|
||||
@@ -285,7 +299,6 @@ proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info)
|
||||
sym.typ = typ
|
||||
result = newSymNode(sym)
|
||||
c.addTopVar(result)
|
||||
|
||||
proc genWasMoved(n: PNode; c: var Con): PNode =
|
||||
result = newNodeI(nkCall, n.info)
|
||||
@@ -339,10 +352,10 @@ proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':'
|
||||
proc passCopyToSink(n: PNode; c: var Con): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let tmp = getTemp(c, n.typ, n.info)
|
||||
# XXX This is only required if we are in a loop. Since we move temporaries
|
||||
# out of loops we need to mark it as 'wasMoved'.
|
||||
result.add genWasMoved(tmp, c)
|
||||
when not scopeBasedDestruction:
|
||||
c.addTopVar(tmp)
|
||||
if hasDestructor(n.typ):
|
||||
result.add genWasMoved(tmp, c)
|
||||
var m = genCopy(c, tmp, n)
|
||||
m.add p(n, c, normal)
|
||||
result.add m
|
||||
@@ -354,6 +367,8 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
|
||||
if c.graph.config.selectedGC in {gcArc, gcOrc}:
|
||||
assert(not containsGarbageCollectedRef(n.typ))
|
||||
result.add newTree(nkAsgn, tmp, p(n, c, normal))
|
||||
# Since we know somebody will take over the produced copy, there is
|
||||
# no need to destroy it.
|
||||
result.add tmp
|
||||
|
||||
proc isDangerousSeq(t: PType): bool {.inline.} =
|
||||
@@ -375,60 +390,137 @@ proc containsConstSeq(n: PNode): bool =
|
||||
if containsConstSeq(son): return true
|
||||
else: discard
|
||||
|
||||
template handleNested(n: untyped, processCall: untyped) =
|
||||
proc handleTmpDestroys(c: var Con; body: PNode; t: PType;
|
||||
oldHasUnstructuredCf, oldTmpDestroysLen: int) =
|
||||
if c.hasUnstructuredCf == oldHasUnstructuredCf:
|
||||
# no need for a try-finally statement:
|
||||
if body.kind == nkStmtList:
|
||||
for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen):
|
||||
body.add c.scopeDestroys[i]
|
||||
elif isEmptyType(t):
|
||||
var n = newNodeI(nkStmtList, body.info)
|
||||
n.add body[^1]
|
||||
for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen):
|
||||
n.add c.scopeDestroys[i]
|
||||
body[^1] = n
|
||||
elif body.kind == nkStmtListExpr and body.len > 0 and body[^1].kind == nkSym:
|
||||
# special case: Do not translate (x; y; sym) into
|
||||
# (x; y; tmp = sym; destroy(x); destroy(y); tmp )
|
||||
# but into
|
||||
# (x; y; destroy(x); destroy(y); sym )
|
||||
let sym = body[^1]
|
||||
body[^1] = c.scopeDestroys[^1]
|
||||
for i in countdown(c.scopeDestroys.high - 1, oldTmpDestroysLen):
|
||||
body.add c.scopeDestroys[i]
|
||||
body.add sym
|
||||
else:
|
||||
# fun ahead: We have to transform (x; y; E()) into
|
||||
# (x; y; tmp = E(); destroy(x); destroy(y); tmp )
|
||||
let t2 = body[^1].typ
|
||||
let tmp = getTemp(c, t2, body.info)
|
||||
when not scopeBasedDestruction:
|
||||
c.addTopVar(tmp)
|
||||
# the tmp does not have to be initialized
|
||||
var n = newNodeIT(nkStmtListExpr, body.info, t2)
|
||||
n.add newTree(nkFastAsgn, tmp, body[^1])
|
||||
for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen):
|
||||
n.add c.scopeDestroys[i]
|
||||
n.add tmp
|
||||
body[^1] = n
|
||||
#c.scopeDestroys.add genDestroy(c, tmp)
|
||||
else:
|
||||
# unstructured control flow was used, use a 'try finally' to ensure
|
||||
# destruction:
|
||||
if isEmptyType(t):
|
||||
var n = newNodeI(nkStmtList, body.info)
|
||||
for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen):
|
||||
n.add c.scopeDestroys[i]
|
||||
body[^1] = newTryFinally(body[^1], n)
|
||||
else:
|
||||
# fun ahead: We have to transform (x; y; E()) into
|
||||
# ((try: tmp = (x; y; E()); finally: destroy(x); destroy(y)); tmp )
|
||||
let t2 = body[^1].typ
|
||||
let tmp = getTemp(c, t2, body.info)
|
||||
when not scopeBasedDestruction:
|
||||
c.addTopVar(tmp)
|
||||
# the tmp does not have to be initialized
|
||||
var fin = newNodeI(nkStmtList, body.info)
|
||||
for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen):
|
||||
fin.add c.scopeDestroys[i]
|
||||
var n = newNodeIT(nkStmtListExpr, body.info, t2)
|
||||
n.add newTryFinally(newTree(nkFastAsgn, tmp, body[^1]), fin)
|
||||
n.add tmp
|
||||
body[^1] = n
|
||||
#c.scopeDestroys.add genDestroy(c, tmp)
|
||||
|
||||
c.scopeDestroys.setLen oldTmpDestroysLen
|
||||
|
||||
proc handleNested(n, dest: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
template processCall(node: PNode): PNode =
|
||||
if node.typ == nil or dest == nil:
|
||||
p(node, c, mode)
|
||||
else:
|
||||
moveOrCopy(dest, node, c)
|
||||
|
||||
proc handleScope(n, dest: PNode; t: PType;
|
||||
takeOver: Natural; c: var Con; mode: ProcessMode): PNode =
|
||||
let oldHasUnstructuredCf = c.hasUnstructuredCf
|
||||
let oldTmpDestroysLen = c.scopeDestroys.len
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<takeOver:
|
||||
result[i] = n[i]
|
||||
let last = n.len - 1
|
||||
for i in takeOver..<last:
|
||||
result[i] = p(n[i], c, normal)
|
||||
|
||||
# if we have an expression producing a temporary, we must
|
||||
# not destroy it too early:
|
||||
if isEmptyType(t):
|
||||
result[last] = processCall(n[last])
|
||||
if c.scopeDestroys.len > oldTmpDestroysLen:
|
||||
handleTmpDestroys(c, result, t, oldHasUnstructuredCf, oldTmpDestroysLen)
|
||||
else:
|
||||
setLen(result.sons, last)
|
||||
if c.scopeDestroys.len > oldTmpDestroysLen:
|
||||
handleTmpDestroys(c, result, t, oldHasUnstructuredCf, oldTmpDestroysLen)
|
||||
if result.kind != nkFinally:
|
||||
result.add processCall(n[last])
|
||||
else:
|
||||
result = newTree(nkStmtListExpr, result, processCall(n[last]))
|
||||
result.typ = t
|
||||
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
if n.len == 0: return n
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len-1:
|
||||
result.add p(n[i], c, normal)
|
||||
template node: untyped = n[^1]
|
||||
result.add processCall
|
||||
result = shallowCopy(n)
|
||||
let last = n.len - 1
|
||||
for i in 0..<last:
|
||||
result[i] = p(n[i], c, normal)
|
||||
result[last] = processCall(n[last])
|
||||
# A statement list does not introduce a scope, the AST can
|
||||
# contain silly nested statement lists.
|
||||
#result = handleScope(n, dest, n.typ, 0, c, mode)
|
||||
of nkBlockStmt, nkBlockExpr:
|
||||
result = copyNode(n)
|
||||
result.add n[0]
|
||||
template node: untyped = n[1]
|
||||
result.add processCall
|
||||
result = handleScope(n, dest, n.typ, 1, c, mode)
|
||||
of nkIfStmt, nkIfExpr:
|
||||
result = copyNode(n)
|
||||
for son in n:
|
||||
var branch = copyNode(son)
|
||||
if son.kind in {nkElifBranch, nkElifExpr}:
|
||||
template node: untyped = son[1]
|
||||
branch.add p(son[0], c, normal) #The condition
|
||||
branch.add if node.typ == nil: p(node, c, normal) #noreturn
|
||||
else: processCall
|
||||
else:
|
||||
template node: untyped = son[0]
|
||||
branch.add if node.typ == nil: p(node, c, normal) #noreturn
|
||||
else: processCall
|
||||
result.add branch
|
||||
result.add handleScope(son, dest, son[^1].typ, 0, c, mode)
|
||||
of nkCaseStmt:
|
||||
result = copyNode(n)
|
||||
result.add p(n[0], c, normal)
|
||||
for i in 1..<n.len:
|
||||
var branch: PNode
|
||||
if n[i].kind == nkOfBranch:
|
||||
branch = n[i] # of branch conditions are constants
|
||||
template node: untyped = n[i][^1]
|
||||
branch[^1] = if node.typ == nil: p(node, c, normal) #noreturn
|
||||
else: processCall
|
||||
elif n[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
branch = copyNode(n[i])
|
||||
branch.add p(n[i][0], c, normal) #The condition
|
||||
template node: untyped = n[i][1]
|
||||
branch.add if node.typ == nil: p(node, c, normal) #noreturn
|
||||
else: processCall
|
||||
else:
|
||||
branch = copyNode(n[i])
|
||||
template node: untyped = n[i][0]
|
||||
branch.add if node.typ == nil: p(node, c, normal) #noreturn
|
||||
else: processCall
|
||||
result.add branch
|
||||
result.add handleScope(n[i], dest, n[i][^1].typ, n[i].len - 1, c, mode)
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
result = copyTree(n)
|
||||
template node: untyped = n[1][0]
|
||||
result[1][0] = processCall
|
||||
result[1][0] = handleScope(n[1][0], dest, n[1][0][^1].typ, 0, c, mode)
|
||||
of nkWhileStmt:
|
||||
#result = copyNode(n)
|
||||
inc c.inLoop
|
||||
result = handleScope(n, dest, nil, 0, c, mode)
|
||||
#result.add p(n[0], c, normal)
|
||||
#result.add p(n[1], c, normal)
|
||||
dec c.inLoop
|
||||
else: assert(false)
|
||||
|
||||
proc ensureDestruction(arg: PNode; c: var Con): PNode =
|
||||
@@ -439,9 +531,23 @@ proc ensureDestruction(arg: PNode; c: var Con): PNode =
|
||||
# This was already done in the sink parameter handling logic.
|
||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||
let tmp = getTemp(c, arg.typ, arg.info)
|
||||
result.add genSink(c, tmp, arg)
|
||||
result.add tmp
|
||||
c.destroys.add genDestroy(c, tmp)
|
||||
when not scopeBasedDestruction:
|
||||
c.addTopVar(tmp)
|
||||
result.add genSink(c, tmp, arg)
|
||||
result.add tmp
|
||||
c.destroys.add genDestroy(c, tmp)
|
||||
else:
|
||||
# if we're inside a dangerous 'or' or 'and' expression, we
|
||||
# do need to initialize it. 'elif' is not among this problem
|
||||
# as we have a separate scope for 'elif' to attach the destructors to.
|
||||
if c.inDangerousBranch == 0 and c.hasUnstructuredCf == 0:
|
||||
tmp.sym.flags.incl sfNoInit
|
||||
c.addTopVar(tmp)
|
||||
# since we do not initialize these temporaries anymore, we
|
||||
# use raw assignments instead of =sink:
|
||||
result.add newTree(nkFastAsgn, tmp, arg)
|
||||
result.add tmp
|
||||
c.scopeDestroys.add genDestroy(c, tmp)
|
||||
else:
|
||||
result = arg
|
||||
|
||||
@@ -486,10 +592,49 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
message(c.graph.config, n.info, warnCycleCreated, msg)
|
||||
break
|
||||
|
||||
proc pVarTopLevel(v: PNode; c: var Con; ri, res: PNode) =
|
||||
# move the variable declaration to the top of the frame:
|
||||
if not containsOrIncl(c.declaredVars, v.sym.id):
|
||||
c.addTopVar v
|
||||
if isUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
elif sfThread notin v.sym.flags:
|
||||
# do not destroy thread vars for now at all for consistency.
|
||||
c.destroys.add genDestroy(c, v)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c)
|
||||
elif ri.kind != nkEmpty:
|
||||
res.add moveOrCopy(v, ri, c)
|
||||
|
||||
proc pVarScoped(v: PNode; c: var Con; ri, res: PNode) =
|
||||
if not containsOrIncl(c.declaredVars, v.sym.id):
|
||||
c.addTopVar(v)
|
||||
if isUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
elif {sfGlobal, sfThread} * v.sym.flags == {sfGlobal}:
|
||||
c.destroys.add genDestroy(c, v)
|
||||
else:
|
||||
# We always translate 'var v = f()' into bitcopies. If 'v' is in a loop,
|
||||
# the destruction at the loop end will free the resources. Other assignments
|
||||
# will destroy the old value inside 'v'. If we have 'var v' without an initial
|
||||
# default value we translate it into 'var v = default()'. We translate
|
||||
# 'var x = someGlobal' into 'var v = default(); `=`(v, someGlobal). The
|
||||
# lack of copy constructors is really beginning to hurt us. :-(
|
||||
#if c.inDangerousBranch == 0: v.sym.flags.incl sfNoInit
|
||||
c.scopeDestroys.add genDestroy(c, v)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c)
|
||||
elif ri.kind != nkEmpty:
|
||||
res.add moveOrCopy(v, ri, c)
|
||||
|
||||
proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||
nkIfExpr, nkCaseStmt, nkWhen}:
|
||||
handleNested(n): p(node, c, mode)
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt}:
|
||||
result = handleNested(n, nil, c, mode)
|
||||
elif mode == sinkArg:
|
||||
if n.containsConstSeq:
|
||||
# const sequences are not mutable and so we need to pass a copy to the
|
||||
@@ -522,6 +667,9 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(n)
|
||||
result[0] = p(n[0], c, sinkArg)
|
||||
elif n.typ == nil:
|
||||
# 'raise X' can be part of a 'case' expression. Deal with it here:
|
||||
result = p(n, c, normal)
|
||||
else:
|
||||
# copy objects that are not temporary but passed to a 'sink' parameter
|
||||
result = passCopyToSink(n, c)
|
||||
@@ -554,12 +702,22 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
of nkCallKinds:
|
||||
let parameters = n[0].typ
|
||||
let L = if parameters != nil: parameters.len else: 0
|
||||
|
||||
var isDangerous = false
|
||||
if n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
|
||||
inc c.inDangerousBranch
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isSinkTypeForParam(parameters[i]):
|
||||
result[i] = p(n[i], c, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, normal)
|
||||
|
||||
if isDangerous:
|
||||
dec c.inDangerousBranch
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
||||
result[0] = copyTree(n[0])
|
||||
if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
|
||||
@@ -567,7 +725,8 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
result = newTree(nkStmtList, destroyOld, result)
|
||||
else:
|
||||
result[0] = p(n[0], c, normal)
|
||||
|
||||
when scopeBasedDestruction:
|
||||
if canRaise(n[0]): inc c.hasUnstructuredCf
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, c)
|
||||
of nkDiscardStmt: # Small optimization
|
||||
@@ -589,20 +748,15 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
let v = it[j]
|
||||
if v.kind == nkSym:
|
||||
if sfCompileTime in v.sym.flags: continue
|
||||
# move the variable declaration to the top of the frame:
|
||||
if not containsOrIncl(c.declaredVars, v.sym.id):
|
||||
c.addTopVar v
|
||||
# make sure it's destroyed at the end of the proc:
|
||||
if not isUnpackedTuple(v) and sfThread notin v.sym.flags:
|
||||
# do not destroy thread vars for now at all for consistency.
|
||||
c.destroys.add genDestroy(c, v)
|
||||
elif c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
result.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c)
|
||||
when not scopeBasedDestruction:
|
||||
pVarTopLevel(v, c, ri, result)
|
||||
else:
|
||||
pVarScoped(v, c, ri, result)
|
||||
else:
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c)
|
||||
else: # keep the var but transform 'ri':
|
||||
var v = copyNode(n)
|
||||
var itCopy = copyNode(it)
|
||||
@@ -634,6 +788,7 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
result.add call
|
||||
else:
|
||||
let tmp = getTemp(c, n[0].typ, n.info)
|
||||
c.addTopVar(tmp)
|
||||
var m = genCopyNoCheck(c, tmp, n[0])
|
||||
m.add p(n[0], c, normal)
|
||||
result = newTree(nkStmtList, genWasMoved(tmp, c), m)
|
||||
@@ -648,17 +803,22 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
result.add p(n[0], c, sinkArg)
|
||||
else:
|
||||
result.add copyNode(n[0])
|
||||
inc c.hasUnstructuredCf
|
||||
of nkWhileStmt:
|
||||
result = copyNode(n)
|
||||
inc c.inLoop
|
||||
result.add p(n[0], c, normal)
|
||||
result.add p(n[1], c, normal)
|
||||
dec c.inLoop
|
||||
result = handleNested(n, nil, c, mode)
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState:
|
||||
result = n
|
||||
of nkBreakStmt:
|
||||
inc c.hasUnstructuredCf
|
||||
result = n
|
||||
of nkReturnStmt:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, mode)
|
||||
inc c.hasUnstructuredCf
|
||||
else:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
@@ -721,7 +881,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
else:
|
||||
result = genSink(c, dest, p(ri, c, sinkArg))
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt:
|
||||
handleNested(ri): moveOrCopy(dest, node, c)
|
||||
result = handleNested(ri, dest, c, normal)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
canBeMoved(c, dest.typ):
|
||||
@@ -765,10 +925,6 @@ proc extractDestroysForTemporaries(c: Con, destroys: PNode): PNode =
|
||||
result.add destroys[i]
|
||||
destroys[i] = c.emptyNode
|
||||
|
||||
proc reverseDestroys(destroys: seq[PNode]): seq[PNode] =
|
||||
for i in countdown(destroys.len - 1, 0):
|
||||
result.add destroys[i]
|
||||
|
||||
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
|
||||
return n
|
||||
@@ -801,13 +957,16 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
if c.topLevelVars.len > 0:
|
||||
result.add c.topLevelVars
|
||||
if c.destroys.len > 0:
|
||||
c.destroys.sons = reverseDestroys(c.destroys.sons)
|
||||
if c.destroys.len > 0 or c.scopeDestroys.len > 0:
|
||||
reverse c.destroys.sons
|
||||
var fin: PNode
|
||||
if owner.kind == skModule:
|
||||
result.add newTryFinally(body, extractDestroysForTemporaries(c, c.destroys))
|
||||
fin = newTryFinally(body, extractDestroysForTemporaries(c, c.destroys))
|
||||
g.globalDestructors.add c.destroys
|
||||
else:
|
||||
result.add newTryFinally(body, c.destroys)
|
||||
fin = newTryFinally(body, c.destroys)
|
||||
for i in countdown(c.scopeDestroys.high, 0): fin[1][0].add c.scopeDestroys[i]
|
||||
result.add fin
|
||||
else:
|
||||
result.add body
|
||||
dbg:
|
||||
|
||||
Reference in New Issue
Block a user