diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2018-10-12 19:56:51 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-10-12 19:56:51 +0200 |
commit | 2fecf4f36a39049e2d21065929f44dff38cf7d5a (patch) | |
tree | 09b0f093aa7e2f2d004c19f87f38e0f4d064a790 /compiler/dfa.nim | |
parent | dcc3ac74f46cb25c1088059a38371a95fcc914c1 (diff) | |
download | Nim-2fecf4f36a39049e2d21065929f44dff38cf7d5a.tar.gz |
compiler: cleanup dfa.nim
Diffstat (limited to 'compiler/dfa.nim')
-rw-r--r-- | compiler/dfa.nim | 46 |
1 files changed, 25 insertions, 21 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 44c89b881..016b4a5d1 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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) |