dfa.nim 21 KB

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