mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
compiler: cleanup dfa.nim
This commit is contained in:
@@ -7,18 +7,22 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Data flow analysis for Nim. For now the task is to prove that every
|
||||
## usage of a local variable 'v' is covered by an initialization to 'v'
|
||||
## first.
|
||||
## Data flow analysis for Nim.
|
||||
## We transform the AST into a linear list of instructions first to
|
||||
## make this easier to handle: There are only 2 different branching
|
||||
## instructions: 'goto X' is an unconditional goto, 'fork X'
|
||||
## is a conditional goto (either the next instruction or 'X' can be
|
||||
## taken). Exhaustive case statements are translated
|
||||
## so that the last branch is transformed into an 'else' branch.
|
||||
## taken). Exhaustive case statements could be translated
|
||||
## so that the last branch is transformed into an 'else' branch, but
|
||||
## this is currently not done.
|
||||
## ``return`` and ``break`` are all covered by 'goto'.
|
||||
## The case to detect is ``use v`` that is not dominated by
|
||||
## a ``def v``.
|
||||
##
|
||||
## Control flow through exception handling:
|
||||
## Contrary to popular belief, exception handling doesn't cause
|
||||
## many problems for this DFA representation, ``raise`` is a statement
|
||||
## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
|
||||
## otherwise it is the same as ``return``.
|
||||
##
|
||||
## The data structures and algorithms used here are inspired by
|
||||
## "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
|
||||
@@ -27,15 +31,11 @@ import ast, astalgo, types, intsets, tables, msgs, options, lineinfos
|
||||
|
||||
type
|
||||
InstrKind* = enum
|
||||
goto, fork, def, use,
|
||||
useWithinCall # this strange special case is used to get more
|
||||
# move optimizations out of regular code
|
||||
# XXX This is still overly pessimistic in
|
||||
# call(let x = foo; bar(x))
|
||||
goto, fork, def, use
|
||||
Instr* = object
|
||||
n*: PNode
|
||||
case kind*: InstrKind
|
||||
of def, use, useWithinCall: sym*: PSym
|
||||
of def, use: sym*: PSym
|
||||
of goto, fork: dest*: int
|
||||
|
||||
ControlFlowGraph* = seq[Instr]
|
||||
@@ -71,7 +71,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||
result.add $c[i].kind
|
||||
result.add "\t"
|
||||
case c[i].kind
|
||||
of def, use, useWithinCall:
|
||||
of def, use:
|
||||
result.add c[i].sym.name.s
|
||||
of goto, fork:
|
||||
result.add "L"
|
||||
@@ -199,6 +199,13 @@ proc genCase(c: var Con; n: PNode) =
|
||||
# L2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
when false:
|
||||
# XXX Exhaustiveness is not yet mapped to the control flow graph as
|
||||
# it seems to offer no benefits for the 'last read of' question.
|
||||
let isExhaustive = skipTypes(n.sons[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString} or
|
||||
lastSon(n).kind == nkElse
|
||||
|
||||
var endings: seq[TPosition] = @[]
|
||||
c.gen(n.sons[0])
|
||||
for i in 1 ..< n.len:
|
||||
@@ -250,10 +257,7 @@ proc genUse(c: var Con; n: PNode) =
|
||||
nkAddr, nkHiddenAddr}:
|
||||
n = n[0]
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
if c.inCall > 0:
|
||||
c.code.add Instr(n: n, kind: useWithinCall, sym: n.sym)
|
||||
else:
|
||||
c.code.add Instr(n: n, kind: use, sym: n.sym)
|
||||
c.code.add Instr(n: n, kind: use, sym: n.sym)
|
||||
|
||||
proc genDef(c: var Con; n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
@@ -345,7 +349,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
|
||||
d[i] = initIntSet()
|
||||
c[i] = initIntSet()
|
||||
case code[i].kind
|
||||
of use, useWithinCall: u[i].incl(code[i].sym.id)
|
||||
of use: u[i].incl(code[i].sym.id)
|
||||
of def: d[i].incl(code[i].sym.id)
|
||||
of fork, goto:
|
||||
let d = i+code[i].dest
|
||||
@@ -407,7 +411,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
|
||||
#if someChange:
|
||||
w.add pc + code[pc].dest
|
||||
inc pc
|
||||
of use, useWithinCall:
|
||||
of use:
|
||||
#if not d[prevPc].missingOrExcl():
|
||||
# someChange = true
|
||||
consuming = code[pc].sym.id
|
||||
@@ -424,7 +428,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
|
||||
# now check the condition we're interested in:
|
||||
for i in 0..<code.len:
|
||||
case code[i].kind
|
||||
of use, useWithinCall:
|
||||
of use:
|
||||
let s = code[i].sym
|
||||
if s.id notin d[i]:
|
||||
localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s)
|
||||
|
||||
Reference in New Issue
Block a user