aliasanalysis.nim 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import ast
  2. import std / assertions
  3. const
  4. PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
  5. nkBracketExpr, nkDerefExpr, nkHiddenDeref,
  6. nkAddr, nkHiddenAddr,
  7. nkObjDownConv, nkObjUpConv}
  8. PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
  9. proc skipConvDfa*(n: PNode): PNode =
  10. result = n
  11. while true:
  12. case result.kind
  13. of nkObjDownConv, nkObjUpConv:
  14. result = result[0]
  15. of PathKinds1:
  16. result = result[1]
  17. else: break
  18. proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
  19. var n = orig
  20. while true:
  21. case n.kind
  22. of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
  23. n = n[0]
  24. of PathKinds1:
  25. n = n[1]
  26. of nkHiddenDeref, nkDerefExpr:
  27. # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
  28. # pointer indirection.
  29. # bug #14159, we cannot reason about sinkParam[].location as it can
  30. # still be shared for tyRef.
  31. n = n[0]
  32. return n.kind == nkSym and n.sym.owner == owner and
  33. (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
  34. else: break
  35. # XXX Allow closure deref operations here if we know
  36. # the owner controlled the closure allocation?
  37. result = n.kind == nkSym and n.sym.owner == owner and
  38. {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
  39. (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
  40. # Note: There is a different move analyzer possible that checks for
  41. # consume(param.key); param.key = newValue for all paths. Then code like
  42. #
  43. # let splited = split(move self.root, x)
  44. # self.root = merge(splited.lower, splited.greater)
  45. #
  46. # could be written without the ``move self.root``. However, this would be
  47. # wrong! Then the write barrier for the ``self.root`` assignment would
  48. # free the old data and all is lost! Lesson: Don't be too smart, trust the
  49. # lower level C++ optimizer to specialize this code.
  50. type AliasKind* = enum
  51. yes, no, maybe
  52. proc aliases*(obj, field: PNode): AliasKind =
  53. # obj -> field:
  54. # x -> x: true
  55. # x -> x.f: true
  56. # x.f -> x: false
  57. # x.f -> x.f: true
  58. # x.f -> x.v: false
  59. # x -> x[]: true
  60. # x[] -> x: false
  61. # x -> x[0]: true
  62. # x[0] -> x: false
  63. # x[0] -> x[0]: true
  64. # x[0] -> x[1]: false
  65. # x -> x[i]: true
  66. # x[i] -> x: false
  67. # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
  68. # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
  69. template collectImportantNodes(result, n) =
  70. var result: seq[PNode] = @[]
  71. var n = n
  72. while true:
  73. case n.kind
  74. of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
  75. n = n[0]
  76. of PathKinds1:
  77. n = n[1]
  78. of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
  79. result.add n
  80. n = n[0]
  81. of nkSym:
  82. result.add n
  83. break
  84. else: return no
  85. collectImportantNodes(objImportantNodes, obj)
  86. collectImportantNodes(fieldImportantNodes, field)
  87. # If field is less nested than obj, then it cannot be part of/aliased by obj
  88. if fieldImportantNodes.len < objImportantNodes.len: return no
  89. result = yes
  90. for i in 1..objImportantNodes.len:
  91. # We compare the nodes leading to the location of obj and field
  92. # with each other.
  93. # We continue until they diverge, in which case we return no, or
  94. # until we reach the location of obj, in which case we do not need
  95. # to look further, since field must be part of/aliased by obj now.
  96. # If we encounter an element access using an index which is a runtime value,
  97. # we simply return maybe instead of yes; should further nodes not diverge.
  98. let currFieldPath = fieldImportantNodes[^i]
  99. let currObjPath = objImportantNodes[^i]
  100. if currFieldPath.kind != currObjPath.kind:
  101. return no
  102. case currFieldPath.kind
  103. of nkSym:
  104. if currFieldPath.sym != currObjPath.sym: return no
  105. of nkDotExpr:
  106. if currFieldPath[1].sym != currObjPath[1].sym: return no
  107. of nkDerefExpr, nkHiddenDeref:
  108. discard
  109. of nkBracketExpr:
  110. if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
  111. if currFieldPath[1].intVal != currObjPath[1].intVal:
  112. return no
  113. else:
  114. result = maybe
  115. else: assert false # unreachable