dfa.nim 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Data flow analysis for Nim.
  10. ## We transform the AST into a linear list of instructions first to
  11. ## make this easier to handle: There are only 2 different branching
  12. ## instructions: 'goto X' is an unconditional goto, 'fork X'
  13. ## is a conditional goto (either the next instruction or 'X' can be
  14. ## taken). Exhaustive case statements are translated
  15. ## so that the last branch is transformed into an 'else' branch.
  16. ## ``return`` and ``break`` are all covered by 'goto'.
  17. ##
  18. ## Control flow through exception handling:
  19. ## Contrary to popular belief, exception handling doesn't cause
  20. ## many problems for this DFA representation, ``raise`` is a statement
  21. ## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
  22. ## otherwise it is the same as ``return``. Every call is treated as
  23. ## a call that can potentially ``raise``. However, without a surrounding
  24. ## ``try`` we don't emit these ``fork ReturnLabel`` instructions in order
  25. ## to speed up the dataflow analysis passes.
  26. ##
  27. ## The data structures and algorithms used here are inspired by
  28. ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
  29. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
  30. import ast, types, intsets, lineinfos, renderer, asciitables
  31. from patterns import sameTrees
  32. type
  33. InstrKind* = enum
  34. goto, fork, def, use
  35. Instr* = object
  36. n*: PNode # contains the def/use location.
  37. case kind*: InstrKind
  38. of goto, fork: dest*: int
  39. else: discard
  40. ControlFlowGraph* = seq[Instr]
  41. TPosition = distinct int
  42. TBlock = object
  43. case isTryBlock: bool
  44. of false:
  45. label: PSym
  46. breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
  47. of true:
  48. finale: PNode
  49. raiseFixups: seq[TPosition] #Contains the gotos for the raises
  50. Con = object
  51. code: ControlFlowGraph
  52. inTryStmt: int
  53. blocks: seq[TBlock]
  54. owner: PSym
  55. proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
  56. # for debugging purposes
  57. # first iteration: compute all necessary labels:
  58. var jumpTargets = initIntSet()
  59. let last = if last < 0: c.len-1 else: min(last, c.len-1)
  60. for i in start..last:
  61. if c[i].kind in {goto, fork}:
  62. jumpTargets.incl(i+c[i].dest)
  63. var i = start
  64. while i <= last:
  65. if i in jumpTargets: result.add("L" & $i & ":\n")
  66. result.add "\t"
  67. result.add ($i & " " & $c[i].kind)
  68. result.add "\t"
  69. case c[i].kind
  70. of def, use:
  71. result.add renderTree(c[i].n)
  72. of goto, fork:
  73. result.add "L"
  74. result.addInt c[i].dest+i
  75. result.add("\t#")
  76. result.add($c[i].n.info.line)
  77. result.add("\n")
  78. inc i
  79. if i in jumpTargets: result.add("L" & $i & ": End\n")
  80. proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
  81. ## echos the ControlFlowGraph for debugging purposes.
  82. echo codeListing(c, start, last).alignTable
  83. proc forkI(c: var Con; n: PNode): TPosition =
  84. result = TPosition(c.code.len)
  85. c.code.add Instr(n: n, kind: fork, dest: 0)
  86. proc gotoI(c: var Con; n: PNode): TPosition =
  87. result = TPosition(c.code.len)
  88. c.code.add Instr(n: n, kind: goto, dest: 0)
  89. #[
  90. Join is no more
  91. ===============
  92. Instead of generating join instructions we adapt our traversal of the CFG.
  93. When encountering a fork we split into two paths, we follow the path
  94. starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
  95. If we encounter gotos that would jump further than the current joinpoint,
  96. as can happen with gotos generated by unstructured controlflow such as break, raise or return,
  97. we simply suspend following the current path, and follow the other path until the new joinpoint
  98. which is simply the instruction pointer returned to us by the now suspended path.
  99. If the path we are following now, also encounters a goto that exceeds the joinpoint
  100. we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
  101. If we eventually reach a common joinpoint we join the two paths.
  102. This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
  103. cutting down on the CFG size but is also mandatory for correctly handling complicated cases
  104. of unstructured controlflow.
  105. Design of join
  106. ==============
  107. block:
  108. if cond: break
  109. def(x)
  110. use(x)
  111. Generates:
  112. L0: fork lab1
  113. join L0 # patched.
  114. goto Louter
  115. lab1:
  116. def x
  117. join L0
  118. Louter:
  119. use x
  120. block outer:
  121. while a:
  122. while b:
  123. if foo:
  124. if bar:
  125. break outer # --> we need to 'join' every pushed 'fork' here
  126. This works and then our abstract interpretation needs to deal with 'fork'
  127. differently. It really causes a split in execution. Two threads are
  128. "spawned" and both need to reach the 'join L' instruction. Afterwards
  129. the abstract interpretations are joined and execution resumes single
  130. threaded.
  131. Abstract Interpretation
  132. -----------------------
  133. proc interpret(pc, state, comesFrom): state =
  134. result = state
  135. # we need an explicit 'create' instruction (an explicit heap), in order
  136. # to deal with 'var x = create(); var y = x; var z = y; destroy(z)'
  137. while true:
  138. case pc
  139. of fork:
  140. let a = interpret(pc+1, result, pc)
  141. let b = interpret(forkTarget, result, pc)
  142. result = a ++ b # ++ is a union operation
  143. inc pc
  144. of join:
  145. if joinTarget == comesFrom: return result
  146. else: inc pc
  147. of use X:
  148. if not result.contains(x):
  149. error "variable not initialized " & x
  150. inc pc
  151. of def X:
  152. if not result.contains(x):
  153. result.incl X
  154. else:
  155. error "overwrite of variable causes memory leak " & x
  156. inc pc
  157. of destroy X:
  158. result.excl X
  159. This is correct but still can lead to false positives:
  160. proc p(cond: bool) =
  161. if cond:
  162. new(x)
  163. otherThings()
  164. if cond:
  165. destroy x
  166. Is not a leak. We should find a way to model *data* flow, not just
  167. control flow. One solution is to rewrite the 'if' without a fork
  168. instruction. The unstructured aspect can now be easily dealt with
  169. the 'goto' and 'join' instructions.
  170. proc p(cond: bool) =
  171. L0: fork Lend
  172. new(x)
  173. # do not 'join' here!
  174. Lend:
  175. otherThings()
  176. join L0 # SKIP THIS FOR new(x) SOMEHOW
  177. destroy x
  178. join L0 # but here.
  179. But if we follow 'goto Louter' we will never come to the join point.
  180. We restore the bindings after popping pc from the stack then there
  181. "no" problem?!
  182. while cond:
  183. prelude()
  184. if not condB: break
  185. postlude()
  186. --->
  187. var setFlag = true
  188. while cond and not setFlag:
  189. prelude()
  190. if not condB:
  191. setFlag = true # BUT: Dependency
  192. if not setFlag: # HERE
  193. postlude()
  194. --->
  195. var setFlag = true
  196. while cond and not setFlag:
  197. prelude()
  198. if not condB:
  199. postlude()
  200. setFlag = true
  201. -------------------------------------------------
  202. while cond:
  203. prelude()
  204. if more:
  205. if not condB: break
  206. stuffHere()
  207. postlude()
  208. -->
  209. var setFlag = true
  210. while cond and not setFlag:
  211. prelude()
  212. if more:
  213. if not condB:
  214. setFlag = false
  215. else:
  216. stuffHere()
  217. postlude()
  218. else:
  219. postlude()
  220. This is getting complicated. Instead we keep the whole 'join' idea but
  221. duplicate the 'join' instructions on breaks and return exits!
  222. ]#
  223. proc genLabel(c: Con): TPosition = TPosition(c.code.len)
  224. template checkedDistance(dist): int =
  225. doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
  226. dist
  227. proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
  228. c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
  229. proc patch(c: var Con, p: TPosition) =
  230. # patch with current index
  231. c.code[p.int].dest = checkedDistance(c.code.len - p.int)
  232. proc gen(c: var Con; n: PNode)
  233. proc popBlock(c: var Con; oldLen: int) =
  234. var exits: seq[TPosition]
  235. exits.add c.gotoI(newNode(nkEmpty))
  236. for f in c.blocks[oldLen].breakFixups:
  237. c.patch(f[0])
  238. for finale in f[1]:
  239. c.gen(finale)
  240. exits.add c.gotoI(newNode(nkEmpty))
  241. for e in exits:
  242. c.patch e
  243. c.blocks.setLen(oldLen)
  244. template withBlock(labl: PSym; body: untyped) =
  245. let oldLen = c.blocks.len
  246. c.blocks.add TBlock(isTryBlock: false, label: labl)
  247. body
  248. popBlock(c, oldLen)
  249. proc isTrue(n: PNode): bool =
  250. n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
  251. n.kind == nkIntLit and n.intVal != 0
  252. when true:
  253. proc genWhile(c: var Con; n: PNode) =
  254. # We unroll every loop 3 times. We emulate 0, 1, 2 iterations
  255. # through the loop. We need to prove this is correct for our
  256. # purposes. But Herb Sutter claims it is. (Proof by authority.)
  257. #[
  258. while cond:
  259. body
  260. Becomes:
  261. block:
  262. if cond:
  263. body
  264. if cond:
  265. body
  266. if cond:
  267. body
  268. We still need to ensure 'break' resolves properly, so an AST to AST
  269. translation is impossible.
  270. So the code to generate is:
  271. cond
  272. fork L4 # F1
  273. body
  274. cond
  275. fork L5 # F2
  276. body
  277. cond
  278. fork L6 # F3
  279. body
  280. L6:
  281. join F3
  282. L5:
  283. join F2
  284. L4:
  285. join F1
  286. ]#
  287. if isTrue(n[0]):
  288. # 'while true' is an idiom in Nim and so we produce
  289. # better code for it:
  290. withBlock(nil):
  291. for i in 0..2:
  292. c.gen(n[1])
  293. else:
  294. withBlock(nil):
  295. var endings: array[3, TPosition]
  296. for i in 0..2:
  297. c.gen(n[0])
  298. endings[i] = c.forkI(n)
  299. c.gen(n[1])
  300. for i in countdown(endings.high, 0):
  301. c.patch(endings[i])
  302. else:
  303. proc genWhile(c: var Con; n: PNode) =
  304. # lab1:
  305. # cond, tmp
  306. # fork tmp, lab2
  307. # body
  308. # jmp lab1
  309. # lab2:
  310. let lab1 = c.genLabel
  311. withBlock(nil):
  312. if isTrue(n[0]):
  313. c.gen(n[1])
  314. c.jmpBack(n, lab1)
  315. else:
  316. c.gen(n[0])
  317. forkT(n):
  318. c.gen(n[1])
  319. c.jmpBack(n, lab1)
  320. template forkT(n, body) =
  321. let lab1 = c.forkI(n)
  322. body
  323. c.patch(lab1)
  324. proc genIf(c: var Con, n: PNode) =
  325. #[
  326. if cond:
  327. A
  328. elif condB:
  329. B
  330. elif condC:
  331. C
  332. else:
  333. D
  334. cond
  335. fork lab1
  336. A
  337. goto Lend
  338. lab1:
  339. condB
  340. fork lab2
  341. B
  342. goto Lend2
  343. lab2:
  344. condC
  345. fork L3
  346. C
  347. goto Lend3
  348. L3:
  349. D
  350. goto Lend3 # not eliminated to simplify the join generation
  351. Lend3:
  352. join F3
  353. Lend2:
  354. join F2
  355. Lend:
  356. join F1
  357. ]#
  358. var endings: seq[TPosition] = @[]
  359. for i in 0..<n.len:
  360. let it = n[i]
  361. c.gen(it[0])
  362. if it.len == 2:
  363. forkT(it[1]):
  364. c.gen(it[1])
  365. endings.add c.gotoI(it[1])
  366. for i in countdown(endings.high, 0):
  367. c.patch(endings[i])
  368. proc genAndOr(c: var Con; n: PNode) =
  369. # asgn dest, a
  370. # fork lab1
  371. # asgn dest, b
  372. # lab1:
  373. # join F1
  374. c.gen(n[1])
  375. forkT(n):
  376. c.gen(n[2])
  377. proc genCase(c: var Con; n: PNode) =
  378. # if (!expr1) goto lab1;
  379. # thenPart
  380. # goto LEnd
  381. # lab1:
  382. # if (!expr2) goto lab2;
  383. # thenPart2
  384. # goto LEnd
  385. # lab2:
  386. # elsePart
  387. # Lend:
  388. let isExhaustive = skipTypes(n[0].typ,
  389. abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
  390. var endings: seq[TPosition] = @[]
  391. c.gen(n[0])
  392. for i in 1..<n.len:
  393. let it = n[i]
  394. if it.len == 1 or (i == n.len-1 and isExhaustive):
  395. # treat the last branch as 'else' if this is an exhaustive case statement.
  396. c.gen(it.lastSon)
  397. else:
  398. forkT(it.lastSon):
  399. c.gen(it.lastSon)
  400. endings.add c.gotoI(it.lastSon)
  401. for i in countdown(endings.high, 0):
  402. let endPos = endings[i]
  403. c.patch(endPos)
  404. proc genBlock(c: var Con; n: PNode) =
  405. withBlock(n[0].sym):
  406. c.gen(n[1])
  407. proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
  408. let lab1 = c.gotoI(n)
  409. if c.blocks[i].isTryBlock:
  410. c.blocks[i].raiseFixups.add lab1
  411. else:
  412. var trailingFinales: seq[PNode]
  413. if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
  414. for b in countdown(c.blocks.high, i):
  415. if c.blocks[b].isTryBlock:
  416. trailingFinales.add c.blocks[b].finale
  417. c.blocks[i].breakFixups.add (lab1, trailingFinales)
  418. proc genBreak(c: var Con; n: PNode) =
  419. if n[0].kind == nkSym:
  420. for i in countdown(c.blocks.high, 0):
  421. if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
  422. genBreakOrRaiseAux(c, i, n)
  423. return
  424. #globalError(n.info, "VM problem: cannot find 'break' target")
  425. else:
  426. for i in countdown(c.blocks.high, 0):
  427. if not c.blocks[i].isTryBlock:
  428. genBreakOrRaiseAux(c, i, n)
  429. return
  430. proc genTry(c: var Con; n: PNode) =
  431. var endings: seq[TPosition] = @[]
  432. let oldLen = c.blocks.len
  433. c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
  434. inc c.inTryStmt
  435. c.gen(n[0])
  436. dec c.inTryStmt
  437. for f in c.blocks[oldLen].raiseFixups:
  438. c.patch(f)
  439. c.blocks.setLen oldLen
  440. for i in 1..<n.len:
  441. let it = n[i]
  442. if it.kind != nkFinally:
  443. forkT(it):
  444. c.gen(it.lastSon)
  445. endings.add c.gotoI(it)
  446. for i in countdown(endings.high, 0):
  447. c.patch(endings[i])
  448. let fin = lastSon(n)
  449. if fin.kind == nkFinally:
  450. c.gen(fin[0])
  451. template genNoReturn(c: var Con; n: PNode) =
  452. # leave the graph
  453. c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
  454. proc genRaise(c: var Con; n: PNode) =
  455. gen(c, n[0])
  456. if c.inTryStmt > 0:
  457. for i in countdown(c.blocks.high, 0):
  458. if c.blocks[i].isTryBlock:
  459. genBreakOrRaiseAux(c, i, n)
  460. return
  461. assert false #Unreachable
  462. else:
  463. genNoReturn(c, n)
  464. proc genImplicitReturn(c: var Con) =
  465. if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
  466. gen(c, c.owner.ast[resultPos])
  467. proc genReturn(c: var Con; n: PNode) =
  468. if n[0].kind != nkEmpty:
  469. gen(c, n[0])
  470. else:
  471. genImplicitReturn(c)
  472. genBreakOrRaiseAux(c, 0, n)
  473. const
  474. InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
  475. PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
  476. nkBracketExpr, nkDerefExpr, nkHiddenDeref,
  477. nkAddr, nkHiddenAddr,
  478. nkObjDownConv, nkObjUpConv}
  479. PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
  480. proc skipConvDfa*(n: PNode): PNode =
  481. result = n
  482. while true:
  483. case result.kind
  484. of nkObjDownConv, nkObjUpConv:
  485. result = result[0]
  486. of PathKinds1:
  487. result = result[1]
  488. else: break
  489. proc aliases*(obj, field: PNode): bool =
  490. var n = field
  491. var obj = obj
  492. while true:
  493. case obj.kind
  494. of {nkObjDownConv, nkObjUpConv, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}:
  495. obj = obj[0]
  496. of PathKinds1:
  497. obj = obj[1]
  498. else: break
  499. while true:
  500. if sameTrees(obj, n): return true
  501. case n.kind
  502. of PathKinds0:
  503. n = n[0]
  504. of PathKinds1:
  505. n = n[1]
  506. else: break
  507. type InstrTargetKind* = enum
  508. None, Full, Partial
  509. proc instrTargets*(insloc, loc: PNode): InstrTargetKind =
  510. if sameTrees(insloc, loc) or insloc.aliases(loc):
  511. Full # x -> x; x -> x.f
  512. elif loc.aliases(insloc):
  513. Partial # x.f -> x
  514. else: None
  515. proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
  516. var n = orig
  517. while true:
  518. case n.kind
  519. of PathKinds0 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}:
  520. n = n[0]
  521. of PathKinds1:
  522. n = n[1]
  523. of nkBracketExpr:
  524. # in a[i] the 'i' must be known
  525. if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}:
  526. n = n[0]
  527. else:
  528. return false
  529. of nkHiddenDeref, nkDerefExpr:
  530. # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
  531. # pointer indirection.
  532. # bug #14159, we cannot reason about sinkParam[].location as it can
  533. # still be shared for tyRef.
  534. n = n[0]
  535. return n.kind == nkSym and n.sym.owner == owner and
  536. (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
  537. else: break
  538. # XXX Allow closure deref operations here if we know
  539. # the owner controlled the closure allocation?
  540. result = n.kind == nkSym and n.sym.owner == owner and
  541. {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
  542. (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
  543. # Note: There is a different move analyzer possible that checks for
  544. # consume(param.key); param.key = newValue for all paths. Then code like
  545. #
  546. # let splited = split(move self.root, x)
  547. # self.root = merge(splited.lower, splited.greater)
  548. #
  549. # could be written without the ``move self.root``. However, this would be
  550. # wrong! Then the write barrier for the ``self.root`` assignment would
  551. # free the old data and all is lost! Lesson: Don't be too smart, trust the
  552. # lower level C++ optimizer to specialize this code.
  553. proc skipTrivials(c: var Con, n: PNode): PNode =
  554. result = n
  555. while true:
  556. case result.kind
  557. of PathKinds0 - {nkBracketExpr}:
  558. result = result[0]
  559. of nkBracketExpr:
  560. gen(c, result[1])
  561. result = result[0]
  562. of PathKinds1:
  563. result = result[1]
  564. else: break
  565. proc genUse(c: var Con; orig: PNode) =
  566. let n = c.skipTrivials(orig)
  567. if n.kind == nkSym and n.sym.kind in InterestingSyms:
  568. c.code.add Instr(n: orig, kind: use)
  569. elif n.kind in nkCallKinds:
  570. gen(c, n)
  571. proc genDef(c: var Con; orig: PNode) =
  572. let n = c.skipTrivials(orig)
  573. if n.kind == nkSym and n.sym.kind in InterestingSyms:
  574. c.code.add Instr(n: orig, kind: def)
  575. proc genCall(c: var Con; n: PNode) =
  576. gen(c, n[0])
  577. var t = n[0].typ
  578. if t != nil: t = t.skipTypes(abstractInst)
  579. for i in 1..<n.len:
  580. gen(c, n[i])
  581. when false:
  582. if t != nil and i < t.len and t[i].kind == tyOut:
  583. # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
  584. genDef(c, n[i])
  585. # every call can potentially raise:
  586. if c.inTryStmt > 0 and canRaiseConservative(n[0]):
  587. # we generate the instruction sequence:
  588. # fork lab1
  589. # goto exceptionHandler (except or finally)
  590. # lab1:
  591. # join F1
  592. forkT(n):
  593. for i in countdown(c.blocks.high, 0):
  594. if c.blocks[i].isTryBlock:
  595. genBreakOrRaiseAux(c, i, n)
  596. break
  597. proc genMagic(c: var Con; n: PNode; m: TMagic) =
  598. case m
  599. of mAnd, mOr: c.genAndOr(n)
  600. of mNew, mNewFinalize:
  601. genDef(c, n[1])
  602. for i in 2..<n.len: gen(c, n[i])
  603. else:
  604. genCall(c, n)
  605. proc genVarSection(c: var Con; n: PNode) =
  606. for a in n:
  607. if a.kind == nkCommentStmt:
  608. discard
  609. elif a.kind == nkVarTuple:
  610. gen(c, a.lastSon)
  611. for i in 0..<a.len-2: genDef(c, a[i])
  612. else:
  613. gen(c, a.lastSon)
  614. if a.lastSon.kind != nkEmpty:
  615. genDef(c, a[0])
  616. proc gen(c: var Con; n: PNode) =
  617. case n.kind
  618. of nkSym: genUse(c, n)
  619. of nkCallKinds:
  620. if n[0].kind == nkSym:
  621. let s = n[0].sym
  622. if s.magic != mNone:
  623. genMagic(c, n, s.magic)
  624. else:
  625. genCall(c, n)
  626. if sfNoReturn in n[0].sym.flags:
  627. genNoReturn(c, n)
  628. else:
  629. genCall(c, n)
  630. of nkCharLit..nkNilLit: discard
  631. of nkAsgn, nkFastAsgn:
  632. gen(c, n[1])
  633. # watch out: 'obj[i].f2 = value' sets 'f2' but
  634. # "uses" 'i'. But we are only talking about builtin array indexing so
  635. # it doesn't matter and 'x = 34' is NOT a usage of 'x'.
  636. genDef(c, n[0])
  637. of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
  638. genUse(c, n)
  639. of nkIfStmt, nkIfExpr: genIf(c, n)
  640. of nkWhenStmt:
  641. # This is "when nimvm" node. Chose the first branch.
  642. gen(c, n[0][1])
  643. of nkCaseStmt: genCase(c, n)
  644. of nkWhileStmt: genWhile(c, n)
  645. of nkBlockExpr, nkBlockStmt: genBlock(c, n)
  646. of nkReturnStmt: genReturn(c, n)
  647. of nkRaiseStmt: genRaise(c, n)
  648. of nkBreakStmt: genBreak(c, n)
  649. of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
  650. of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
  651. nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
  652. for x in n: gen(c, x)
  653. of nkPragmaBlock: gen(c, n.lastSon)
  654. of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
  655. gen(c, n[0])
  656. of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
  657. gen(c, n[1])
  658. of nkVarSection, nkLetSection: genVarSection(c, n)
  659. of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
  660. else: discard
  661. proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
  662. ## constructs a control flow graph for ``body``.
  663. var c = Con(code: @[], blocks: @[], owner: s)
  664. withBlock(s):
  665. gen(c, body)
  666. genImplicitReturn(c)
  667. when defined(gcArc) or defined(gcOrc):
  668. result = c.code # will move
  669. else:
  670. shallowCopy(result, c.code)