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
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
import
|
||||
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
|
||||
strutils, options, dfa, lowerings, tables, modulegraphs,
|
||||
strutils, options, lowerings, tables, modulegraphs,
|
||||
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
|
||||
varpartitions
|
||||
varpartitions, aliasanalysis, dfa
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
|
@ -27,12 +27,14 @@ from trees import exprStructuralEquivalent, getRoot
|
|||
type
|
||||
Con = object
|
||||
owner: PSym
|
||||
g: ControlFlowGraph
|
||||
when true:
|
||||
g: ControlFlowGraph
|
||||
graph: ModuleGraph
|
||||
inLoop, inSpawn, inLoopCond: int
|
||||
uninit: IntSet # set of uninit'ed vars
|
||||
uninitComputed: bool
|
||||
idgen: IdGenerator
|
||||
body: PNode
|
||||
otherUsage: TLineInfo
|
||||
|
||||
Scope = object # we do scope-based memory management.
|
||||
# a scope is comparable to an nkStmtListExpr like
|
||||
|
|
@ -40,6 +42,8 @@ type
|
|||
vars: seq[PSym]
|
||||
wasMoved: seq[PNode]
|
||||
final: seq[PNode] # finally section
|
||||
locals: seq[PSym]
|
||||
body: PNode
|
||||
needsTry: bool
|
||||
parent: ptr Scope
|
||||
|
||||
|
|
@ -70,160 +74,95 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
|||
s.vars.add(sym)
|
||||
result = newSymNode(sym)
|
||||
|
||||
proc nestedScope(parent: var Scope): Scope =
|
||||
Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent))
|
||||
proc nestedScope(parent: var Scope; body: PNode): Scope =
|
||||
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
|
||||
|
||||
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
|
||||
|
||||
import sets, hashes
|
||||
when false:
|
||||
var
|
||||
perfCounters: array[InstrKind, int]
|
||||
|
||||
proc hash(n: PNode): Hash = hash(cast[pointer](n))
|
||||
proc showCounters*() =
|
||||
for i in low(InstrKind)..high(InstrKind):
|
||||
echo "INSTR ", i, " ", perfCounters[i]
|
||||
|
||||
type
|
||||
State = ref object
|
||||
lastReads: IntSet
|
||||
potentialLastReads: IntSet
|
||||
notLastReads: IntSet
|
||||
alreadySeen: HashSet[PNode]
|
||||
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
let root = parampatterns.exprRoot(n, allowCalls=false)
|
||||
if root == nil: return false
|
||||
|
||||
proc preprocessCfg(cfg: var ControlFlowGraph) =
|
||||
for i in 0..<cfg.len:
|
||||
if cfg[i].kind in {goto, fork} and i + cfg[i].dest > cfg.len:
|
||||
cfg[i].dest = cfg.len - i
|
||||
var s = addr(scope)
|
||||
while s != nil:
|
||||
if s.locals.contains(root): break
|
||||
s = s.parent
|
||||
|
||||
proc mergeStates(a: var State, b: sink State) =
|
||||
# Inplace for performance:
|
||||
# lastReads = a.lastReads + b.lastReads
|
||||
# potentialLastReads = (a.potentialLastReads + b.potentialLastReads) - (a.notLastReads + b.notLastReads)
|
||||
# notLastReads = a.notLastReads + b.notLastReads
|
||||
# alreadySeen = a.alreadySeen + b.alreadySeen
|
||||
# b is never nil
|
||||
if a == nil:
|
||||
a = b
|
||||
c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
|
||||
dbg:
|
||||
echo "\n### ", c.owner.name.s, ":\nCFG:"
|
||||
echoCfg(c.g)
|
||||
#echo c.body
|
||||
|
||||
var j = 0
|
||||
while j < c.g.len:
|
||||
if c.g[j].kind == use and c.g[j].n == n: break
|
||||
inc j
|
||||
c.otherUsage = unknownLineInfo
|
||||
if j < c.g.len:
|
||||
var pcs = @[j+1]
|
||||
var marked = initIntSet()
|
||||
result = true
|
||||
while pcs.len > 0:
|
||||
var pc = pcs.pop()
|
||||
if not marked.contains(pc):
|
||||
let oldPc = pc
|
||||
while pc < c.g.len:
|
||||
dbg:
|
||||
echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
|
||||
when false:
|
||||
inc perfCounters[c.g[pc].kind]
|
||||
case c.g[pc].kind
|
||||
of loop:
|
||||
let back = pc + c.g[pc].dest
|
||||
if not marked.containsOrIncl(back):
|
||||
pc = back
|
||||
else:
|
||||
break
|
||||
of goto:
|
||||
pc = pc + c.g[pc].dest
|
||||
of fork:
|
||||
if not marked.contains(pc+1):
|
||||
pcs.add pc + 1
|
||||
pc = pc + c.g[pc].dest
|
||||
of use:
|
||||
if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
|
||||
c.otherUsage = c.g[pc].n.info
|
||||
return false
|
||||
inc pc
|
||||
of def:
|
||||
if c.g[pc].n.aliases(n) == yes:
|
||||
# the path leads to a redefinition of 's' --> sink 's'.
|
||||
break
|
||||
elif n.aliases(c.g[pc].n) != no:
|
||||
# only partially writes to 's' --> can't sink 's', so this def reads 's'
|
||||
# or maybe writes to 's' --> can't sink 's'
|
||||
c.otherUsage = c.g[pc].n.info
|
||||
return false
|
||||
inc pc
|
||||
marked.incl oldPc
|
||||
else:
|
||||
a.lastReads.incl b.lastReads
|
||||
a.potentialLastReads.incl b.potentialLastReads
|
||||
a.potentialLastReads.excl a.notLastReads
|
||||
a.potentialLastReads.excl b.notLastReads
|
||||
a.notLastReads.incl b.notLastReads
|
||||
a.alreadySeen.incl b.alreadySeen
|
||||
result = false
|
||||
|
||||
proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) =
|
||||
template aliasesCached(obj, field: PNode): AliasKind =
|
||||
aliases(obj, field)
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
if not hasDestructor(c, n.typ): return true
|
||||
|
||||
var cfg = cfg
|
||||
preprocessCfg(cfg)
|
||||
|
||||
var states = newSeq[State](cfg.len + 1)
|
||||
states[0] = State()
|
||||
|
||||
for pc in 0..<cfg.len:
|
||||
template state: State = states[pc]
|
||||
if state != nil:
|
||||
case cfg[pc].kind
|
||||
of def:
|
||||
var potentialLastReadsCopy = state.potentialLastReads
|
||||
for r in potentialLastReadsCopy:
|
||||
if cfg[pc].n.aliasesCached(cfg[r].n) == yes:
|
||||
# the path leads to a redefinition of 's' --> sink 's'.
|
||||
state.lastReads.incl r
|
||||
state.potentialLastReads.excl r
|
||||
elif cfg[r].n.aliasesCached(cfg[pc].n) != no:
|
||||
# only partially writes to 's' --> can't sink 's', so this def reads 's'
|
||||
# or maybe writes to 's' --> can't sink 's'
|
||||
cfg[r].n.comment = '\n' & $pc
|
||||
state.potentialLastReads.excl r
|
||||
state.notLastReads.incl r
|
||||
|
||||
var alreadySeenThisNode = false
|
||||
for s in state.alreadySeen:
|
||||
if cfg[pc].n.aliasesCached(s) != no or s.aliasesCached(cfg[pc].n) != no:
|
||||
alreadySeenThisNode = true; break
|
||||
if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite
|
||||
else: cfg[pc].n.flags.incl nfFirstWrite
|
||||
|
||||
state.alreadySeen.incl cfg[pc].n
|
||||
|
||||
mergeStates(states[pc + 1], move(states[pc]))
|
||||
of use:
|
||||
var potentialLastReadsCopy = state.potentialLastReads
|
||||
for r in potentialLastReadsCopy:
|
||||
if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no:
|
||||
cfg[r].n.comment = '\n' & $pc
|
||||
state.potentialLastReads.excl r
|
||||
state.notLastReads.incl r
|
||||
|
||||
state.potentialLastReads.incl pc
|
||||
|
||||
state.alreadySeen.incl cfg[pc].n
|
||||
|
||||
mergeStates(states[pc + 1], move(states[pc]))
|
||||
of goto:
|
||||
mergeStates(states[pc + cfg[pc].dest], move(states[pc]))
|
||||
of fork:
|
||||
var copy = State()
|
||||
copy[] = states[pc][]
|
||||
mergeStates(states[pc + cfg[pc].dest], copy)
|
||||
mergeStates(states[pc + 1], move(states[pc]))
|
||||
|
||||
let lastReads = (states[^1].lastReads + states[^1].potentialLastReads) - states[^1].notLastReads
|
||||
var lastReadTable: Table[PNode, seq[int]]
|
||||
for position, node in cfg:
|
||||
if node.kind == use:
|
||||
lastReadTable.mgetOrPut(node.n, @[]).add position
|
||||
for node, positions in lastReadTable:
|
||||
block checkIfAllPosLastRead:
|
||||
for p in positions:
|
||||
if p notin lastReads: break checkIfAllPosLastRead
|
||||
node.flags.incl nfLastRead
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con): bool =
|
||||
let m = dfa.skipConvDfa(n)
|
||||
(m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or nfLastRead in m.flags
|
||||
let m = skipConvDfa(n)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
isLastReadImpl(n, c, s)
|
||||
|
||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||
let m = dfa.skipConvDfa(n)
|
||||
nfFirstWrite in m.flags
|
||||
|
||||
proc initialized(code: ControlFlowGraph; pc: int,
|
||||
init, uninit: var IntSet; until: int): int =
|
||||
## Computes the set of definitely initialized variables across all code paths
|
||||
## as an IntSet of IDs.
|
||||
var pc = pc
|
||||
while pc < code.len:
|
||||
case code[pc].kind
|
||||
of goto:
|
||||
pc += code[pc].dest
|
||||
of fork:
|
||||
var initA = initIntSet()
|
||||
var initB = initIntSet()
|
||||
var variantA = pc + 1
|
||||
var variantB = pc + code[pc].dest
|
||||
while variantA != variantB:
|
||||
if max(variantA, variantB) > until:
|
||||
break
|
||||
if variantA < variantB:
|
||||
variantA = initialized(code, variantA, initA, uninit, min(variantB, until))
|
||||
else:
|
||||
variantB = initialized(code, variantB, initB, uninit, min(variantA, until))
|
||||
pc = min(variantA, variantB)
|
||||
# we add vars if they are in both branches:
|
||||
for v in initA:
|
||||
if v in initB:
|
||||
init.incl v
|
||||
of use:
|
||||
let v = code[pc].n.sym
|
||||
if v.kind != skParam and v.id notin init:
|
||||
# attempt to read an uninit'ed variable
|
||||
uninit.incl v.id
|
||||
inc pc
|
||||
of def:
|
||||
let v = code[pc].n.sym
|
||||
init.incl v.id
|
||||
inc pc
|
||||
return pc
|
||||
let m = skipConvDfa(n)
|
||||
result = nfFirstWrite2 in m.flags
|
||||
|
||||
proc isCursor(n: PNode): bool =
|
||||
case n.kind
|
||||
|
|
@ -247,9 +186,11 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
|||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
if ri.comment.startsWith('\n'):
|
||||
if c.otherUsage != unknownLineInfo:
|
||||
# ri.comment.startsWith('\n'):
|
||||
m.add "; another read is done here: "
|
||||
m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
|
||||
m.add c.graph.config $ c.otherUsage
|
||||
#m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
|
||||
elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
|
||||
m.add "; try to make "
|
||||
m.add renderTree(ri)
|
||||
|
|
@ -625,7 +566,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
var branch = shallowCopy(it)
|
||||
for j in 0 ..< it.len-1:
|
||||
branch[j] = copyTree(it[j])
|
||||
var ofScope = nestedScope(s)
|
||||
var ofScope = nestedScope(s, it.lastSon)
|
||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
||||
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
|
||||
else:
|
||||
|
|
@ -638,7 +579,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
result = copyNode(n)
|
||||
result.add p(n[0], c, s, normal)
|
||||
dec c.inLoopCond
|
||||
var bodyScope = nestedScope(s)
|
||||
var bodyScope = nestedScope(s, n[1])
|
||||
let bodyResult = p(n[1], c, bodyScope, normal)
|
||||
result.add processScope(c, bodyScope, bodyResult)
|
||||
dec c.inLoop
|
||||
|
|
@ -650,7 +591,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
for i in 0..<last-1:
|
||||
result[i] = n[i]
|
||||
result[last-1] = p(n[last-1], c, s, normal)
|
||||
var bodyScope = nestedScope(s)
|
||||
var bodyScope = nestedScope(s, n[1])
|
||||
let bodyResult = p(n[last], c, bodyScope, normal)
|
||||
result[last] = processScope(c, bodyScope, bodyResult)
|
||||
dec c.inLoop
|
||||
|
|
@ -658,7 +599,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
of nkBlockStmt, nkBlockExpr:
|
||||
result = copyNode(n)
|
||||
result.add n[0]
|
||||
var bodyScope = nestedScope(s)
|
||||
var bodyScope = nestedScope(s, n[1])
|
||||
result.add if n[1].typ.isEmptyType or willProduceStmt:
|
||||
processScope(c, bodyScope, processCall(n[1], bodyScope))
|
||||
else:
|
||||
|
|
@ -669,7 +610,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
var branch = shallowCopy(it)
|
||||
var branchScope = nestedScope(s)
|
||||
var branchScope = nestedScope(s, it.lastSon)
|
||||
if it.kind in {nkElifBranch, nkElifExpr}:
|
||||
#Condition needs to be destroyed outside of the condition/branch scope
|
||||
branch[0] = p(it[0], c, s, normal)
|
||||
|
|
@ -682,7 +623,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
|
||||
of nkTryStmt:
|
||||
result = copyNode(n)
|
||||
var tryScope = nestedScope(s)
|
||||
var tryScope = nestedScope(s, n[0])
|
||||
result.add if n[0].typ.isEmptyType or willProduceStmt:
|
||||
processScope(c, tryScope, maybeVoid(n[0], tryScope))
|
||||
else:
|
||||
|
|
@ -691,7 +632,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
|||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
var branch = copyTree(it)
|
||||
var branchScope = nestedScope(s)
|
||||
var branchScope = nestedScope(s, it[^1])
|
||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
|
||||
processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
|
||||
else: maybeVoid(it[^1], branchScope))
|
||||
|
|
@ -744,7 +685,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
|||
nkCallKinds + nkLiterals:
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c) and not (n.kind == nkSym and isCursor(n)):
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
result = destructiveMoveVar(n, c, s)
|
||||
|
|
@ -864,6 +805,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
|||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||
for i in 0..<it.len-2:
|
||||
if it[i].kind == nkSym: s.locals.add it[i].sym
|
||||
let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
|
||||
result.add p(x, c, s, consumed)
|
||||
elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
|
||||
|
|
@ -871,6 +814,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
|||
let v = skipPragmaExpr(it[j])
|
||||
if v.kind == nkSym:
|
||||
if sfCompileTime in v.sym.flags: continue
|
||||
s.locals.add v.sym
|
||||
pVarTopLevel(v, c, s, result)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
|
||||
|
|
@ -943,7 +887,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
|||
for i in 1 ..< n.len:
|
||||
result[i] = n[i]
|
||||
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
|
||||
s.wasMoved.add c.genWasMoved(n)
|
||||
else:
|
||||
result = passCopyToSink(result, c, s)
|
||||
|
|
@ -953,7 +897,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
|||
for i in 0 ..< n.len:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
|
||||
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
|
||||
# without the junk, hence 'c.genWasMoved(n)'
|
||||
# and not 'c.genWasMoved(result)':
|
||||
|
|
@ -1054,7 +998,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
|
|||
if isUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||
if aliases(dest, ri) == no:
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
if isAtom(ri[1]):
|
||||
|
|
@ -1079,12 +1023,12 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
|
|||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkSym:
|
||||
if isSinkParam(ri.sym) and isLastRead(ri, c):
|
||||
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||
isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
|
|
@ -1101,7 +1045,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
|
|||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
|
|
@ -1111,49 +1055,44 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
|
|||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
|
||||
proc computeUninit(c: var Con) =
|
||||
if not c.uninitComputed:
|
||||
c.uninitComputed = true
|
||||
c.uninit = initIntSet()
|
||||
var init = initIntSet()
|
||||
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
if not c.uninitComputed:
|
||||
c.uninitComputed = true
|
||||
c.uninit = initIntSet()
|
||||
var init = initIntSet()
|
||||
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
|
||||
|
||||
proc injectDefaultCalls(n: PNode, c: var Con) =
|
||||
case n.kind
|
||||
of nkVarSection, nkLetSection:
|
||||
for it in n:
|
||||
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
|
||||
computeUninit(c)
|
||||
for j in 0..<it.len-2:
|
||||
let v = skipPragmaExpr(it[j])
|
||||
doAssert v.kind == nkSym
|
||||
if c.uninit.contains(v.sym.id):
|
||||
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
|
||||
break
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
||||
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
injectDefaultCalls(n[i], c)
|
||||
proc injectDefaultCalls(n: PNode, c: var Con) =
|
||||
case n.kind
|
||||
of nkVarSection, nkLetSection:
|
||||
for it in n:
|
||||
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
|
||||
computeUninit(c)
|
||||
for j in 0..<it.len-2:
|
||||
let v = skipPragmaExpr(it[j])
|
||||
doAssert v.kind == nkSym
|
||||
if c.uninit.contains(v.sym.id):
|
||||
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
|
||||
break
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
||||
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
injectDefaultCalls(n[i], c)
|
||||
|
||||
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
|
||||
when toDebug.len > 0:
|
||||
shouldDebug = toDebug == owner.name.s or toDebug == "always"
|
||||
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
|
||||
return n
|
||||
var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen)
|
||||
dbg:
|
||||
echo "\n### ", owner.name.s, ":\nCFG:"
|
||||
echoCfg(c.g)
|
||||
echo n
|
||||
var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
|
||||
|
||||
if optCursorInference in g.config.options:
|
||||
computeCursors(owner, n, g)
|
||||
|
||||
computeLastReadsAndFirstWrites(c.g)
|
||||
|
||||
var scope: Scope
|
||||
var scope = Scope(body: n)
|
||||
let body = p(n, c, scope, normal)
|
||||
|
||||
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue