forloops.nim 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements for loop detection for better C code generation.
  10. import ast, astalgo
  11. const
  12. someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
  13. mLeI, mLeF64, mLeU, mLeEnum,
  14. mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtEnum,
  15. mLtCh, mLtB, mLtPtr}
  16. proc isCounter(s: PSym): bool {.inline.} =
  17. s.kind in {skResult, skVar, skLet, skTemp} and
  18. {sfGlobal, sfAddrTaken} * s.flags == {}
  19. proc isCall(n: PNode): bool {.inline.} =
  20. n.kind in nkCallKinds and n[0].kind == nkSym
  21. proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
  22. proc getCounter(lastStmt: PNode): PSym =
  23. if lastStmt.isCall:
  24. let op = lastStmt.sym
  25. if op.magic in {mDec, mInc} or
  26. ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
  27. if op[1].kind == nkSym and isCounter(op[1].sym):
  28. result = op[1].sym
  29. proc counterInTree(n, loop: PNode; counter: PSym): bool =
  30. # prune the search tree: within the loop the counter may be used:
  31. if n == loop: return
  32. case n.kind
  33. of nkSym:
  34. if n.sym == counter: return true
  35. of nkVarSection, nkLetSection:
  36. # definitions are fine!
  37. for it in n:
  38. if counterInTree(it.lastSon): return true
  39. else:
  40. for i in 0..<n.safeLen:
  41. if counterInTree(n[i], loop, counter): return true
  42. proc copyExcept(n: PNode, x, dest: PNode) =
  43. if x == n: return
  44. if n.kind in {nkStmtList, nkStmtListExpr}:
  45. for i in 0..<n.len: copyExcept(n[i], x, dest)
  46. else:
  47. dest.add n
  48. type
  49. ForLoop* = object
  50. counter*: PSym
  51. init*, cond*, increment*, body*: PNode
  52. proc extractForLoop*(loop, fullTree: PNode): ForLoop =
  53. ## returns 'counter == nil' if the while loop 'n' is not a for loop:
  54. assert loop.kind == nkWhileStmt
  55. let cond == loop[0]
  56. if not cond.isCall: return
  57. if cond[0].sym.magic notin someCmp: return
  58. var lastStmt = loop[1]
  59. while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
  60. lastStmt = lastStmt.lastSon
  61. let counter = getCounter(lastStmt)
  62. if counter.isNil or counter.ast.isNil: return
  63. template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
  64. if cond[1] =~ counter or cond[2] =~ counter:
  65. # ok, now check 'counter' is not used *after* the loop
  66. if counterInTree(fullTree, loop, counter): return
  67. # ok, success, fill in the fields:
  68. result.counter = counter
  69. result.init = counter.ast
  70. result.cond = cond
  71. result.increment = lastStmt
  72. result.body = newNodeI(nkStmtList, loop[1].info)
  73. copyExcept(loop[1], lastStmt, result.body)