New "ping-pong" DFA (#14322)
* New ping-pong analysis * Add testcase for #13456 * Remove debugging leftover * Unquote "unstructured controlflow" * Fix typo * Fix exponential complexity in edge cases * Add sanity testcase * Fix
This commit is contained in:
parent
13cfaf5fd5
commit
9f78f116b2
3 changed files with 163 additions and 73 deletions
|
|
@ -35,11 +35,11 @@ from patterns import sameTrees
|
||||||
|
|
||||||
type
|
type
|
||||||
InstrKind* = enum
|
InstrKind* = enum
|
||||||
goto, fork, join, def, use
|
goto, fork, def, use
|
||||||
Instr* = object
|
Instr* = object
|
||||||
n*: PNode # contains the def/use location.
|
n*: PNode # contains the def/use location.
|
||||||
case kind*: InstrKind
|
case kind*: InstrKind
|
||||||
of goto, fork, join: dest*: int
|
of goto, fork: dest*: int
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
ControlFlowGraph* = seq[Instr]
|
ControlFlowGraph* = seq[Instr]
|
||||||
|
|
@ -47,8 +47,6 @@ type
|
||||||
TPosition = distinct int
|
TPosition = distinct int
|
||||||
|
|
||||||
TBlock = object
|
TBlock = object
|
||||||
joins: seq[Instr]
|
|
||||||
forks: seq[TPosition]
|
|
||||||
case isTryBlock: bool
|
case isTryBlock: bool
|
||||||
of false:
|
of false:
|
||||||
label: PSym
|
label: PSym
|
||||||
|
|
@ -72,7 +70,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||||
var jumpTargets = initIntSet()
|
var jumpTargets = initIntSet()
|
||||||
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
||||||
for i in start..last:
|
for i in start..last:
|
||||||
if c[i].kind in {goto, fork, join}:
|
if c[i].kind in {goto, fork}:
|
||||||
jumpTargets.incl(i+c[i].dest)
|
jumpTargets.incl(i+c[i].dest)
|
||||||
var i = start
|
var i = start
|
||||||
while i <= last:
|
while i <= last:
|
||||||
|
|
@ -83,7 +81,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||||
case c[i].kind
|
case c[i].kind
|
||||||
of def, use:
|
of def, use:
|
||||||
result.add renderTree(c[i].n)
|
result.add renderTree(c[i].n)
|
||||||
of goto, fork, join:
|
of goto, fork:
|
||||||
result.add "L"
|
result.add "L"
|
||||||
result.addInt c[i].dest+i
|
result.addInt c[i].dest+i
|
||||||
result.add("\t#")
|
result.add("\t#")
|
||||||
|
|
@ -102,7 +100,6 @@ proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
|
||||||
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)
|
||||||
c.code.add Instr(n: n, kind: fork, dest: 0)
|
c.code.add Instr(n: n, kind: fork, dest: 0)
|
||||||
c.blocks[^1].forks.add result
|
|
||||||
|
|
||||||
proc gotoI(c: var Con; n: PNode): TPosition =
|
proc gotoI(c: var Con; n: PNode): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
|
|
@ -110,6 +107,24 @@ proc gotoI(c: var Con; n: PNode): TPosition =
|
||||||
|
|
||||||
#[
|
#[
|
||||||
|
|
||||||
|
Join is no more
|
||||||
|
===============
|
||||||
|
Instead of generating join instructions we adapt our traversal of the CFG.
|
||||||
|
|
||||||
|
When encountering a fork we split into two paths, we follow the path
|
||||||
|
starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
|
||||||
|
If we encounter gotos that would jump further than the current joinpoint,
|
||||||
|
as can happen with gotos generated by unstructured controlflow such as break, raise or return,
|
||||||
|
we simply suspend following the current path, and follow the other path until the new joinpoint
|
||||||
|
which is simply the instruction pointer returned to us by the now suspended path.
|
||||||
|
If the path we are following now, also encounters a goto that exceeds the joinpoint
|
||||||
|
we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
|
||||||
|
If we eventually reach a common joinpoint we join the two paths.
|
||||||
|
This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
|
||||||
|
cutting down on the CFG size but is also mandatory for correctly handling complicated cases
|
||||||
|
of unstructured controlflow.
|
||||||
|
|
||||||
|
|
||||||
Design of join
|
Design of join
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
|
@ -258,10 +273,6 @@ duplicate the 'join' instructions on breaks and return exits!
|
||||||
|
|
||||||
]#
|
]#
|
||||||
|
|
||||||
proc joinI(c: var Con; fromFork: TPosition; n: PNode) =
|
|
||||||
let dist = fromFork.int - c.code.len
|
|
||||||
c.code.add Instr(n: n, kind: join, dest: dist)
|
|
||||||
|
|
||||||
proc genLabel(c: Con): TPosition =
|
proc genLabel(c: Con): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
|
|
||||||
|
|
@ -289,10 +300,6 @@ proc popBlock(c: var Con; oldLen: int) =
|
||||||
exits.add c.gotoI(newNode(nkEmpty))
|
exits.add c.gotoI(newNode(nkEmpty))
|
||||||
for e in exits:
|
for e in exits:
|
||||||
c.patch e
|
c.patch e
|
||||||
for j in c.blocks[oldLen].joins:
|
|
||||||
var patchedJ = j
|
|
||||||
patchedJ.dest -= c.code.len
|
|
||||||
c.code.add patchedJ
|
|
||||||
c.blocks.setLen(oldLen)
|
c.blocks.setLen(oldLen)
|
||||||
|
|
||||||
template withBlock(labl: PSym; body: untyped) {.dirty.} =
|
template withBlock(labl: PSym; body: untyped) {.dirty.} =
|
||||||
|
|
@ -353,7 +360,6 @@ when true:
|
||||||
c.gen(n[1])
|
c.gen(n[1])
|
||||||
else:
|
else:
|
||||||
withBlock(nil):
|
withBlock(nil):
|
||||||
let oldForksLen = c.blocks[^1].forks.len
|
|
||||||
var endings: array[3, TPosition]
|
var endings: array[3, TPosition]
|
||||||
for i in 0..2:
|
for i in 0..2:
|
||||||
c.gen(n[0])
|
c.gen(n[0])
|
||||||
|
|
@ -362,8 +368,6 @@ when true:
|
||||||
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)
|
||||||
c.joinI(c.blocks[^1].forks.pop(), n)
|
|
||||||
doAssert(c.blocks[^1].forks.len == oldForksLen)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
|
@ -374,7 +378,6 @@ else:
|
||||||
# body
|
# body
|
||||||
# jmp lab1
|
# jmp lab1
|
||||||
# lab2:
|
# lab2:
|
||||||
let oldForksLen = c.blocks[^1].forks.len
|
|
||||||
let lab1 = c.genLabel
|
let lab1 = c.genLabel
|
||||||
withBlock(nil):
|
withBlock(nil):
|
||||||
if isTrue(n[0]):
|
if isTrue(n[0]):
|
||||||
|
|
@ -386,15 +389,11 @@ else:
|
||||||
c.gen(n[1])
|
c.gen(n[1])
|
||||||
c.jmpBack(n, lab1)
|
c.jmpBack(n, lab1)
|
||||||
c.patch(lab2)
|
c.patch(lab2)
|
||||||
setLen(c.blocks[^1].forks, oldForksLen)
|
|
||||||
|
|
||||||
template forkT(n, body) =
|
template forkT(n, body) =
|
||||||
let oldLen = c.blocks[^1].forks.len
|
|
||||||
let lab1 = c.forkI(n)
|
let lab1 = c.forkI(n)
|
||||||
body
|
body
|
||||||
c.patch(lab1)
|
c.patch(lab1)
|
||||||
c.joinI(lab1, n)
|
|
||||||
setLen(c.blocks[^1].forks, oldLen)
|
|
||||||
|
|
||||||
proc genIf(c: var Con, n: PNode) =
|
proc genIf(c: var Con, n: PNode) =
|
||||||
#[
|
#[
|
||||||
|
|
@ -433,7 +432,6 @@ proc genIf(c: var Con, n: PNode) =
|
||||||
join F1
|
join F1
|
||||||
|
|
||||||
]#
|
]#
|
||||||
let oldLen = c.blocks[^1].forks.len
|
|
||||||
var endings: seq[TPosition] = @[]
|
var endings: seq[TPosition] = @[]
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
var it = n[i]
|
var it = n[i]
|
||||||
|
|
@ -446,8 +444,6 @@ proc genIf(c: var Con, n: PNode) =
|
||||||
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)
|
||||||
c.joinI(c.blocks[^1].forks.pop(), n)
|
|
||||||
doAssert(c.blocks[^1].forks.len == oldLen)
|
|
||||||
|
|
||||||
proc genAndOr(c: var Con; n: PNode) =
|
proc genAndOr(c: var Con; n: PNode) =
|
||||||
# asgn dest, a
|
# asgn dest, a
|
||||||
|
|
@ -474,7 +470,6 @@ proc genCase(c: var Con; n: PNode) =
|
||||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||||
|
|
||||||
var endings: seq[TPosition] = @[]
|
var endings: seq[TPosition] = @[]
|
||||||
let oldLen = c.blocks[^1].forks.len
|
|
||||||
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]
|
||||||
|
|
@ -491,8 +486,6 @@ proc genCase(c: var Con; n: PNode) =
|
||||||
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)
|
||||||
c.joinI(c.blocks[^1].forks.pop(), n)
|
|
||||||
doAssert(c.blocks[^1].forks.len == oldLen)
|
|
||||||
|
|
||||||
proc genBlock(c: var Con; n: PNode) =
|
proc genBlock(c: var Con; n: PNode) =
|
||||||
withBlock(n[0].sym):
|
withBlock(n[0].sym):
|
||||||
|
|
@ -511,10 +504,6 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||||
|
|
||||||
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
||||||
|
|
||||||
for b in countdown(c.blocks.high, i):
|
|
||||||
for f in countdown(c.blocks[b].forks.high, 0):
|
|
||||||
c.blocks[i].joins.add Instr(n: n, kind: join, dest: c.blocks[b].forks[f].int)
|
|
||||||
|
|
||||||
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)
|
#echo cast[int](n[0].sym)
|
||||||
|
|
@ -530,7 +519,6 @@ proc genBreak(c: var Con; n: PNode) =
|
||||||
return
|
return
|
||||||
|
|
||||||
proc genTry(c: var Con; n: PNode) =
|
proc genTry(c: var Con; n: PNode) =
|
||||||
let oldForksLen = c.blocks[^1].forks.len
|
|
||||||
var endings: seq[TPosition] = @[]
|
var endings: seq[TPosition] = @[]
|
||||||
|
|
||||||
let oldLen = c.blocks.len
|
let oldLen = c.blocks.len
|
||||||
|
|
@ -543,10 +531,6 @@ proc genTry(c: var Con; n: PNode) =
|
||||||
|
|
||||||
for f in c.blocks[oldLen].raiseFixups:
|
for f in c.blocks[oldLen].raiseFixups:
|
||||||
c.patch(f)
|
c.patch(f)
|
||||||
for j in c.blocks[oldLen].joins:
|
|
||||||
var patchedJ = j
|
|
||||||
patchedJ.dest -= c.code.len
|
|
||||||
c.code.add patchedJ
|
|
||||||
|
|
||||||
c.blocks.setLen oldLen
|
c.blocks.setLen oldLen
|
||||||
|
|
||||||
|
|
@ -561,7 +545,6 @@ proc genTry(c: var Con; n: PNode) =
|
||||||
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)
|
||||||
c.joinI(c.blocks[^1].forks.pop(), n)
|
|
||||||
|
|
||||||
# join the 'elsePos' forkI instruction:
|
# join the 'elsePos' forkI instruction:
|
||||||
#c.joinI(c.blocks[^1].forks.pop(), n)
|
#c.joinI(c.blocks[^1].forks.pop(), n)
|
||||||
|
|
@ -569,7 +552,6 @@ proc genTry(c: var Con; n: PNode) =
|
||||||
let fin = lastSon(n)
|
let fin = lastSon(n)
|
||||||
if fin.kind == nkFinally:
|
if fin.kind == nkFinally:
|
||||||
c.gen(fin[0])
|
c.gen(fin[0])
|
||||||
doAssert(c.blocks[^1].forks.len == oldForksLen)
|
|
||||||
|
|
||||||
template genNoReturn(c: var Con; n: PNode) =
|
template genNoReturn(c: var Con; n: PNode) =
|
||||||
# leave the graph
|
# leave the graph
|
||||||
|
|
@ -751,7 +733,6 @@ proc genCall(c: var Con; n: PNode) =
|
||||||
genBreakOrRaiseAux(c, i, n)
|
genBreakOrRaiseAux(c, i, n)
|
||||||
break
|
break
|
||||||
c.patch(endGoto)
|
c.patch(endGoto)
|
||||||
c.joinI(c.blocks[^1].forks.pop(), n)
|
|
||||||
dec c.inCall
|
dec c.inCall
|
||||||
|
|
||||||
proc genMagic(c: var Con; n: PNode; m: TMagic) =
|
proc genMagic(c: var Con; n: PNode; m: TMagic) =
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,9 @@ 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): PNode
|
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
|
||||||
|
|
||||||
proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int =
|
proc isLastRead(location: PNode; c: var Con; pc, until: int): int =
|
||||||
var pc = pc
|
var pc = pc
|
||||||
while pc < c.g.len:
|
while pc < c.g.len and pc < until:
|
||||||
case c.g[pc].kind
|
case c.g[pc].kind
|
||||||
of def:
|
of def:
|
||||||
if defInstrTargets(c.g[pc], location):
|
if defInstrTargets(c.g[pc], location):
|
||||||
|
|
@ -83,15 +83,17 @@ proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int =
|
||||||
pc = pc + c.g[pc].dest
|
pc = pc + c.g[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:
|
||||||
let variantA = isLastRead(location, c, pc+1, pc)
|
var variantA = pc + 1
|
||||||
if variantA < 0: return -1
|
var variantB = pc + c.g[pc].dest
|
||||||
var variantB = isLastRead(location, c, pc + c.g[pc].dest, pc)
|
while variantA != variantB:
|
||||||
if variantB < 0: return -1
|
if min(variantA, variantB) < 0: return -1
|
||||||
|
if max(variantA, variantB) >= c.g.len or min(variantA, variantB) >= until:
|
||||||
|
break
|
||||||
|
if variantA < variantB:
|
||||||
|
variantA = isLastRead(location, c, variantA, min(variantB, until))
|
||||||
|
else:
|
||||||
|
variantB = isLastRead(location, c, variantB, min(variantA, until))
|
||||||
pc = min(variantA, variantB)
|
pc = min(variantA, variantB)
|
||||||
of InstrKind.join:
|
|
||||||
let dest = pc + c.g[pc].dest
|
|
||||||
if dest == comesFrom: return pc + 1
|
|
||||||
inc pc
|
|
||||||
return pc
|
return pc
|
||||||
|
|
||||||
proc isLastRead(n: PNode; c: var Con): bool =
|
proc isLastRead(n: PNode; c: var Con): bool =
|
||||||
|
|
@ -114,12 +116,12 @@ 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, -1) >= 0
|
result = isLastRead(n, c, 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, comesFrom: int; instr: int): int =
|
proc isFirstWrite(location: PNode; c: var Con; pc, until: int): int =
|
||||||
var pc = pc
|
var pc = pc
|
||||||
while pc < instr:
|
while pc < until:
|
||||||
case c.g[pc].kind
|
case c.g[pc].kind
|
||||||
of def:
|
of def:
|
||||||
if defInstrTargets(c.g[pc], location):
|
if defInstrTargets(c.g[pc], location):
|
||||||
|
|
@ -134,15 +136,17 @@ proc isFirstWrite(location: PNode; c: var Con; pc, comesFrom: int; instr: int):
|
||||||
pc = pc + c.g[pc].dest
|
pc = pc + c.g[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:
|
||||||
let variantA = isFirstWrite(location, c, pc+1, pc, instr)
|
var variantA = pc + 1
|
||||||
if variantA < 0: return -1
|
var variantB = pc + c.g[pc].dest
|
||||||
var variantB = isFirstWrite(location, c, pc + c.g[pc].dest, pc, instr + c.g[pc].dest)
|
while variantA != variantB:
|
||||||
if variantB < 0: return -1
|
if min(variantA, variantB) < 0: return -1
|
||||||
|
if max(variantA, variantB) > until:
|
||||||
|
break
|
||||||
|
if variantA < variantB:
|
||||||
|
variantA = isFirstWrite(location, c, variantA, min(variantB, until))
|
||||||
|
else:
|
||||||
|
variantB = isFirstWrite(location, c, variantB, min(variantA, until))
|
||||||
pc = min(variantA, variantB)
|
pc = min(variantA, variantB)
|
||||||
of InstrKind.join:
|
|
||||||
let dest = pc + c.g[pc].dest
|
|
||||||
if dest == comesFrom: return pc + 1
|
|
||||||
inc pc
|
|
||||||
return pc
|
return pc
|
||||||
|
|
||||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||||
|
|
@ -161,10 +165,10 @@ 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, -1, instr) >= 0
|
result = isFirstWrite(n, c, 0, instr) >= 0
|
||||||
|
|
||||||
proc initialized(code: ControlFlowGraph; pc: int,
|
proc initialized(code: ControlFlowGraph; pc: int,
|
||||||
init, uninit: var IntSet; comesFrom: int): int =
|
init, uninit: var IntSet; until: int): int =
|
||||||
## Computes the set of definitely initialized variables across all code paths
|
## Computes the set of definitely initialized variables across all code paths
|
||||||
## as an IntSet of IDs.
|
## as an IntSet of IDs.
|
||||||
var pc = pc
|
var pc = pc
|
||||||
|
|
@ -173,20 +177,22 @@ proc initialized(code: ControlFlowGraph; pc: int,
|
||||||
of goto:
|
of goto:
|
||||||
pc = pc + code[pc].dest
|
pc = pc + code[pc].dest
|
||||||
of fork:
|
of fork:
|
||||||
let target = pc + code[pc].dest
|
|
||||||
var initA = initIntSet()
|
var initA = initIntSet()
|
||||||
var initB = initIntSet()
|
var initB = initIntSet()
|
||||||
let pcA = initialized(code, pc+1, initA, uninit, pc)
|
var variantA = pc + 1
|
||||||
discard initialized(code, target, initB, uninit, pc)
|
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:
|
# we add vars if they are in both branches:
|
||||||
for v in initA:
|
for v in initA:
|
||||||
if v in initB:
|
if v in initB:
|
||||||
init.incl v
|
init.incl v
|
||||||
pc = pcA+1
|
|
||||||
of InstrKind.join:
|
|
||||||
let target = pc + code[pc].dest
|
|
||||||
if comesFrom == target: return pc
|
|
||||||
inc pc
|
|
||||||
of use:
|
of use:
|
||||||
let v = code[pc].n.sym
|
let v = code[pc].n.sym
|
||||||
if v.kind != skParam and v.id notin init:
|
if v.kind != skParam and v.id notin init:
|
||||||
|
|
@ -1040,7 +1046,7 @@ proc computeUninit(c: var Con) =
|
||||||
c.uninitComputed = true
|
c.uninitComputed = true
|
||||||
c.uninit = initIntSet()
|
c.uninit = initIntSet()
|
||||||
var init = initIntSet()
|
var init = initIntSet()
|
||||||
discard initialized(c.g, pc = 0, init, c.uninit, comesFrom = -1)
|
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
|
||||||
|
|
||||||
proc injectDefaultCalls(n: PNode, c: var Con) =
|
proc injectDefaultCalls(n: PNode, c: var Con) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,23 @@ c.text = hello
|
||||||
c.text = hello
|
c.text = hello
|
||||||
pOD.text = hello
|
pOD.text = hello
|
||||||
pOD.toks = @["hello"]
|
pOD.toks = @["hello"]
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
mmm
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
3
|
||||||
|
mmm
|
||||||
|
sink me (sink)
|
||||||
|
assign me (not sink)
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -199,3 +216,89 @@ when false:
|
||||||
let pOHD = parseOHD()
|
let pOHD = parseOHD()
|
||||||
dump pOHD.text
|
dump pOHD.text
|
||||||
dump pOHD.toks
|
dump pOHD.toks
|
||||||
|
|
||||||
|
# bug #13456
|
||||||
|
|
||||||
|
iterator combinations[T](s: openarray[T], k: int): seq[T] =
|
||||||
|
let n = len(s)
|
||||||
|
assert k >= 0 and k <= n
|
||||||
|
var pos = newSeq[int](k)
|
||||||
|
var current = newSeq[T](k)
|
||||||
|
for i in 0..k-1:
|
||||||
|
pos[k-i-1] = i
|
||||||
|
var done = false
|
||||||
|
while not done:
|
||||||
|
for i in 0..k-1:
|
||||||
|
current[i] = s[pos[k-i-1]]
|
||||||
|
yield current
|
||||||
|
var i = 0
|
||||||
|
while i < k:
|
||||||
|
pos[i] += 1
|
||||||
|
if pos[i] < n-i:
|
||||||
|
for j in 0..i-1:
|
||||||
|
pos[j] = pos[i] + i - j
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
if i >= k:
|
||||||
|
break
|
||||||
|
|
||||||
|
type
|
||||||
|
UndefEx = object of Exception
|
||||||
|
|
||||||
|
proc main2 =
|
||||||
|
var delayedSyms = @[1, 2, 3]
|
||||||
|
var unp: seq[int]
|
||||||
|
block myb:
|
||||||
|
for a in 1 .. 2:
|
||||||
|
if delayedSyms.len > a:
|
||||||
|
unp = delayedSyms
|
||||||
|
for t in unp.combinations(a + 1):
|
||||||
|
try:
|
||||||
|
var h = false
|
||||||
|
for k in t:
|
||||||
|
echo "fff"
|
||||||
|
if h: continue
|
||||||
|
if true:
|
||||||
|
raise newException(UndefEx, "forward declaration")
|
||||||
|
break myb
|
||||||
|
except UndefEx:
|
||||||
|
echo t.len
|
||||||
|
echo "mmm"
|
||||||
|
|
||||||
|
main2()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type ME = object
|
||||||
|
who: string
|
||||||
|
|
||||||
|
proc `=`(x: var ME, y: ME) =
|
||||||
|
if x.who.len > 0: echo "assign ",x.who
|
||||||
|
|
||||||
|
proc `=sink`(x: var ME, y: ME) =
|
||||||
|
if x.who.len > 0: echo "sink ",x.who
|
||||||
|
|
||||||
|
var dump: ME
|
||||||
|
template use(x) = dump = x
|
||||||
|
template def(x) = x = dump
|
||||||
|
|
||||||
|
var c = true
|
||||||
|
|
||||||
|
proc shouldSink() =
|
||||||
|
var x = ME(who: "me (sink)")
|
||||||
|
use(x) # we analyse this
|
||||||
|
if c: def(x)
|
||||||
|
else: def(x)
|
||||||
|
use(x) # ok, with the [else] part.
|
||||||
|
|
||||||
|
shouldSink()
|
||||||
|
|
||||||
|
dump = ME()
|
||||||
|
|
||||||
|
proc shouldNotSink() =
|
||||||
|
var x = ME(who: "me (not sink)")
|
||||||
|
use(x) # we analyse this
|
||||||
|
if c: def(x)
|
||||||
|
use(x) # Not ok without the '[else]'
|
||||||
|
|
||||||
|
shouldNotSink()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue