Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions compiler/aliasanalysis.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ proc skipConvDfa*(n: PNode): PNode =
else: break

proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
let stripped = skipConvDfa(orig)
if stripped.typ != nil and isSinkType(stripped.typ):
var base = stripped
while true:
case base.kind
of nkDotExpr, nkCheckedFieldExpr:
base = skipConvDfa(base[0])
of nkHiddenDeref, nkDerefExpr:
base = skipConvDfa(base[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
base = skipConvDfa(base[1])
of nkObjUpConv, nkObjDownConv:
base = skipConvDfa(base[0])
else:
break
if base.kind == nkSym and base.sym.owner == owner and
{sfGlobal, sfThread, sfCursor} * base.sym.flags == {}:
return true
var n = orig
while true:
case n.kind
Expand Down
190 changes: 179 additions & 11 deletions compiler/dfa.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import ast, lineinfos, renderer, aliasanalysis
import std/private/asciitables
import std/intsets
import std/tables
import std/algorithm

when defined(nimPreviewSlimSystem):
import std/assertions
Expand Down Expand Up @@ -57,6 +59,10 @@ type
blocks: seq[TBlock]
owner: PSym
root: PSym
stateLabels: Table[int, TPosition]
stateFixups: Table[int, seq[TPosition]]
stateCaseDepth: int
stateCaseNextState: int

proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
# for debugging purposes
Expand Down Expand Up @@ -106,12 +112,25 @@ template checkedDistance(dist): int =
proc jmpBack(c: var Con, p = TPosition(0)) =
c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))

proc genStateGoto(c: var Con; target: int) =
if target < 0:
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
else:
let lab = c.stateLabels.getOrDefault(target, TPosition(-1))
if lab.int >= 0:
c.code.add Instr(kind: goto, dest: checkedDistance(lab.int - c.code.len))
else:
let p = c.gotoI()
c.stateFixups.mgetOrPut(target, @[]).add p

proc patch(c: var Con, p: TPosition) =
# patch with current index
c.code[p.int].dest = checkedDistance(c.code.len - p.int)

proc gen(c: var Con; n: PNode)

const stateUnset = high(int)

proc popBlock(c: var Con; oldLen: int) =
var exits: seq[TPosition] = @[]
exits.add c.gotoI()
Expand Down Expand Up @@ -142,9 +161,34 @@ proc genWhile(c: var Con; n: PNode) =
# body
# jmp lab1
# lab2:
proc containsGotoState(n: PNode): bool =
if n.kind == nkGotoState: return true
for i in 0..<n.safeLen:
if containsGotoState(n[i]): return true
result = false
proc isStateAccess(n: PNode): bool =
let it = skipConvDfa(n)
case it.kind
of nkSym:
result = it.sym.name.s == ":state"
of nkDotExpr, nkCheckedFieldExpr:
result = it[1].kind == nkSym and it[1].sym.name.s == ":state"
of nkHiddenDeref:
result = isStateAccess(it[0])
else:
result = false
proc containsStateCase(n: PNode): bool =
if n.kind == nkCaseStmt and n.len > 0 and isStateAccess(n[0]):
return true
for i in 0..<n.safeLen:
if containsStateCase(n[i]): return true
result = false
let lab1 = c.genLabel
withBlock(nil):
if isTrue(n[0]):
if containsStateCase(n[1]) or containsGotoState(n[1]):
# State-machine dispatcher loop; control flow is modeled via the case/goto.
c.gen(n[1])
elif isTrue(n[0]):
c.gen(n[1])
c.jmpBack(lab1)
else:
Expand Down Expand Up @@ -227,15 +271,86 @@ proc genCase(c: var Con; n: PNode) =
c.gen(n[0])
let oldInteresting = c.interestingInstructions
let oldLen = c.code.len
for i in 1..<n.len:
proc isStateAccess(n: PNode): bool =
let it = skipConvDfa(n)
case it.kind
of nkSym:
result = it.sym.name.s == ":state"
of nkDotExpr, nkCheckedFieldExpr:
result = it[1].kind == nkSym and it[1].sym.name.s == ":state"
of nkHiddenDeref:
result = isStateAccess(it[0])
else:
result = false
proc findStateAssignment(n: PNode): int =
var res = stateUnset
proc traverse(n: PNode) =
case n.kind
of nkAsgn, nkFastAsgn:
if isStateAccess(n[0]) and n[1].kind == nkIntLit:
res = n[1].intVal.int
else:
discard
for i in 0..<n.safeLen:
traverse(n[i])
traverse(n)
result = res
let isStateCase = isStateAccess(n[0])
var branchOrder: seq[int] = @[]
if isStateCase:
var labeled: seq[(int, int)] = @[]
var others: seq[int] = @[]
var elseIdx = -1
for i in 1..<n.len:
let it = n[i]
if it.len == 1:
elseIdx = i
else:
var stateId = high(int)
for j in 0..it.len-2:
if it[j].kind == nkIntLit:
stateId = it[j].intVal.int
break
if stateId != high(int):
labeled.add (stateId, i)
else:
others.add i
labeled.sort(proc (a, b: (int, int)): int = cmp(a[0], b[0]))
for it in labeled: branchOrder.add it[1]
for i in others: branchOrder.add i
if elseIdx >= 0: branchOrder.add elseIdx
else:
for i in 1..<n.len: branchOrder.add i

