summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/writetracking.nim275
1 files changed, 0 insertions, 275 deletions
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
deleted file mode 100644
index 44ef3a937..000000000
--- a/compiler/writetracking.nim
+++ /dev/null
@@ -1,275 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the write tracking analysis. Read my block post for
-## a basic description of the algorithm and ideas.
-## The algorithm operates in 2 phases:
-##
-##   * Collecting information about assignments (and pass-by-var calls).
-##   * Computing an aliasing relation based on the assignments. This relation
-##     is then used to compute the 'writes' and 'escapes' effects.
-
-import intsets, idents, ast, trees, msgs, types, options, lineinfos
-
-const
-  debug = false
-
-type
-  AssignToResult = enum
-    asgnNil,   # 'nil' is fine
-    asgnNew,   # 'new(result)'
-    asgnOther  # result = fooBar # not a 'new' --> 'result' might not 'new'
-  NewLocation = enum
-    newNone,
-    newLit,
-    newCall
-  RootInfo = enum
-    rootIsResultOrParam,
-    rootIsHeapAccess,
-    rootIsSym,
-    markAsWrittenTo,
-    markAsEscaping
-
-  Assignment = object # \
-    # Note that the transitive closures MUST be computed in
-    # phase 2 of the algorithm.
-    dest, src: seq[ptr TSym] # we use 'ptr' here to save RC ops and GC cycles
-    destNoTc, srcNoTc: int # length of 'dest', 'src' without the
-                           # transitive closure
-    destInfo: set[RootInfo]
-    info: TLineInfo
-
-  W = object # WriteTrackContext
-    owner: PSym
-    returnsNew: AssignToResult # assignments to 'result'
-    assignments: seq[Assignment] # list of all assignments in this proc
-
-proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) =
-  case n.kind
-  of nkSym:
-    if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}:
-      if n.sym.kind in {skResult, skParam}: incl(info, rootIsResultOrParam)
-      result.add(cast[ptr TSym](n.sym))
-  of nkHiddenDeref, nkDerefExpr:
-    incl(info, rootIsHeapAccess)
-    allRoots(n[0], result, info)
-  of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr,
-      nkHiddenAddr, nkObjUpConv, nkObjDownConv:
-    allRoots(n[0], result, info)
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv,
-      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
-      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
-    allRoots(n.lastSon, result, info)
-  of nkCallKinds:
-    if getMagic(n) == mSlice:
-      allRoots(n[1], result, info)
-    else:
-      # we do significantly better here by using the available escape
-      # information:
-      if n[0].typ.isNil: return
-      var typ = n[0].typ
-      if typ != nil:
-        typ = skipTypes(typ, abstractInst)
-        if typ.kind != tyProc: typ = nil
-        else: assert(typ.len == typ.n.len)
-
-      for i in 1..<n.len:
-        let it = n[i]
-        if typ != nil and i < typ.len:
-          assert(typ.n[i].kind == nkSym)
-          let paramType = typ.n[i]
-          if paramType.typ.isCompileTimeOnly: continue
-          if sfEscapes in paramType.sym.flags or paramType.typ.kind in {tyVar}:
-            allRoots(it, result, info)
-        else:
-          allRoots(it, result, info)
-  else:
-    for i in 0..<n.safeLen:
-      allRoots(n[i], result, info)
-
-proc addAsgn(a: var Assignment; dest, src: PNode; destInfo: set[RootInfo]) =
-  a.dest = @[]
-  a.src = @[]
-  a.destInfo = destInfo
-  allRoots(dest, a.dest, a.destInfo)
-  if dest.kind == nkSym: incl(a.destInfo, rootIsSym)
-  if src != nil:
-    var dummy: set[RootInfo]
-    allRoots(src, a.src, dummy)
-  a.destNoTc = a.dest.len
-  a.srcNoTc = a.src.len
-  a.info = dest.info
-  #echo "ADDING ", dest.info, " ", a.destInfo
-
-proc srcHasSym(a: Assignment; x: ptr TSym): bool =
-  for i in 0..<a.srcNoTc:
-    if a.src[i] == x: return true
-
-proc returnsNewExpr*(n: PNode): NewLocation =
-  case n.kind
-  of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
-      nkFloatLit..nkFloat64Lit, nkNilLit:
-    result = newLit
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv,
-      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
-      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
-    result = returnsNewExpr(n.lastSon)
-  of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure,
-      nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt, nkHiddenTryStmt:
-    result = newLit
-    for i in ord(n.kind == nkObjConstr)..<n.len:
-      let x = returnsNewExpr(n[i])
-      case x
-      of newNone: return newNone
-      of newLit: discard
-      of newCall: result = newCall
-  of nkCallKinds:
-    if n[0].typ != nil and tfReturnsNew in n[0].typ.flags:
-      result = newCall
-  else:
-    result = newNone
-
-proc deps(w: var W; dest, src: PNode; destInfo: set[RootInfo]) =
-  # let x = (localA, localB)
-  # compute 'returnsNew' property:
-  let retNew = if src.isNil: newNone else: returnsNewExpr(src)
-  if dest.kind == nkSym and dest.sym.kind == skResult:
-    if retNew != newNone:
-      if w.returnsNew != asgnOther: w.returnsNew = asgnNew
-    else:
-      w.returnsNew = asgnOther
-  # mark the dependency, but
-  # rule out obviously innocent assignments like 'somebool = true'
-  if dest.kind == nkSym and retNew == newLit: discard
-  else:
-    w.assignments.setLen(w.assignments.len+1)
-    addAsgn(w.assignments[^1], dest, src, destInfo)
-
-proc depsArgs(w: var W; n: PNode) =
-  if n[0].typ.isNil: return
-  var typ = skipTypes(n[0].typ, abstractInst)
-  if typ.kind != tyProc: return
-  # echo n.info, " ", n, " ", w.owner.name.s, " ", typeToString(typ)
-  assert(typ.len == typ.n.len)
-  for i in 1..<n.len:
-    let it = n[i]
-    if i < typ.len:
-      assert(typ.n[i].kind == nkSym)
-      let paramType = typ.n[i]
-      if paramType.typ.isCompileTimeOnly: continue
-      var destInfo: set[RootInfo] = {}
-      if sfWrittenTo in paramType.sym.flags or paramType.typ.kind in {tyVar}:
-        # p(f(x, y), X, g(h, z))
-        destInfo.incl markAsWrittenTo
-      if sfEscapes in paramType.sym.flags:
-        destInfo.incl markAsEscaping
-      if destInfo != {}:
-        deps(w, it, nil, destInfo)
-
-proc deps(w: var W; n: PNode) =
-  case n.kind
-  of nkLetSection, nkVarSection:
-    for child in n:
-      let last = lastSon(child)
-      if last.kind == nkEmpty: continue
-      if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
-        if child.len-2 != last.len: return
-        for i in 0..<child.len-2:
-          deps(w, child[i], last[i], {})
-      else:
-        for i in 0..<child.len-2:
-          deps(w, child[i], last, {})
-  of nkAsgn, nkFastAsgn:
-    deps(w, n[0], n[1], {})
-  else:
-    for i in 0..<n.safeLen:
-      deps(w, n[i])
-    if n.kind in nkCallKinds:
-      if getMagic(n) in {mNew, mNewFinalize, mNewSeq}:
-        # may not look like an assignment, but it is:
-        deps(w, n[1], newNodeIT(nkObjConstr, n.info, n[1].typ), {})
-      else:
-        depsArgs(w, n)
-
-proc possibleAliases(w: var W; result: var seq[ptr TSym]) =
-  # this is an expensive fixpoint iteration. We could speed up this analysis
-  # by a smarter data-structure but we wait until profiling shows us it's
-  # expensive. Usually 'w.assignments' is small enough.
-  var alreadySeen = initIntSet()
-  template addNoDup(x) =
-    if not alreadySeen.containsOrIncl(x.id): result.add x
-  for x in result: alreadySeen.incl x.id
-
-  var todo = 0
-  while todo < result.len:
-    let x = result[todo]
-    inc todo
-    for i in 0..<w.assignments.len:
-      let a = addr(w.assignments[i])
-      #if a.srcHasSym(x):
-      #  # y = f(..., x, ...)
-      #  for i in 0..<a.destNoTc: addNoDup a.dest[i]
-      if a.destNoTc > 0 and a.dest[0] == x and rootIsSym in a.destInfo:
-        # x = f(..., y, ....)
-        for i in 0..<a.srcNoTc: addNoDup a.src[i]
-
-proc markWriteOrEscape(w: var W; conf: ConfigRef) =
-  ## Both 'writes' and 'escapes' effects ultimately only care
-  ## about *parameters*.
-  ## However, due to aliasing, even locals that might not look as parameters
-  ## have to count as parameters if they can alias a parameter:
-  ##
-  ##..code-block:: nim
-  ##   proc modifies(n: Node) {.writes: [n].} =
-  ##     let x = n
-  ##     x.data = "abc"
-  ##
-  ## We call a symbol *parameter-like* if it is a parameter or can alias a
-  ## parameter.
-  ## Let ``p``, ``q`` be *parameter-like* and ``x``, ``y`` be general
-  ## expressions.
-  ##
-  ## A write then looks like ``p[] = x``.
-  ## An escape looks like ``p[] = q`` or more generally
-  ## like ``p[] = f(q)`` where ``f`` can forward ``q``.
-  for i in 0..<w.assignments.len:
-    let a = addr(w.assignments[i])
-    if a.destInfo != {}:
-      possibleAliases(w, a.dest)
-
-    if {rootIsHeapAccess, markAsWrittenTo} * a.destInfo != {}:
-      for p in a.dest:
-        if p.kind == skParam and p.owner == w.owner:
-          incl(p.flags, sfWrittenTo)
-          if w.owner.kind == skFunc and p.typ.kind notin {tyVar}:
-            localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
-
-    if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
-      var destIsParam = false
-      for p in a.dest:
-        if p.kind in {skResult, skParam} and p.owner == w.owner:
-          destIsParam = true
-          break
-      if destIsParam:
-        possibleAliases(w, a.src)
-        for p in a.src:
-          if p.kind == skParam and p.owner == w.owner:
-            incl(p.flags, sfEscapes)
-
-proc trackWrites*(owner: PSym; body: PNode; conf: ConfigRef) =
-  var w: W
-  w.owner = owner
-  w.assignments = @[]
-  # Phase 1: Collect and preprocess any assignments in the proc body:
-  deps(w, body)
-  # Phase 2: Compute the 'writes' and 'escapes' effects:
-  markWriteOrEscape(w, conf)
-  if w.returnsNew != asgnOther and not isEmptyType(owner.typ[0]) and
-      containsGarbageCollectedRef(owner.typ[0]):
-    incl(owner.typ.flags, tfReturnsNew)