summary refs log tree commit diff stats
path: root/compiler/lowerings.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/lowerings.nim')
-rw-r--r--compiler/lowerings.nim740
1 files changed, 202 insertions, 538 deletions
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index f895e887c..2c9c4cb32 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,239 +12,291 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, astalgo, types, idents, magicsys, msgs, options
-from trees import getMagic
+import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
+  lineinfos
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 proc newDeref*(n: PNode): PNode {.inline.} =
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
-  addSon(result, n)
-
-proc newTupleAccess*(tup: PNode, i: int): PNode =
-  result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
-                     abstractInst).sons[i])
-  addSon(result, copyTree(tup))
-  var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
-  lit.intVal = i
-  addSon(result, lit)
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
+  result.add n
+
+proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
+  if tup.kind == nkHiddenAddr:
+    result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
+    result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
+    result[0].add tup[0]
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result[0].add lit
+  else:
+    result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
+                       abstractInst)[i])
+    result.add copyTree(tup)
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result.add lit
 
 proc addVar*(father, v: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
-  vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = ast.emptyNode
-  addSon(father, vpart)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = vpart[1]
+  father.add vpart
 
-proc newAsgnStmt(le, ri: PNode): PNode =
+proc addVar*(father, v, value: PNode) =
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = value
+  father.add vpart
+
+proc newAsgnStmt*(le, ri: PNode): PNode =
   result = newNodeI(nkAsgn, le.info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
+  result[0] = le
+  result[1] = ri
+
+proc newFastAsgnStmt*(le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result[0] = le
+  result[1] = ri
 
-proc newFastAsgnStmt(le, ri: PNode): PNode =
+proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
   result = newNodeI(nkFastAsgn, le.info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
+  result[0] = le
+  result[1] = newNodeIT(nkCall, ri.info, ri.typ)
+  result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
+  result[1].add ri
 
-proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
+  var tempAsNode: PNode
+  let avoidTemp = value.kind == nkSym
+  if avoidTemp:
+    tempAsNode = value
+  else:
+    var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                  owner, value.info, g.config.options)
+    temp.typ = skipTypes(value.typ, abstractInst)
+    incl(temp.flags, sfFromGeneric)
+    tempAsNode = newSymNode(temp)
+
+  var v = newNodeI(nkVarSection, value.info)
+  if not avoidTemp:
+    v.addVar(tempAsNode, value)
+  result.add(v)
+
+  for i in 0..<n.len-2:
+    let val = newTupleAccess(g, tempAsNode, i)
+    if n[i].kind == nkSym: v.addVar(n[i], val)
+    else: result.add newAsgnStmt(n[i], val)
+
+proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
+  ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
+  ## freely, multiple times. This is frequently required and such a builtin would also be
+  ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
+  result = newNodeIT(nkStmtListExpr, value.info, value.typ)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                    owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
-  var v = newNodeI(nkVarSection, value.info)
+  var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp)
   v.addVar(tempAsNode)
   result.add(v)
-
   result.add newAsgnStmt(tempAsNode, value)
-  for i in 0 .. n.len-3:
-    if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
-    result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
+  result.add tempAsNode
 
 proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
   result = newNodeI(nkBracketExpr, tup.info)
-  addSon(result, copyTree(tup))
+  result.add copyTree(tup)
   var lit = newNodeI(nkIntLit, tup.info)
   lit.intVal = i
-  addSon(result, lit)
+  result.add lit
 
 proc newTryFinally*(body, final: PNode): PNode =
-  result = newTree(nkTryStmt, body, newTree(nkFinally, final))
+  result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
-  let value = n.lastSon
-  result = newNodeI(nkStmtList, n.info)
-
-  var temp = newSym(skLet, getIdent("_"), owner, value.info)
-  var v = newNodeI(nkLetSection, value.info)
-  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
-
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = value
-  addSon(v, vpart)
-  result.add(v)
-
-  let lhs = n.sons[0]
-  for i in 0 .. lhs.len-1:
-    result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
-
-proc lowerSwap*(n: PNode; owner: PSym): PNode =
+proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
-  var temp = newSym(skVar, getIdent(genPrefix), owner, n.info)
-  temp.typ = n.sons[1].typ
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
+  temp.typ = n[1].typ
   incl(temp.flags, sfFromGeneric)
+  incl(temp.flags, sfGenSym)
 
   var v = newNodeI(nkVarSection, n.info)
   let tempAsNode = newSymNode(temp)
 
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
-  vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = n[1]
-  addSon(v, vpart)
+  vpart[0] = tempAsNode
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = n[1]
+  v.add vpart
 
   result.add(v)
   result.add newFastAsgnStmt(n[1], n[2])
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
-proc createObj*(owner: PSym, info: TLineInfo; final=true): PType =
-  result = newType(tyObject, owner)
+proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
+  result = newType(tyObject, idgen, owner)
   if final:
     rawAddSon(result, nil)
     incl result.flags, tfFinal
   else:
-    rawAddSon(result, getCompilerProc("RootObj").typ)
+    rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
-  let s = newSym(skType, getIdent("Env_" & info.toFilename),
-                  owner, info)
+  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
+                  idgen, owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
   result.sym = s
 
+template fieldCheck {.dirty.} =
+  when false:
+    if tfCheckedForDestructor in obj.flags:
+      echo "missed field ", field.name.s
+      writeStackTrace()
+
 proc rawAddField*(obj: PType; field: PSym) =
   assert field.kind == skField
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
+  field.position = obj.n.len
+  obj.n.add newSymNode(field)
+  propagateToOwner(obj, field.typ)
+  fieldCheck()
 
 proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
   # returns a[].field as a node
   assert field.kind == skField
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
-  addSon(deref, a)
+  deref.typ = a.typ.skipTypes(abstractInst)[0]
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
 proc rawDirectAccess*(obj, field: PSym): PNode =
   # returns a.field as a node
   assert field.kind == skField
   result = newNodeI(nkDotExpr, field.info)
-  addSon(result, newSymNode obj)
-  addSon(result, newSymNode field)
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc lookupInRecord(n: PNode, id: int): PSym =
+proc lookupInRecord(n: PNode, id: ItemId): PSym =
   result = nil
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = lookupInRecord(n.sons[i], id)
+    for i in 0..<n.len:
+      result = lookupInRecord(n[i], id)
       if result != nil: return
   of nkRecCase:
-    if n.sons[0].kind != nkSym: return
-    result = lookupInRecord(n.sons[0], id)
+    if n[0].kind != nkSym: return
+    result = lookupInRecord(n[0], id)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = lookupInRecord(lastSon(n.sons[i]), id)
+        result = lookupInRecord(lastSon(n[i]), id)
         if result != nil: return
       else: discard
   of nkSym:
-    if n.sym.id == -abs(id): result = n.sym
+    if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
   else: discard
 
-proc addField*(obj: PType; s: PSym) =
+proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
-  field.id = -s.id
-  let t = skipIntLit(s.typ)
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
+                     idgen, s.owner, s.info, s.options)
+  field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+  let t = skipIntLit(s.typ, idgen)
   field.typ = t
-  assert t.kind != tyStmt
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
-
-proc addUniqueField*(obj: PType; s: PSym) =
-  if lookupInRecord(obj.n, s.id) == nil:
-    var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
-    field.id = -s.id
-    let t = skipIntLit(s.typ)
+  if s.kind in {skLet, skVar, skField, skForVar}:
+    #field.bitsize = s.bitsize
+    field.alignment = s.alignment
+  assert t.kind != tyTyped
+  propagateToOwner(obj, t)
+  field.position = obj.n.len
+  # sfNoInit flag for skField is used in closureiterator codegen
+  field.flags = s.flags * {sfCursor, sfNoInit}
+  obj.n.add newSymNode(field)
+  fieldCheck()
+  result = field
+
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
+  result = lookupInRecord(obj.n, s.itemId)
+  if result == nil:
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
+                       s.owner, s.info, s.options)
+    field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+    let t = skipIntLit(s.typ, idgen)
     field.typ = t
-    assert t.kind != tyStmt
-    field.position = sonsLen(obj.n)
-    addSon(obj.n, newSymNode(field))
+    assert t.kind != tyTyped
+    propagateToOwner(obj, t)
+    field.position = obj.n.len
+    obj.n.add newSymNode(field)
+    result = field
 
-proc newDotExpr(obj, b: PSym): PNode =
+proc newDotExpr*(obj, b: PSym): PNode =
   result = newNodeI(nkDotExpr, obj.info)
-  let field = lookupInRecord(obj.typ.n, b.id)
+  let field = lookupInRecord(obj.typ.n, b.itemId)
   assert field != nil, b.name.s
-  addSon(result, newSymNode(obj))
-  addSon(result, newSymNode(field))
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
+proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
   while true:
     assert t.kind == tyObject
     field = lookupInRecord(t.n, b)
     if field != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
-  internalAssert field != nil
-  addSon(deref, a)
+  assert field != nil
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
+proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
-  let bb = getIdent(b)
+  let bb = getIdent(cache, b)
   while true:
     assert t.kind == tyObject
     field = getSymFromList(t.n, bb)
     if field != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
-  internalAssert field != nil
-  addSon(deref, a)
+  assert field != nil
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
 proc getFieldFromObj*(t: PType; v: PSym): PSym =
