new move analyser2 (#20471)
* produce better code for closure environment creation * new 'first write' analysis; * scope based move analyser * code cleanup Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
This commit is contained in:
parent
cfff454cf9
commit
8d47bf1822
19 changed files with 472 additions and 684 deletions
|
|
@ -67,10 +67,11 @@ type
|
|||
exc: PNode # stack of exceptions
|
||||
tags: PNode # list of tags
|
||||
forbids: PNode # list of tags
|
||||
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt: int
|
||||
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
|
||||
owner: PSym
|
||||
ownerModule: PSym
|
||||
init: seq[int] # list of initialized variables
|
||||
scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`).
|
||||
guards: TModel # nested guards
|
||||
locked: seq[PNode] # locked locations
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
|
|
@ -189,6 +190,10 @@ proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
|
|||
if a.inTryStmt > 0 and a.config.exc == excSetjmp:
|
||||
incl(s.flags, sfVolatile)
|
||||
|
||||
proc varDecl(a: PEffects; n: PNode) {.inline.} =
|
||||
if n.kind == nkSym:
|
||||
a.scopes[n.sym.id] = a.currentBlock
|
||||
|
||||
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||
if n.kind != nkSym: return
|
||||
let s = n.sym
|
||||
|
|
@ -197,6 +202,23 @@ proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
|||
for x in a.init:
|
||||
if x == s.id: return
|
||||
a.init.add s.id
|
||||
if a.scopes.getOrDefault(s.id) == a.currentBlock:
|
||||
#[ Consider this case:
|
||||
|
||||
var x: T
|
||||
while true:
|
||||
if cond:
|
||||
x = T() #1
|
||||
else:
|
||||
x = T() #2
|
||||
use x
|
||||
|
||||
Even though both #1 and #2 are first writes we must use the `=copy`
|
||||
here so that the old value is destroyed because `x`'s destructor is
|
||||
run outside of the while loop. This is why we need the check here that
|
||||
the assignment is done in the same logical block as `x` was declared in.
|
||||
]#
|
||||
n.flags.incl nfFirstWrite2
|
||||
|
||||
proc initVarViaNew(a: PEffects, n: PNode) =
|
||||
if n.kind != nkSym: return
|
||||
|
|
@ -1102,18 +1124,22 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
createTypeBoundOps(tracked, child[i].typ, child.info)
|
||||
else:
|
||||
createTypeBoundOps(tracked, skipPragmaExpr(child[0]).typ, child.info)
|
||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||
if child.kind == nkIdentDefs:
|
||||
for i in 0..<child.len-2:
|
||||
let a = skipPragmaExpr(child[i])
|
||||
initVar(tracked, a, volatileCheck=false)
|
||||
addAsgnFact(tracked.guards, a, last)
|
||||
notNilCheck(tracked, last, a.typ)
|
||||
elif child.kind == nkVarTuple and last.kind != nkEmpty:
|
||||
varDecl(tracked, a)
|
||||
if last.kind != nkEmpty:
|
||||
initVar(tracked, a, volatileCheck=false)
|
||||
addAsgnFact(tracked.guards, a, last)
|
||||
notNilCheck(tracked, last, a.typ)
|
||||
elif child.kind == nkVarTuple:
|
||||
for i in 0..<child.len-1:
|
||||
if child[i].kind == nkEmpty or
|
||||
child[i].kind == nkSym and child[i].sym.name.s == "_":
|
||||
continue
|
||||
initVar(tracked, child[i], volatileCheck=false)
|
||||
varDecl(tracked, child[i])
|
||||
if last.kind != nkEmpty:
|
||||
initVar(tracked, child[i], volatileCheck=false)
|
||||
if last.kind in {nkPar, nkTupleConstr}:
|
||||
addAsgnFact(tracked.guards, child[i], last[i])
|
||||
notNilCheck(tracked, last[i], child[i].typ)
|
||||
|
|
@ -1128,6 +1154,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
inc tracked.currentBlock
|
||||
if isTrue(n[0]):
|
||||
trackBlock(tracked, n[1])
|
||||
else:
|
||||
|
|
@ -1139,8 +1166,10 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
track(tracked, n[1])
|
||||
setLen(tracked.init, oldState)
|
||||
setLen(tracked.guards.s, oldFacts)
|
||||
dec tracked.currentBlock
|
||||
of nkForStmt, nkParForStmt:
|
||||
# we are very conservative here and assume the loop is never executed:
|
||||
inc tracked.currentBlock
|
||||
let oldState = tracked.init.len
|
||||
|
||||
let oldFacts = tracked.guards.s.len
|
||||
|
|
@ -1182,6 +1211,8 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
track(tracked, loopBody)
|
||||
setLen(tracked.init, oldState)
|
||||
setLen(tracked.guards.s, oldFacts)
|
||||
dec tracked.currentBlock
|
||||
|
||||
of nkObjConstr:
|
||||
when false: track(tracked, n[0])
|
||||
let oldFacts = tracked.guards.s.len
|
||||
|
|
@ -1435,6 +1466,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
|
|||
t.graph = g
|
||||
t.config = g.config
|
||||
t.c = c
|
||||
t.currentBlock = 1
|
||||
|
||||
proc hasRealBody(s: PSym): bool =
|
||||
## also handles importc procs with runnableExamples, which requires `=`,
|
||||
|
|
@ -1455,6 +1487,12 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
|||
var t: TEffects
|
||||
initEffects(g, inferredEffects, s, t, c)
|
||||
rawInitEffects g, effects
|
||||
|
||||
if not isEmptyType(s.typ[0]) and
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
t.scopes[res.id] = t.currentBlock
|
||||
|
||||
track(t, body)
|
||||
|
||||
if s.kind != skMacro:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue