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:
Andreas Rumpf 2022-10-01 16:46:51 +02:00 • committed by GitHub
commit 8d47bf1822
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 472 additions and 684 deletions

View file

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