for i in branchOrder:
let it = n[i]
if isStateCase and it.len >= 2:
for j in 0..it.len-2:
if it[j].kind == nkIntLit:
let stateId = it[j].intVal.int
let lab = c.genLabel
c.stateLabels[stateId] = lab
if c.stateFixups.hasKey(stateId):
for p in c.stateFixups[stateId]:
c.code[p.int].dest = checkedDistance(lab.int - p.int)
c.stateFixups.del(stateId)
break
let oldDepth = c.stateCaseDepth
let oldNext = c.stateCaseNextState
if isStateCase:
c.stateCaseDepth = oldDepth + 1
c.stateCaseNextState = findStateAssignment(it.lastSon)
if it.len == 1 or (i == n.len-1 and isExhaustive):
# treat the last branch as 'else' if this is an exhaustive case statement.
c.gen(it.lastSon)
else:
forkT:
c.gen(it.lastSon)
endings.add c.gotoI()
if isStateCase:
c.stateCaseDepth = oldDepth
c.stateCaseNextState = oldNext
discard

if oldInteresting == c.interestingInstructions:
setLen c.code, oldLen
Expand All @@ -247,6 +362,17 @@ proc genBlock(c: var Con; n: PNode) =
withBlock(n[0].sym):
c.gen(n[1])

proc genGotoState(c: var Con; n: PNode) =
if n.len == 0:
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
return
if n[0].kind == nkIntLit:
let target = n[0].intVal.int
c.genStateGoto(target)
else:
# Dynamic target; be conservative and assume a loop.
c.jmpBack(TPosition(0))

proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
let lab1 = c.gotoI()
if c.blocks[i].isTryBlock:
Expand All @@ -263,6 +389,11 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =

proc genBreak(c: var Con; n: PNode) =
inc c.interestingInstructions
if c.stateCaseDepth > 0 and n[0].kind == nkSym and
n[0].sym.name.s == ":stateLoop" and
c.stateCaseNextState != stateUnset:
c.genStateGoto(c.stateCaseNextState)
return
if n[0].kind == nkSym:
for i in countdown(c.blocks.high, 0):
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
Expand Down Expand Up @@ -329,7 +460,13 @@ proc genReturn(c: var Con; n: PNode) =
gen(c, n[0])
else:
genImplicitReturn(c)
genBreakOrRaiseAux(c, 0, n)
if c.stateCaseDepth > 0 and c.stateCaseNextState != stateUnset and
c.stateCaseNextState >= 0:
forkT:
genBreakOrRaiseAux(c, 0, n)
c.genStateGoto(c.stateCaseNextState)
else:
genBreakOrRaiseAux(c, 0, n)

const
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
Expand All @@ -347,23 +484,43 @@ proc skipTrivials(c: var Con, n: PNode): PNode =
result = result[1]
else: break

proc matchesFieldAccess(orig: PNode; field: PSym): bool =
result = false
var n = skipConvDfa(orig)
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr:
if n[1].kind == nkSym and n[1].sym == field:
result = true
break
n = skipConvDfa(n[0])
else:
break

proc genUse(c: var Con; orig: PNode) =
let n = c.skipTrivials(orig)

if n.kind == nkSym:
if c.root.kind == skField and matchesFieldAccess(orig, c.root):
c.code.add Instr(kind: use, n: orig)
inc c.interestingInstructions
elif n.kind == nkSym:
if n.sym.kind in InterestingSyms and n.sym == c.root:
c.code.add Instr(kind: use, n: orig)
inc c.interestingInstructions
else:
discard
else:
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:
if n.sym == c.root:
c.code.add Instr(kind: def, n: orig)
inc c.interestingInstructions
if c.root.kind == skField and matchesFieldAccess(orig, c.root):
c.code.add Instr(kind: def, n: orig)
inc c.interestingInstructions
elif n.kind == nkSym and n.sym.kind in InterestingSyms and n.sym == c.root:
c.code.add Instr(kind: def, n: orig)
inc c.interestingInstructions

proc genCall(c: var Con; n: PNode) =
gen(c, n[0])
Expand Down Expand Up @@ -448,9 +605,16 @@ proc gen(c: var Con; n: PNode) =
of nkRaiseStmt: genRaise(c, n)
of nkBreakStmt: genBreak(c, n)
of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
of nkStmtList, nkStmtListExpr:
for x in n: gen(c, x)
of nkYieldStmt:
forkT:
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
of nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
for x in n: gen(c, x)
of nkGotoState:
genGotoState(c, n)
of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
gen(c, n[0])
Expand Down Expand Up @@ -478,7 +642,11 @@ when false:

proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
## constructs a control flow graph for ``body``.
var c = Con(code: @[], blocks: @[], owner: s, root: root)
var c = Con(code: @[], blocks: @[], owner: s, root: root,
stateLabels: initTable[int, TPosition](),
stateFixups: initTable[int, seq[TPosition]](),
stateCaseDepth: 0,
stateCaseNextState: stateUnset)
withBlock(s):
gen(c, body)
if root.kind == skResult:
Expand Down
Loading
Loading