dfa.nim 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  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, intsets, lineinfos, renderer
  31. import std/private/asciitables
  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. # EDIT: Actually, we only need to unroll 2 times
  259. # because Nim doesn't have a way of breaking/goto-ing into
  260. # a loop iteration. Unrolling 2 times is much better for compile
  261. # times of nested loops than 3 times, so we do that here.
  262. #[
  263. while cond:
  264. body
  265. Becomes:
  266. block:
  267. if cond:
  268. body
  269. if cond:
  270. body
  271. if cond:
  272. body
  273. We still need to ensure 'break' resolves properly, so an AST to AST
  274. translation is impossible.
  275. So the code to generate is:
  276. cond
  277. fork L4 # F1
  278. body
  279. cond
  280. fork L5 # F2
  281. body
  282. cond
  283. fork L6 # F3
  284. body
  285. L6:
  286. join F3
  287. L5:
  288. join F2
  289. L4:
  290. join F1
  291. ]#
  292. if isTrue(n[0]):
  293. # 'while true' is an idiom in Nim and so we produce
  294. # better code for it:
  295. withBlock(nil):
  296. for i in 0..1:
  297. c.gen(n[1])
  298. else:
  299. withBlock(nil):
  300. var endings: array[2, TPosition]
  301. for i in 0..1:
  302. c.gen(n[0])
  303. endings[i] = c.forkI(n)
  304. c.gen(n[1])
  305. for i in countdown(endings.high, 0):
  306. c.patch(endings[i])
  307. else:
  308. proc genWhile(c: var Con; n: PNode) =
  309. # lab1:
  310. # cond, tmp
  311. # fork tmp, lab2
  312. # body
  313. # jmp lab1
  314. # lab2:
  315. let lab1 = c.genLabel
  316. withBlock(nil):
  317. if isTrue(n[0]):
  318. c.gen(n[1])
  319. c.jmpBack(n, lab1)
  320. else:
  321. c.gen(n[0])
  322. forkT(n):
  323. c.gen(n[1])
  324. c.jmpBack(n, lab1)
  325. template forkT(n, body) =
  326. let lab1 = c.forkI(n)
  327. body
  328. c.patch(lab1)
  329. proc genIf(c: var Con, n: PNode) =
  330. #[
  331. if cond:
  332. A
  333. elif condB:
  334. B
  335. elif condC:
  336. C
  337. else:
  338. D
  339. cond
  340. fork lab1
  341. A
  342. goto Lend
  343. lab1:
  344. condB
  345. fork lab2
  346. B
  347. goto Lend2
  348. lab2:
  349. condC
  350. fork L3
  351. C
  352. goto Lend3
  353. L3:
  354. D
  355. goto Lend3 # not eliminated to simplify the join generation
  356. Lend3:
  357. join F3
  358. Lend2:
  359. join F2
  360. Lend:
  361. join F1
  362. ]#
  363. var endings: seq[TPosition] = @[]
  364. for i in 0..<n.len:
  365. let it = n[i]
  366. c.gen(it[0])
  367. if it.len == 2:
  368. forkT(it[1]):
  369. c.gen(it[1])
  370. endings.add c.gotoI(it[1])
  371. for i in countdown(endings.high, 0):
  372. c.patch(endings[i])
  373. proc genAndOr(c: var Con; n: PNode) =
  374. # asgn dest, a
  375. # fork lab1
  376. # asgn dest, b
  377. # lab1:
  378. # join F1
  379. c.gen(n[1])
  380. forkT(n):
  381. c.gen(n[2])
  382. proc genCase(c: var Con; n: PNode) =
  383. # if (!expr1) goto lab1;
  384. # thenPart
  385. # goto LEnd
  386. # lab1:
  387. # if (!expr2) goto lab2;
  388. # thenPart2
  389. # goto LEnd
  390. # lab2:
  391. # elsePart
  392. # Lend:
  393. let isExhaustive = skipTypes(n[0].typ,
  394. abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
  395. # we generate endings as a set of chained gotos, this is a bit awkward but it
  396. # ensures when recursively traversing the CFG for various analysis, we don't
  397. # artificially extended the life of each branch (for the purposes of DFA)
  398. # beyond the minimum amount.
  399. var endings: seq[TPosition] = @[]
  400. c.gen(n[0])
  401. for i in 1..<n.len:
  402. let it = n[i]
  403. if it.len == 1 or (i == n.len-1 and isExhaustive):
  404. # treat the last branch as 'else' if this is an exhaustive case statement.
  405. c.gen(it.lastSon)
  406. if endings.len != 0:
  407. c.patch(endings[^1])
  408. else:
  409. forkT(it.lastSon):
  410. c.gen(it.lastSon)
  411. if endings.len != 0:
  412. c.patch(endings[^1])
  413. endings.add c.gotoI(it.lastSon)
  414. proc genBlock(c: var Con; n: PNode) =
  415. withBlock(n[0].sym):
  416. c.gen(n[1])
  417. proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
  418. let lab1 = c.gotoI(n)
  419. if c.blocks[i].isTryBlock:
  420. c.blocks[i].raiseFixups.add lab1
  421. else:
  422. var trailingFinales: seq[PNode]
  423. if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
  424. for b in countdown(c.blocks.high, i):
  425. if c.blocks[b].isTryBlock:
  426. trailingFinales.add c.blocks[b].finale
  427. c.blocks[i].breakFixups.add (lab1, trailingFinales)
  428. proc genBreak(c: var Con; n: PNode) =
  429. if n[0].kind == nkSym:
  430. for i in countdown(c.blocks.high, 0):
  431. if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
  432. genBreakOrRaiseAux(c, i, n)
  433. return
  434. #globalError(n.info, "VM problem: cannot find 'break' target")
  435. else:
  436. for i in countdown(c.blocks.high, 0):
  437. if not c.blocks[i].isTryBlock:
  438. genBreakOrRaiseAux(c, i, n)
  439. return
  440. proc genTry(c: var Con; n: PNode) =
  441. var endings: seq[TPosition] = @[]
  442. let oldLen = c.blocks.len
  443. c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
  444. inc c.inTryStmt
  445. c.gen(n[0])
  446. dec c.inTryStmt
  447. for f in c.blocks[oldLen].raiseFixups:
  448. c.patch(f)
  449. c.blocks.setLen oldLen
  450. for i in 1..<n.len:
  451. let it = n[i]
  452. if it.kind != nkFinally:
  453. forkT(it):
  454. c.gen(it.lastSon)
  455. endings.add c.gotoI(it)
  456. for i in countdown(endings.high, 0):
  457. c.patch(endings[i])
  458. let fin = lastSon(n)
  459. if fin.kind == nkFinally:
  460. c.gen(fin[0])
  461. template genNoReturn(c: var Con; n: PNode) =
  462. # leave the graph
  463. c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
  464. proc genRaise(c: var Con; n: PNode) =
  465. gen(c, n[0])
  466. if c.inTryStmt > 0:
  467. for i in countdown(c.blocks.high, 0):
  468. if c.blocks[i].isTryBlock:
  469. genBreakOrRaiseAux(c, i, n)
  470. return
  471. assert false #Unreachable
  472. else:
  473. genNoReturn(c, n)
  474. proc genImplicitReturn(c: var Con) =
  475. if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
  476. gen(c, c.owner.ast[resultPos])
  477. proc genReturn(c: var Con; n: PNode) =
  478. if n[0].kind != nkEmpty:
  479. gen(c, n[0])
  480. else:
  481. genImplicitReturn(c)
  482. genBreakOrRaiseAux(c, 0, n)
  483. const
  484. InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
  485. PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
  486. nkBracketExpr, nkDerefExpr, nkHiddenDeref,
  487. nkAddr, nkHiddenAddr,
  488. nkObjDownConv, nkObjUpConv}
  489. PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
  490. proc skipConvDfa*(n: PNode): PNode =
  491. result = n
  492. while true:
  493. case result.kind
  494. of nkObjDownConv, nkObjUpConv:
  495. result = result[0]
  496. of PathKinds1:
  497. result = result[1]
  498. else: break
  499. type AliasKind* = enum
  500. yes, no, maybe
  501. proc aliases*(obj, field: PNode): AliasKind =
  502. # obj -> field:
  503. # x -> x: true
  504. # x -> x.f: true
  505. # x.f -> x: false
  506. # x.f -> x.f: true
  507. # x.f -> x.v: false
  508. # x -> x[0]: true
  509. # x[0] -> x: false
  510. # x[0] -> x[0]: true
  511. # x[0] -> x[1]: false
  512. # x -> x[i]: true
  513. # x[i] -> x: false
  514. # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
  515. # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
  516. template collectImportantNodes(result, n) =
  517. var result: seq[PNode]
  518. var n = n
  519. while true:
  520. case n.kind
  521. of PathKinds0 - {nkDotExpr, nkBracketExpr}:
  522. n = n[0]
  523. of PathKinds1:
  524. n = n[1]
  525. of nkDotExpr, nkBracketExpr:
  526. result.add n
  527. n = n[0]
  528. of nkSym:
  529. result.add n; break
  530. else: return no
  531. collectImportantNodes(objImportantNodes, obj)
  532. collectImportantNodes(fieldImportantNodes, field)
  533. # If field is less nested than obj, then it cannot be part of/aliased by obj
  534. if fieldImportantNodes.len < objImportantNodes.len: return no
  535. result = yes
  536. for i in 1..objImportantNodes.len:
  537. # We compare the nodes leading to the location of obj and field
  538. # with each other.
  539. # We continue until they diverge, in which case we return no, or
  540. # until we reach the location of obj, in which case we do not need
  541. # to look further, since field must be part of/aliased by obj now.
  542. # If we encounter an element access using an index which is a runtime value,
  543. # we simply return maybe instead of yes; should further nodes not diverge.
  544. let currFieldPath = fieldImportantNodes[^i]
  545. let currObjPath = objImportantNodes[^i]
  546. if currFieldPath.kind != currObjPath.kind:
  547. return no
  548. case currFieldPath.kind
  549. of nkSym:
  550. if currFieldPath.sym != currObjPath.sym: return no
  551. of nkDotExpr:
  552. if currFieldPath[1].sym != currObjPath[1].sym: return no
  553. of nkBracketExpr:
  554. if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
  555. if currFieldPath[1].intVal != currObjPath[1].intVal:
  556. return no
  557. else:
  558. result = maybe
  559. else: assert false # unreachable
  560. proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
  561. var n = orig
  562. while true:
  563. case n.kind
  564. of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
  565. n = n[0]
  566. of PathKinds1:
  567. n = n[1]
  568. of nkHiddenDeref, nkDerefExpr:
  569. # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
  570. # pointer indirection.
  571. # bug #14159, we cannot reason about sinkParam[].location as it can
  572. # still be shared for tyRef.
  573. n = n[0]
  574. return n.kind == nkSym and n.sym.owner == owner and
  575. (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
  576. else: break
  577. # XXX Allow closure deref operations here if we know
  578. # the owner controlled the closure allocation?
  579. result = n.kind == nkSym and n.sym.owner == owner and
  580. {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
  581. (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
  582. # Note: There is a different move analyzer possible that checks for
  583. # consume(param.key); param.key = newValue for all paths. Then code like
  584. #
  585. # let splited = split(move self.root, x)
  586. # self.root = merge(splited.lower, splited.greater)
  587. #
  588. # could be written without the ``move self.root``. However, this would be
  589. # wrong! Then the write barrier for the ``self.root`` assignment would
  590. # free the old data and all is lost! Lesson: Don't be too smart, trust the
  591. # lower level C++ optimizer to specialize this code.
  592. proc skipTrivials(c: var Con, n: PNode): PNode =
  593. result = n
  594. while true:
  595. case result.kind
  596. of PathKinds0 - {nkBracketExpr}:
  597. result = result[0]
  598. of nkBracketExpr:
  599. gen(c, result[1])
  600. result = result[0]
  601. of PathKinds1:
  602. result = result[1]
  603. else: break
  604. proc genUse(c: var Con; orig: PNode) =
  605. let n = c.skipTrivials(orig)
  606. if n.kind == nkSym:
  607. if n.sym.kind in InterestingSyms:
  608. c.code.add Instr(n: orig, kind: use)
  609. else:
  610. gen(c, n)
  611. proc genDef(c: var Con; orig: PNode) =
  612. let n = c.skipTrivials(orig)
  613. if n.kind == nkSym and n.sym.kind in InterestingSyms:
  614. c.code.add Instr(n: orig, kind: def)
  615. proc genCall(c: var Con; n: PNode) =
  616. gen(c, n[0])
  617. var t = n[0].typ
  618. if t != nil: t = t.skipTypes(abstractInst)
  619. for i in 1..<n.len:
  620. gen(c, n[i])
  621. when false:
  622. if t != nil and i < t.len and t[i].kind == tyOut:
  623. # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
  624. genDef(c, n[i])
  625. # every call can potentially raise:
  626. if c.inTryStmt > 0 and canRaiseConservative(n[0]):
  627. # we generate the instruction sequence:
  628. # fork lab1
  629. # goto exceptionHandler (except or finally)
  630. # lab1:
  631. # join F1
  632. forkT(n):
  633. for i in countdown(c.blocks.high, 0):
  634. if c.blocks[i].isTryBlock:
  635. genBreakOrRaiseAux(c, i, n)
  636. break
  637. proc genMagic(c: var Con; n: PNode; m: TMagic) =
  638. case m
  639. of mAnd, mOr: c.genAndOr(n)
  640. of mNew, mNewFinalize:
  641. genDef(c, n[1])
  642. for i in 2..<n.len: gen(c, n[i])
  643. else:
  644. genCall(c, n)
  645. proc genVarSection(c: var Con; n: PNode) =
  646. for a in n:
  647. if a.kind == nkCommentStmt:
  648. discard
  649. elif a.kind == nkVarTuple:
  650. gen(c, a.lastSon)
  651. for i in 0..<a.len-2: genDef(c, a[i])
  652. else:
  653. gen(c, a.lastSon)
  654. if a.lastSon.kind != nkEmpty:
  655. genDef(c, a[0])
  656. proc gen(c: var Con; n: PNode) =
  657. case n.kind
  658. of nkSym: genUse(c, n)
  659. of nkCallKinds:
  660. if n[0].kind == nkSym:
  661. let s = n[0].sym
  662. if s.magic != mNone:
  663. genMagic(c, n, s.magic)
  664. else:
  665. genCall(c, n)
  666. if sfNoReturn in n[0].sym.flags:
  667. genNoReturn(c, n)
  668. else:
  669. genCall(c, n)
  670. of nkCharLit..nkNilLit: discard
  671. of nkAsgn, nkFastAsgn:
  672. gen(c, n[1])
  673. if n[0].kind in PathKinds0:
  674. let a = c.skipTrivials(n[0])
  675. if a.kind in nkCallKinds:
  676. gen(c, a)
  677. # watch out: 'obj[i].f2 = value' sets 'f2' but
  678. # "uses" 'i'. But we are only talking about builtin array indexing so
  679. # it doesn't matter and 'x = 34' is NOT a usage of 'x'.
  680. genDef(c, n[0])
  681. of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
  682. genUse(c, n)
  683. of nkIfStmt, nkIfExpr: genIf(c, n)
  684. of nkWhenStmt:
  685. # This is "when nimvm" node. Chose the first branch.
  686. gen(c, n[0][1])
  687. of nkCaseStmt: genCase(c, n)
  688. of nkWhileStmt: genWhile(c, n)
  689. of nkBlockExpr, nkBlockStmt: genBlock(c, n)
  690. of nkReturnStmt: genReturn(c, n)
  691. of nkRaiseStmt: genRaise(c, n)
  692. of nkBreakStmt: genBreak(c, n)
  693. of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
  694. of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
  695. nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
  696. for x in n: gen(c, x)
  697. of nkPragmaBlock: gen(c, n.lastSon)
  698. of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
  699. gen(c, n[0])
  700. of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
  701. gen(c, n[1])
  702. of nkVarSection, nkLetSection: genVarSection(c, n)
  703. of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
  704. else: discard
  705. proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
  706. ## constructs a control flow graph for ``body``.
  707. var c = Con(code: @[], blocks: @[], owner: s)
  708. withBlock(s):
  709. gen(c, body)
  710. genImplicitReturn(c)
  711. when defined(gcArc) or defined(gcOrc):
  712. result = c.code # will move
  713. else:
  714. shallowCopy(result, c.code)