123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- import ast
- import std / assertions
- const
- PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
- nkBracketExpr, nkDerefExpr, nkHiddenDeref,
- nkAddr, nkHiddenAddr,
- nkObjDownConv, nkObjUpConv}
- PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
- proc skipConvDfa*(n: PNode): PNode =
- result = n
- while true:
- case result.kind
- of nkObjDownConv, nkObjUpConv:
- result = result[0]
- of PathKinds1:
- result = result[1]
- else: break
- proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
- var n = orig
- while true:
- case n.kind
- of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
- n = n[0]
- of PathKinds1:
- n = n[1]
- of nkHiddenDeref, nkDerefExpr:
- # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
- # pointer indirection.
- # bug #14159, we cannot reason about sinkParam[].location as it can
- # still be shared for tyRef.
- n = n[0]
- return n.kind == nkSym and n.sym.owner == owner and
- (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
- else: break
- # XXX Allow closure deref operations here if we know
- # the owner controlled the closure allocation?
- result = n.kind == nkSym and n.sym.owner == owner and
- {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
- (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
- # Note: There is a different move analyzer possible that checks for
- # consume(param.key); param.key = newValue for all paths. Then code like
- #
- # let splited = split(move self.root, x)
- # self.root = merge(splited.lower, splited.greater)
- #
- # could be written without the ``move self.root``. However, this would be
- # wrong! Then the write barrier for the ``self.root`` assignment would
- # free the old data and all is lost! Lesson: Don't be too smart, trust the
- # lower level C++ optimizer to specialize this code.
- type AliasKind* = enum
- yes, no, maybe
- proc aliases*(obj, field: PNode): AliasKind =
- # obj -> field:
- # x -> x: true
- # x -> x.f: true
- # x.f -> x: false
- # x.f -> x.f: true
- # x.f -> x.v: false
- # x -> x[]: true
- # x[] -> x: false
- # x -> x[0]: true
- # x[0] -> x: false
- # x[0] -> x[0]: true
- # x[0] -> x[1]: false
- # x -> x[i]: true
- # x[i] -> x: false
- # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
- # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
- template collectImportantNodes(result, n) =
- var result: seq[PNode] = @[]
- var n = n
- while true:
- case n.kind
- of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
- n = n[0]
- of PathKinds1:
- n = n[1]
- of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
- result.add n
- n = n[0]
- of nkSym:
- result.add n
- break
- else: return no
- collectImportantNodes(objImportantNodes, obj)
- collectImportantNodes(fieldImportantNodes, field)
- # If field is less nested than obj, then it cannot be part of/aliased by obj
- if fieldImportantNodes.len < objImportantNodes.len: return no
- result = yes
- for i in 1..objImportantNodes.len:
- # We compare the nodes leading to the location of obj and field
- # with each other.
- # We continue until they diverge, in which case we return no, or
- # until we reach the location of obj, in which case we do not need
- # to look further, since field must be part of/aliased by obj now.
- # If we encounter an element access using an index which is a runtime value,
- # we simply return maybe instead of yes; should further nodes not diverge.
- let currFieldPath = fieldImportantNodes[^i]
- let currObjPath = objImportantNodes[^i]
- if currFieldPath.kind != currObjPath.kind:
- return no
- case currFieldPath.kind
- of nkSym:
- if currFieldPath.sym != currObjPath.sym: return no
- of nkDotExpr:
- if currFieldPath[1].sym != currObjPath[1].sym: return no
- of nkDerefExpr, nkHiddenDeref:
- discard
- of nkBracketExpr:
- if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
- if currFieldPath[1].intVal != currObjPath[1].intVal:
- return no
- else:
- result = maybe
- else: assert false # unreachable
|