@@ -252,455 +304,67 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
   var t = t
   while true:
     assert t.kind == tyObject
-    result = lookupInRecord(t.n, v.id)
+    result = lookupInRecord(t.n, v.itemId)
     if result != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
 
 proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
   # returns a[].b as a node
-  result = indirectAccess(a, b.id, info)
+  result = indirectAccess(a, b.itemId, info)
 
 proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
   result = indirectAccess(newSymNode(a), b, info)
 
-proc genAddrOf*(n: PNode): PNode =
+proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
   result = newNodeI(nkAddr, n.info, 1)
-  result.sons[0] = n
-  result.typ = newType(tyPtr, n.typ.owner)
+  result[0] = n
+  result.typ = newType(typeKind, idgen, n.typ.owner)
   result.typ.rawAddSon(n.typ)
 
-proc genDeref*(n: PNode): PNode =
-  result = newNodeIT(nkHiddenDeref, n.info,
-                     n.typ.skipTypes(abstractInst).sons[0])
+proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
+  result = newNodeIT(k, n.info,
+                     n.typ.skipTypes(abstractInst).elementType)
   result.add n
 
-proc callCodegenProc*(name: string, arg1: PNode;
-                      arg2, arg3, optionalArgs: PNode = nil): PNode =
-  result = newNodeI(nkCall, arg1.info)
-  let sym = magicsys.getCompilerProc(name)
+proc callCodegenProc*(g: ModuleGraph; name: string;
+                      info: TLineInfo = unknownLineInfo;
+                      arg1: PNode = nil, arg2: PNode = nil,
+                      arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
+  result = newNodeI(nkCall, info)
+  let sym = magicsys.getCompilerProc(g, name)
   if sym == nil:
-    localError(arg1.info, errSystemNeeds, name)
+    localError(g.config, info, "system module needs: " & name)
   else:
     result.add newSymNode(sym)
-    result.add arg1
+    if arg1 != nil: result.add arg1
     if arg2 != nil: result.add arg2
     if arg3 != nil: result.add arg3
     if optionalArgs != nil:
-      for i in 1..optionalArgs.len-3:
+      for i in 1..<optionalArgs.len-2:
         result.add optionalArgs[i]
-    result.typ = sym.typ.sons[0]
-
-proc callProc(a: PNode): PNode =
-  result = newNodeI(nkCall, a.info)
-  result.add a
-  result.typ = a.typ.sons[0]
-
-# we have 4 cases to consider:
-# - a void proc --> nothing to do
-# - a proc returning GC'ed memory --> requires a flowVar
-# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
-# - not in a parallel environment --> requires a flowVar for memory safety
-type
-  TSpawnResult* = enum
-    srVoid, srFlowVar, srByVar
-  TFlowVarKind = enum
-    fvInvalid # invalid type T for 'FlowVar[T]'
-    fvGC      # FlowVar of a GC'ed type
-    fvBlob    # FlowVar of a blob type
-
-proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
-  if t.isEmptyType: srVoid
-  elif inParallel and not containsGarbageCollectedRef(t): srByVar
-  else: srFlowVar
-
-proc flowVarKind(t: PType): TFlowVarKind =
-  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
-  elif containsGarbageCollectedRef(t): fvInvalid
-  else: fvBlob
-
-proc typeNeedsNoDeepCopy(t: PType): bool =
-  var t = t.skipTypes(abstractInst)
-  # for the tconvexhull example (and others) we're a bit lax here and pretend
-  # seqs and strings are *by value* only and 'shallow' doesn't exist!
-  if t.kind == tyString: return true
-  # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
-  # for the stricter check and likewise we can skip 'seq' for a less
-  # strict check:
-  if t.kind in {tyVar, tySequence}: t = t.sons[0]
-  result = not containsGarbageCollectedRef(t)
-
-proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
-                 v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
-  result.typ = typ
-  incl(result.flags, sfFromGeneric)
-
-  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
-  vpart.sons[0] = newSymNode(result)
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
-  varSection.add vpart
-  if varInit != nil:
-    if useShallowCopy and typeNeedsNoDeepCopy(typ):
-      varInit.add newFastAsgnStmt(newSymNode(result), v)
-    else:
-      let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
-      deepCopyCall.sons[0] = newSymNode(getSysMagic("deepCopy", mDeepCopy))
-      deepCopyCall.sons[1] = newSymNode(result)
-      deepCopyCall.sons[2] = v
-      varInit.add deepCopyCall
-
-discard """
-We generate roughly this:
-
-proc f_wrapper(thread, args) =
-  barrierEnter(args.barrier)  # for parallel statement
-  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
-                 # depending on whether we're in a 'parallel' statement
-  var b = args.b
-  var fv = args.fv
-
-  fv.owner = thread # optional
-  nimArgsPassingDone() # signal parent that the work is done
-  #
-  args.fv.blob = f(a, b, ...)
-  nimFlowVarSignal(args.fv)
-
-  # - or -
-  f(a, b, ...)
-  barrierLeave(args.barrier)  # for parallel statement
-
-stmtList:
-  var scratchObj
-  scratchObj.a = a
-  scratchObj.b = b
-
-  nimSpawn(f_wrapper, addr scratchObj)
-  scratchObj.fv # optional
-
-"""
-
-proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
-                       varSection, varInit, call, barrier, fv: PNode;
-                       spawnKind: TSpawnResult): PSym =
-  var body = newNodeI(nkStmtList, f.info)
-  var threadLocalBarrier: PSym
-  if barrier != nil:
-    var varSection2 = newNodeI(nkVarSection, barrier.info)
-    threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
-                                     barrier.typ, barrier)
-    body.add varSection2
-    body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
-  var threadLocalProm: PSym
-  if spawnKind == srByVar:
-    threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
-  elif fv != nil:
-    internalAssert fv.typ.kind == tyGenericInst
-    threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
-  body.add varSection
-  body.add varInit
-  if fv != nil and spawnKind != srByVar:
-    # generate:
-    #   fv.owner = threadParam
-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      "owner", fv.info), threadParam.newSymNode)
-
-  body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode)
-  if spawnKind == srByVar:
-    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
-  elif fv != nil:
-    let fk = fv.typ.sons[1].flowVarKind
-    if fk == fvInvalid:
-      localError(f.info, "cannot create a flowVar of type: " &
-        typeToString(fv.typ.sons[1]))
-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      if fk == fvGC: "data" else: "blob", fv.info), call)
-    if fk == fvGC:
-      let incRefCall = newNodeI(nkCall, fv.info, 2)
-      incRefCall.sons[0] = newSymNode(getSysMagic("GCref", mGCref))
-      incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
-                                          "data", fv.info)
-      body.add incRefCall
-    if barrier == nil:
-      # by now 'fv' is shared and thus might have beeen overwritten! we need
-      # to use the thread-local view instead:
-      body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
-  else:
-    body.add call
-  if barrier != nil:
-    body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode)
-
-  var params = newNodeI(nkFormalParams, f.info)
-  params.add emptyNode
-  params.add threadParam.newSymNode
-  params.add argsParam.newSymNode
-
-  var t = newType(tyProc, threadParam.owner)
-  t.rawAddSon nil
-  t.rawAddSon threadParam.typ
-  t.rawAddSon argsParam.typ
-  t.n = newNodeI(nkFormalParams, f.info)
-  t.n.add newNodeI(nkEffectList, f.info)
-  t.n.add threadParam.newSymNode
-  t.n.add argsParam.newSymNode
-
-  let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
-  result = newSym(skProc, getIdent(name), argsParam.owner, f.info)
-  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
-  result.typ = t
-
-proc createCastExpr(argsParam: PSym; objType: PType): PNode =
-  result = newNodeI(nkCast, argsParam.info)
-  result.add emptyNode
-  result.add newSymNode(argsParam)
-  result.typ = newType(tyPtr, objType.owner)
-  result.typ.rawAddSon(objType)
-
-proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
-                             castExpr, call,
-                             varSection, varInit, result: PNode) =
-  let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
-  for i in 1 .. <n.len:
-    # we pick n's type here, which hopefully is 'tyArray' and not
-    # 'tyOpenArray':
-    var argType = n[i].typ.skipTypes(abstractInst)
-    if i < formals.len and formals[i].typ.kind == tyVar:
-      localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
-    #elif containsTyRef(argType):
-    #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
-
-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info)
-    field.typ = argType
-    objType.addField(field)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
-
-    let temp = addLocalVar(varSection, varInit, objType.owner, argType,
-                           indirectAccess(castExpr, field, n.info))
-    call.add(newSymNode(temp))
-
-proc getRoot*(n: PNode): PSym =
-  ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
-  ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
-  ## determined as the owner; ``obj`` in the example.
-  case n.kind
-  of nkSym:
-    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
-      result = n.sym
-  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
-      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = getRoot(n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    result = getRoot(n.sons[1])
-  of nkCallKinds:
-    if getMagic(n) == mSlice: result = getRoot(n.sons[1])
-  else: discard
+    result.typ = sym.typ.returnType
 
-proc newIntLit*(value: BiggestInt): PNode =
+proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
   result = nkIntLit.newIntNode(value)
-  result.typ = getSysType(tyInt)
+  result.typ = getSysType(g, info, tyInt)
 
-proc genHigh*(n: PNode): PNode =
+proc genHigh*(g: ModuleGraph; n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind == tyArray:
-    result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
   else:
     result = newNodeI(nkCall, n.info, 2)
-    result.typ = getSysType(tyInt)
-    result.sons[0] = newSymNode(getSysMagic("high", mHigh))
-    result.sons[1] = n
-
-proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
-                             castExpr, call,
-                             varSection, varInit, result: PNode) =
-  let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
-  # we need to copy the foreign scratch object fields into local variables
-  # for correctness: These are called 'threadLocal' here.
-  for i in 1 .. <n.len:
-    let n = n[i]
-    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
-                            abstractInst)
-    #if containsTyRef(argType):
-    #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
-
-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info)
-
-    if argType.kind in {tyVarargs, tyOpenArray}:
-      # important special case: we always create a zero-copy slice:
-      let slice = newNodeI(nkCall, n.info, 4)
-      slice.typ = n.typ
-      slice.sons[0] = newSymNode(createMagic("slice", mSlice))
-      slice.sons[0].typ = getSysType(tyInt) # fake type
-      var fieldB = newSym(skField, tmpName, objType.owner, n.info)
-      fieldB.typ = getSysType(tyInt)
-      objType.addField(fieldB)
-
-      if getMagic(n) == mSlice:
-        let a = genAddrOf(n[1])
-        field.typ = a.typ
-        objType.addField(field)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-
-        var fieldA = newSym(skField, tmpName, objType.owner, n.info)
-        fieldA.typ = getSysType(tyInt)
-        objType.addField(fieldA)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
-
-        let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ,
-                                      indirectAccess(castExpr, fieldA, n.info),
-                                      useShallowCopy=true)
-        slice.sons[2] = threadLocal.newSymNode
-      else:
-        let a = genAddrOf(n)
-        field.typ = a.typ
-        objType.addField(field)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
-
-        slice.sons[2] = newIntLit(0)
-      # the array itself does not need to go through a thread local variable:
-      slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
-
-      let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ,
-                                    indirectAccess(castExpr, fieldB, n.info),
-                                    useShallowCopy=true)
-      slice.sons[3] = threadLocal.newSymNode
-      call.add slice
-    elif (let size = computeSize(argType); size < 0 or size > 16) and
-        n.getRoot != nil:
-      # it is more efficient to pass a pointer instead:
-      let a = genAddrOf(n)
-      field.typ = a.typ
-      objType.addField(field)
-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-      let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
-                                    indirectAccess(castExpr, field, n.info),
-                                    useShallowCopy=true)
-      call.add(genDeref(threadLocal.newSymNode))
-    else:
-      # boring case
-      field.typ = argType
-      objType.addField(field)
-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
-      let threadLocal = addLocalVar(varSection, varInit,
-                                    objType.owner, field.typ,
-                                    indirectAccess(castExpr, field, n.info),
-                                    useShallowCopy=true)
-      call.add(threadLocal.newSymNode)
-
-proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
-                       barrier, dest: PNode = nil): PNode =
-  # if 'barrier' != nil, then it is in a 'parallel' section and we
-  # generate quite different code
-  let n = spawnExpr[^2]
-  let spawnKind = spawnResult(retType, barrier!=nil)
-  case spawnKind
-  of srVoid:
-    internalAssert dest == nil
-    result = newNodeI(nkStmtList, n.info)
-  of srFlowVar:
-    internalAssert dest == nil
-    result = newNodeIT(nkStmtListExpr, n.info, retType)
-  of srByVar:
-    if dest == nil: localError(n.info, "'spawn' must not be discarded")
-    result = newNodeI(nkStmtList, n.info)
-
-  if n.kind notin nkCallKinds:
-    localError(n.info, "'spawn' takes a call expression")
-    return
-  if optThreadAnalysis in gGlobalOptions:
-    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
-      localError(n.info, "'spawn' takes a GC safe call expression")
-  var
-    threadParam = newSym(skParam, getIdent"thread", owner, n.info)
-    argsParam = newSym(skParam, getIdent"args", owner, n.info)
-  block:
-    let ptrType = getSysType(tyPointer)
-    threadParam.typ = ptrType
-    argsParam.typ = ptrType
-    argsParam.position = 1
-
-  var objType = createObj(owner, n.info)
-  incl(objType.flags, tfFinal)
-  let castExpr = createCastExpr(argsParam, objType)
-
-  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
-  block:
-    scratchObj.typ = objType
-    incl(scratchObj.flags, sfFromGeneric)
-    var varSectionB = newNodeI(nkVarSection, n.info)
-    varSectionB.addVar(scratchObj.newSymNode)
-    result.add varSectionB
-
-  var call = newNodeIT(nkCall, n.info, n.typ)
-  var fn = n.sons[0]
-  # templates and macros are in fact valid here due to the nature of
-  # the transformation:
-  if fn.kind == nkClosure:
-    localError(n.info, "closure in spawn environment is not allowed")
-  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
-                                               skFunc, skMethod, skConverter}):
-    # for indirect calls we pass the function pointer in the scratchObj
-    var argType = n[0].typ.skipTypes(abstractInst)
-    var field = newSym(skField, getIdent"fn", owner, n.info)
-    field.typ = argType
-    objType.addField(field)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
-    fn = indirectAccess(castExpr, field, n.info)
-  elif fn.kind == nkSym and fn.sym.kind == skIterator:
-    localError(n.info, "iterator in spawn environment is not allowed")
-  elif fn.typ.callConv == ccClosure:
-    localError(n.info, "closure in spawn environment is not allowed")
-
-  call.add(fn)
-  var varSection = newNodeI(nkVarSection, n.info)
-  var varInit = newNodeI(nkStmtList, n.info)
-  if barrier.isNil:
-    setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
-                            varSection, varInit, result)
+    result.typ = getSysType(g, n.info, tyInt)
+    result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
+    result[1] = n
+
+proc genLen*(g: ModuleGraph; n: PNode): PNode =
+  if skipTypes(n.typ, abstractVar).kind == tyArray:
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
   else:
