DFA and injectdestructors cleanup (#14824)

* DFA and injectdestructors cleanup

* More precise write analysis

* Cleanup obsoleted path

* Unify defInstrTargets and useInstrTargets

* Misc cleanups

* Nicer CFG printing

* Misc cleanups 2
This commit is contained in:
Clyybber 2020-06-28 19:36:30 +02:00 • committed by GitHub
commit 62394616e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 156 deletions

View file

@ -29,7 +29,7 @@
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, types, intsets, lineinfos, renderer import ast, types, intsets, lineinfos, renderer, asciitables
from patterns import sameTrees from patterns import sameTrees
@ -57,14 +57,11 @@ type
Con = object Con = object
code: ControlFlowGraph code: ControlFlowGraph
inCall, inTryStmt: int inTryStmt: int
blocks: seq[TBlock] blocks: seq[TBlock]
owner: PSym owner: PSym
proc debugInfo(info: TLineInfo): string = proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
result = $info.line #info.toFilename & ":" & $info.line
proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
# for debugging purposes # for debugging purposes
# first iteration: compute all necessary labels: # first iteration: compute all necessary labels:
var jumpTargets = initIntSet() var jumpTargets = initIntSet()
@ -85,17 +82,14 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
result.add "L" result.add "L"
result.addInt c[i].dest+i result.addInt c[i].dest+i
result.add("\t#") result.add("\t#")
result.add(debugInfo(c[i].n.info)) result.add($c[i].n.info.line)
result.add("\n") result.add("\n")
inc i inc i
if i in jumpTargets: result.add("L" & $i & ": End\n") if i in jumpTargets: result.add("L" & $i & ": End\n")
# consider calling `asciitables.alignTable`
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} = proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
## echos the ControlFlowGraph for debugging purposes. ## echos the ControlFlowGraph for debugging purposes.
var buf = "" echo codeListing(c, start, last).alignTable
codeListing(c, buf, start, last)
echo buf
proc forkI(c: var Con; n: PNode): TPosition = proc forkI(c: var Con; n: PNode): TPosition =
result = TPosition(c.code.len) result = TPosition(c.code.len)
@ -273,22 +267,20 @@ duplicate the 'join' instructions on breaks and return exits!
]# ]#
proc genLabel(c: Con): TPosition = proc genLabel(c: Con): TPosition = TPosition(c.code.len)
result = TPosition(c.code.len)
template checkedDistance(dist): int =
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
dist
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) = proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
let dist = p.int - c.code.len c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
doAssert(low(int) div 2 + 1 < dist and dist < high(int) div 2)
c.code.add Instr(n: n, kind: goto, dest: dist)
proc patch(c: var Con, p: TPosition) = proc patch(c: var Con, p: TPosition) =
# patch with current index # patch with current index
let p = p.int c.code[p.int].dest = checkedDistance(c.code.len - p.int)
let diff = c.code.len - p
doAssert(low(int) div 2 + 1 < diff and diff < high(int) div 2)
c.code[p].dest = diff
proc gen(c: var Con; n: PNode) # {.noSideEffect.} func gen(c: var Con; n: PNode)
proc popBlock(c: var Con; oldLen: int) = proc popBlock(c: var Con; oldLen: int) =
var exits: seq[TPosition] var exits: seq[TPosition]
@ -302,8 +294,8 @@ proc popBlock(c: var Con; oldLen: int) =
c.patch e c.patch e
c.blocks.setLen(oldLen) c.blocks.setLen(oldLen)
template withBlock(labl: PSym; body: untyped) {.dirty.} = template withBlock(labl: PSym; body: untyped) =
var oldLen {.gensym.} = c.blocks.len let oldLen = c.blocks.len
c.blocks.add TBlock(isTryBlock: false, label: labl) c.blocks.add TBlock(isTryBlock: false, label: labl)
body body
popBlock(c, oldLen) popBlock(c, oldLen)
@ -366,11 +358,9 @@ when true:
endings[i] = c.forkI(n) endings[i] = c.forkI(n)
c.gen(n[1]) c.gen(n[1])
for i in countdown(endings.high, 0): for i in countdown(endings.high, 0):
let endPos = endings[i] c.patch(endings[i])
c.patch(endPos)
else: else:
proc genWhile(c: var Con; n: PNode) = proc genWhile(c: var Con; n: PNode) =
# lab1: # lab1:
# cond, tmp # cond, tmp
@ -385,10 +375,9 @@ else:
c.jmpBack(n, lab1) c.jmpBack(n, lab1)
else: else:
c.gen(n[0]) c.gen(n[0])
let lab2 = c.forkI(n) forkT(n):
c.gen(n[1]) c.gen(n[1])
c.jmpBack(n, lab1) c.jmpBack(n, lab1)
c.patch(lab2)
template forkT(n, body) = template forkT(n, body) =
let lab1 = c.forkI(n) let lab1 = c.forkI(n)
@ -434,16 +423,14 @@ proc genIf(c: var Con, n: PNode) =
]# ]#
var endings: seq[TPosition] = @[] var endings: seq[TPosition] = @[]
for i in 0..<n.len: for i in 0..<n.len:
var it = n[i] let it = n[i]
c.gen(it[0]) c.gen(it[0])
if it.len == 2: if it.len == 2:
let elsePos = forkI(c, it[1]) forkT(it[1]):
c.gen(it[1]) c.gen(it[1])
endings.add(c.gotoI(it[1])) endings.add c.gotoI(it[1])
c.patch(elsePos)
for i in countdown(endings.high, 0): for i in countdown(endings.high, 0):
let endPos = endings[i] c.patch(endings[i])
c.patch(endPos)
proc genAndOr(c: var Con; n: PNode) = proc genAndOr(c: var Con; n: PNode) =
# asgn dest, a # asgn dest, a
@ -473,16 +460,13 @@ proc genCase(c: var Con; n: PNode) =
c.gen(n[0]) c.gen(n[0])
for i in 1..<n.len: for i in 1..<n.len:
let it = n[i] let it = n[i]
if it.len == 1: if it.len == 1 or (i == n.len-1 and isExhaustive):
c.gen(it[0])
elif i == n.len-1 and isExhaustive:
# treat the last branch as 'else' if this is an exhaustive case statement. # treat the last branch as 'else' if this is an exhaustive case statement.
c.gen(it.lastSon) c.gen(it.lastSon)
else: else:
let elsePos = c.forkI(it.lastSon) forkT(it.lastSon):
c.gen(it.lastSon) c.gen(it.lastSon)
endings.add(c.gotoI(it.lastSon)) endings.add c.gotoI(it.lastSon)
c.patch(elsePos)
for i in countdown(endings.high, 0): for i in countdown(endings.high, 0):
let endPos = endings[i] let endPos = endings[i]
c.patch(endPos) c.patch(endPos)
@ -506,7 +490,6 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
proc genBreak(c: var Con; n: PNode) = proc genBreak(c: var Con; n: PNode) =
if n[0].kind == nkSym: if n[0].kind == nkSym:
#echo cast[int](n[0].sym)
for i in countdown(c.blocks.high, 0): for i in countdown(c.blocks.high, 0):
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym: if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
genBreakOrRaiseAux(c, i, n) genBreakOrRaiseAux(c, i, n)
@ -525,7 +508,6 @@ proc genTry(c: var Con; n: PNode) =
c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty)) c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
inc c.inTryStmt inc c.inTryStmt
#let elsePos = c.forkI(n)
c.gen(n[0]) c.gen(n[0])
dec c.inTryStmt dec c.inTryStmt
@ -534,20 +516,14 @@ proc genTry(c: var Con; n: PNode) =
c.blocks.setLen oldLen c.blocks.setLen oldLen
#c.patch(elsePos)
for i in 1..<n.len: for i in 1..<n.len:
let it = n[i] let it = n[i]
if it.kind != nkFinally: if it.kind != nkFinally:
let endExcept = c.forkI(it) forkT(it):
c.gen(it.lastSon) c.gen(it.lastSon)
endings.add(c.gotoI(it)) endings.add c.gotoI(it)
c.patch(endExcept)
for i in countdown(endings.high, 0): for i in countdown(endings.high, 0):
let endPos = endings[i] c.patch(endings[i])
c.patch(endPos)
# join the 'elsePos' forkI instruction:
#c.joinI(c.blocks[^1].forks.pop(), n)
let fin = lastSon(n) let fin = lastSon(n)
if fin.kind == nkFinally: if fin.kind == nkFinally:
@ -597,59 +573,43 @@ proc skipConvDfa*(n: PNode): PNode =
result = result[1] result = result[1]
else: break else: break
proc genUse(c: var Con; orig: PNode) = proc aliases*(obj, field: PNode): bool =
var n = orig var n = field
var obj = obj
while true: while true:
case obj.kind
of {nkObjDownConv, nkObjUpConv, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}:
obj = obj[0]
of PathKinds1:
obj = obj[1]
else: break
while true:
if sameTrees(obj, n): return true
case n.kind case n.kind
of PathKinds0 - {nkBracketExpr}: of PathKinds0:
n = n[0]
of nkBracketExpr:
gen(c, n[1])
n = n[0] n = n[0]
of PathKinds1: of PathKinds1:
n = n[1] n = n[1]
else: break else: break
if n.kind in nkCallKinds:
gen(c, n)
if n.kind == nkSym and n.sym.kind in InterestingSyms:
c.code.add Instr(n: orig, kind: use)
proc aliases*(obj, field: PNode): bool = type InstrTargetKind* = enum
var n = field None, Full, Partial
var obj = obj
while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv,
nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}:
obj = obj[0]
while true:
if sameTrees(obj, n): return true
case n.kind
of PathKinds0, PathKinds1:
n = n[0]
else:
break
proc useInstrTargets*(ins: Instr; loc: PNode): bool = proc instrTargets*(insloc, loc: PNode): InstrTargetKind =
assert ins.kind == use if sameTrees(insloc, loc) or insloc.aliases(loc):
result = sameTrees(ins.n, loc) or Full # x -> x; x -> x.f
ins.n.aliases(loc) or loc.aliases(ins.n) elif loc.aliases(insloc):
# We can come here if loc is 'x.f' and ins.n is 'x' or the other way round. Partial # x.f -> x
# use x.f; question: does it affect the full 'x'? No. else: None
# use x; question does it affect 'x.f'? Yes.
proc defInstrTargets*(ins: Instr; loc: PNode): bool =
assert ins.kind == def
result = sameTrees(ins.n, loc) or ins.n.aliases(loc)
# We can come here if loc is 'x.f' and ins.n is 'x' or the other way round.
# def x.f; question: does it affect the full 'x'? No.
# def x; question: does it affect the 'x.f'? Yes.
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
var n = orig var n = orig
while true: while true:
case n.kind case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, of PathKinds0 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}:
nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr:
n = n[0] n = n[0]
of PathKinds1:
n = n[1]
of nkBracketExpr: of nkBracketExpr:
# in a[i] the 'i' must be known # in a[i] the 'i' must be known
if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}: if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}:
@ -662,10 +622,9 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
# bug #14159, we cannot reason about sinkParam[].location as it can # bug #14159, we cannot reason about sinkParam[].location as it can
# still be shared for tyRef. # still be shared for tyRef.
n = n[0] n = n[0]
return n.kind == nkSym and n.sym.owner == owner and ( return n.kind == nkSym and n.sym.owner == owner and
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
else: else: break
break
# XXX Allow closure deref operations here if we know # XXX Allow closure deref operations here if we know
# the owner controlled the closure allocation? # the owner controlled the closure allocation?
result = n.kind == nkSym and n.sym.owner == owner and result = n.kind == nkSym and n.sym.owner == owner and
@ -682,36 +641,37 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
# free the old data and all is lost! Lesson: Don't be too smart, trust the # free the old data and all is lost! Lesson: Don't be too smart, trust the
# lower level C++ optimizer to specialize this code. # lower level C++ optimizer to specialize this code.
proc genDef(c: var Con; n: PNode) = proc skipTrivials(c: var Con, n: PNode): PNode =
var m = n result = n
# XXX do something about this duplicated logic here.
while true: while true:
case m.kind case result.kind
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, of PathKinds0 - {nkBracketExpr}:
nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr: result = result[0]
m = m[0]
of nkBracketExpr: of nkBracketExpr:
gen(c, m[1]) gen(c, result[1])
m = m[0] result = result[0]
of nkHiddenDeref, nkDerefExpr: of PathKinds1:
m = m[0] result = result[1]
else: else: break
break
proc genUse(c: var Con; orig: PNode) =
let n = c.skipTrivials(orig)
if n.kind == nkSym and n.sym.kind in InterestingSyms: if n.kind == nkSym and n.sym.kind in InterestingSyms:
c.code.add Instr(n: n, kind: def) c.code.add Instr(n: orig, kind: use)
elif isAnalysableFieldAccess(n, c.owner): elif n.kind in nkCallKinds:
c.code.add Instr(n: n, kind: def)
else:
# bug #13314: An assignment to t5.w = -5 is a usage of 't5'
# we still need to gather the use information:
gen(c, n) gen(c, n)
proc genDef(c: var Con; orig: PNode) =
let n = c.skipTrivials(orig)
if n.kind == nkSym and n.sym.kind in InterestingSyms:
c.code.add Instr(n: orig, kind: def)
proc genCall(c: var Con; n: PNode) = proc genCall(c: var Con; n: PNode) =
gen(c, n[0]) gen(c, n[0])
var t = n[0].typ var t = n[0].typ
if t != nil: t = t.skipTypes(abstractInst) if t != nil: t = t.skipTypes(abstractInst)
inc c.inCall
for i in 1..<n.len: for i in 1..<n.len:
gen(c, n[i]) gen(c, n[i])
when false: when false:
@ -725,13 +685,11 @@ proc genCall(c: var Con; n: PNode) =
# goto exceptionHandler (except or finally) # goto exceptionHandler (except or finally)
# lab1: # lab1:
# join F1 # join F1
let endGoto = c.forkI(n) forkT(n):
for i in countdown(c.blocks.high, 0): for i in countdown(c.blocks.high, 0):
if c.blocks[i].isTryBlock: if c.blocks[i].isTryBlock:
genBreakOrRaiseAux(c, i, n) genBreakOrRaiseAux(c, i, n)
break break
c.patch(endGoto)
dec c.inCall
proc genMagic(c: var Con; n: PNode; m: TMagic) = proc genMagic(c: var Con; n: PNode; m: TMagic) =
case m case m
@ -754,7 +712,7 @@ proc genVarSection(c: var Con; n: PNode) =
if a.lastSon.kind != nkEmpty: if a.lastSon.kind != nkEmpty:
genDef(c, a[0]) genDef(c, a[0])
proc gen(c: var Con; n: PNode) = func gen(c: var Con; n: PNode) =
case n.kind case n.kind
of nkSym: genUse(c, n) of nkSym: genUse(c, n)
of nkCallKinds: of nkCallKinds:
@ -775,7 +733,7 @@ proc gen(c: var Con; n: PNode) =
# "uses" 'i'. But we are only talking about builtin array indexing so # "uses" 'i'. But we are only talking about builtin array indexing so
# it doesn't matter and 'x = 34' is NOT a usage of 'x'. # it doesn't matter and 'x = 34' is NOT a usage of 'x'.
genDef(c, n[0]) genDef(c, n[0])
of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}: of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
genUse(c, n) genUse(c, n)
of nkIfStmt, nkIfExpr: genIf(c, n) of nkIfStmt, nkIfExpr: genIf(c, n)
of nkWhenStmt: of nkWhenStmt:
@ -792,13 +750,12 @@ proc gen(c: var Con; n: PNode) =
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt: nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
for x in n: gen(c, x) for x in n: gen(c, x)
of nkPragmaBlock: gen(c, n.lastSon) of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n[0]) of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv: gen(c, n[0])
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
gen(c, n[1]) gen(c, n[1])
of nkStringToCString, nkCStringToString: gen(c, n[0])
of nkVarSection, nkLetSection: genVarSection(c, n) of nkVarSection, nkLetSection: genVarSection(c, n)
of nkDefer: of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
doAssert false, "dfa construction pass requires the elimination of 'defer'"
else: discard else: discard
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =

View file

@ -65,40 +65,43 @@ template dbg(body) =
proc p(n: PNode; c: var Con; mode: ProcessMode): PNode proc p(n: PNode; c: var Con; mode: ProcessMode): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con, isDecl = false): PNode proc moveOrCopy(dest, ri: PNode; c: var Con, isDecl = false): PNode
proc isLastRead(location: PNode; c: var Con; pc, until: int): int = proc isLastRead(location: PNode; cfg: ControlFlowGraph; otherRead: var PNode; pc, until: int): int =
var pc = pc var pc = pc
while pc < c.g.len and pc < until: while pc < cfg.len and pc < until:
case c.g[pc].kind case cfg[pc].kind
of def: of def:
if defInstrTargets(c.g[pc], location): if instrTargets(cfg[pc].n, location) == Full:
# the path leads to a redefinition of 's' --> abandon it. # the path leads to a redefinition of 's' --> abandon it.
return high(int) return high(int)
elif instrTargets(cfg[pc].n, location) == Partial:
# only partially writes to 's' --> can't sink 's', so this def reads 's'
otherRead = cfg[pc].n
return -1
inc pc inc pc
of use: of use:
if useInstrTargets(c.g[pc], location): if instrTargets(cfg[pc].n, location) != None:
c.otherRead = c.g[pc].n otherRead = cfg[pc].n
return -1 return -1
inc pc inc pc
of goto: of goto:
pc = pc + c.g[pc].dest pc = pc + cfg[pc].dest
of fork: of fork:
# every branch must lead to the last read of the location: # every branch must lead to the last read of the location:
var variantA = pc + 1 var variantA = pc + 1
var variantB = pc + c.g[pc].dest var variantB = pc + cfg[pc].dest
while variantA != variantB: while variantA != variantB:
if min(variantA, variantB) < 0: return -1 if min(variantA, variantB) < 0: return -1
if max(variantA, variantB) >= c.g.len or min(variantA, variantB) >= until: if max(variantA, variantB) >= cfg.len or min(variantA, variantB) >= until:
break break
if variantA < variantB: if variantA < variantB:
variantA = isLastRead(location, c, variantA, min(variantB, until)) variantA = isLastRead(location, cfg, otherRead, variantA, min(variantB, until))
else: else:
variantB = isLastRead(location, c, variantB, min(variantA, until)) variantB = isLastRead(location, cfg, otherRead, variantB, min(variantA, until))
pc = min(variantA, variantB) pc = min(variantA, variantB)
return pc return pc
proc isLastRead(n: PNode; c: var Con): bool = proc isLastRead(n: PNode; c: var Con): bool =
# first we need to search for the instruction that belongs to 'n': # first we need to search for the instruction that belongs to 'n':
c.otherRead = nil
var instr = -1 var instr = -1
let m = dfa.skipConvDfa(n) let m = dfa.skipConvDfa(n)
@ -116,36 +119,37 @@ proc isLastRead(n: PNode; c: var Con): bool =
# ensure that we don't find another 'use X' instruction. # ensure that we don't find another 'use X' instruction.
if instr+1 >= c.g.len: return true if instr+1 >= c.g.len: return true
result = isLastRead(n, c, instr+1, int.high) >= 0 c.otherRead = nil
result = isLastRead(n, c.g, c.otherRead, instr+1, int.high) >= 0
dbg: echo "ugh ", c.otherRead.isNil, " ", result dbg: echo "ugh ", c.otherRead.isNil, " ", result
proc isFirstWrite(location: PNode; c: var Con; pc, until: int): int = proc isFirstWrite(location: PNode; cfg: ControlFlowGraph; pc, until: int): int =
var pc = pc var pc = pc
while pc < until: while pc < until:
case c.g[pc].kind case cfg[pc].kind
of def: of def:
if defInstrTargets(c.g[pc], location): if instrTargets(cfg[pc].n, location) != None:
# a definition of 's' before ours makes ours not the first write # a definition of 's' before ours makes ours not the first write
return -1 return -1
inc pc inc pc
of use: of use:
if useInstrTargets(c.g[pc], location): if instrTargets(cfg[pc].n, location) != None:
return -1 return -1
inc pc inc pc
of goto: of goto:
pc = pc + c.g[pc].dest pc = pc + cfg[pc].dest
of fork: of fork:
# every branch must not contain a def/use of our location: # every branch must not contain a def/use of our location:
var variantA = pc + 1 var variantA = pc + 1
var variantB = pc + c.g[pc].dest var variantB = pc + cfg[pc].dest
while variantA != variantB: while variantA != variantB:
if min(variantA, variantB) < 0: return -1 if min(variantA, variantB) < 0: return -1
if max(variantA, variantB) > until: if max(variantA, variantB) > until:
break break
if variantA < variantB: if variantA < variantB:
variantA = isFirstWrite(location, c, variantA, min(variantB, until)) variantA = isFirstWrite(location, cfg, variantA, min(variantB, until))
else: else:
variantB = isFirstWrite(location, c, variantB, min(variantA, until)) variantB = isFirstWrite(location, cfg, variantB, min(variantA, until))
pc = min(variantA, variantB) pc = min(variantA, variantB)
return pc return pc
@ -165,7 +169,7 @@ proc isFirstWrite(n: PNode; c: var Con): bool =
# ensure that we don't find another 'def/use X' instruction. # ensure that we don't find another 'def/use X' instruction.
if instr == 0: return true if instr == 0: return true
result = isFirstWrite(n, c, 0, instr) >= 0 result = isFirstWrite(n, c.g, 0, instr) >= 0
proc initialized(code: ControlFlowGraph; pc: int, proc initialized(code: ControlFlowGraph; pc: int,
init, uninit: var IntSet; until: int): int = init, uninit: var IntSet; until: int): int =