-    setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
-                            varSection, varInit, result)
-
-  var barrierAsExpr: PNode = nil
-  if barrier != nil:
-    let typ = newType(tyPtr, owner)
-    typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
-    var field = newSym(skField, getIdent"barrier", owner, n.info)
-    field.typ = typ
-    objType.addField(field)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
-    barrierAsExpr = indirectAccess(castExpr, field, n.info)
-
-  var fvField, fvAsExpr: PNode = nil
-  if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info)
-    field.typ = retType
-    objType.addField(field)
-    fvField = newDotExpr(scratchObj, field)
-    fvAsExpr = indirectAccess(castExpr, field, n.info)
-    # create flowVar:
-    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
-    if barrier == nil:
-      result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
-
-  elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info)
-    field.typ = newType(tyPtr, objType.owner)
-    field.typ.rawAddSon(retType)
-    objType.addField(field)
-    fvAsExpr = indirectAccess(castExpr, field, n.info)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
-
-  let wrapper = createWrapperProc(fn, threadParam, argsParam,
-                                  varSection, varInit, call,
-                                  barrierAsExpr, fvAsExpr, spawnKind)
-  result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
-                             genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
-
-  if spawnKind == srFlowVar: result.add fvField
+    result = newNodeI(nkCall, n.info, 2)
+    result.typ = getSysType(g, n.info, tyInt)
+    result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
+    result[1] = n
+