summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-10-29 14:47:22 +0100
committerGitHub <noreply@github.com>2023-10-29 14:47:22 +0100
commit0c26d19e228e831393cb5d2c1f43660362d349c9 (patch)
tree6b989d3175a5332dcd61489e22350f3748b89b09
parent94ffc183323c859fc5a395404ac6035ae29cbc5a (diff)
downloadNim-0c26d19e228e831393cb5d2c1f43660362d349c9.tar.gz
NIR: VM + refactorings (#22835)
-rw-r--r--compiler/ccgcalls.nim14
-rw-r--r--compiler/ic/rodfiles.nim1
-rw-r--r--compiler/nir/ast2ir.nim509
-rw-r--r--compiler/nir/nir.nim55
-rw-r--r--compiler/nir/nirc.nim69
-rw-r--r--compiler/nir/nirfiles.nim73
-rw-r--r--compiler/nir/nirinsts.nim97
-rw-r--r--compiler/nir/nirtypes.nim28
-rw-r--r--compiler/nir/nirvm.nim856
-rw-r--r--compiler/nir/types2ir.nim481
-rw-r--r--lib/system/ansi_c.nim4
11 files changed, 1538 insertions, 649 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 494f3c8c6..ad84be3f9 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -221,7 +221,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
     let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
     result.add x & ", " & y
   else:
-    var a: TLoc = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
+    var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
     case skipTypes(a.t, abstractVar+{tyStatic}).kind
     of tyOpenArray, tyVarargs:
       if reifiedOpenArray(n):
@@ -277,7 +277,7 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
   genAssignment(p, result, a, {})
 
 proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
-  var a: TLoc = initLocExpr(p, n[0])
+  var a = initLocExpr(p, n[0])
   appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
 
 proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
@@ -287,7 +287,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
   elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
     var n = if n.kind != nkHiddenAddr: n else: n[0]
     openArrayLoc(p, param.typ, n, result)
-  elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and 
+  elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and
     (optByRef notin param.options or not p.module.compileToCpp):
     a = initLocExpr(p, n)
     if n.kind in {nkCharLit..nkNilLit}:
@@ -417,7 +417,7 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
 
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   # this is a hotspot in the compiler
-  var op: TLoc = initLocExpr(p, ri[0])
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
@@ -439,7 +439,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
   const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
 
-  var op: TLoc = initLocExpr(p, ri[0])
+  var op = initLocExpr(p, ri[0])
 
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
@@ -672,7 +672,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
         result.add(substr(pat, start, i - 1))
 
 proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  var op: TLoc = initLocExpr(p, ri[0])
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
@@ -716,7 +716,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
 proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   # generates a crappy ObjC call
-  var op: TLoc = initLocExpr(p, ri[0])
+  var op = initLocExpr(p, ri[0])
   var pl = "["
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index 4968c5924..3505bfdfb 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -98,6 +98,7 @@ type
     backendFlagsSection
     aliveSymsSection # beware, this is stored in a `.alivesyms` file.
     sideChannelSection
+    symnamesSection
 
   RodFileError* = enum
     ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim
index 0920f5d8a..b1a90e9bf 100644
--- a/compiler/nir/ast2ir.nim
+++ b/compiler/nir/ast2ir.nim
@@ -15,22 +15,27 @@ from ".." / lowerings import lowerSwap, lowerTupleUnpacking
 from ".." / pathutils import customPath
 import .. / ic / bitabs
 
-import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir
+import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir, nirfiles
 
 when defined(nimCompilerStacktraceHints):
   import std/stackframes
 
 type
   ModuleCon* = ref object
-    man*: LineInfoManager
-    lit*: Literals
-    types*: TypesCon
+    nirm*: ref NirModule
+    types: TypesCon
     module*: PSym
     graph*: ModuleGraph
+    symnames*: SymNames
     nativeIntId, nativeUIntId: TypeId
+    strPayloadId: (TypeId, TypeId)
     idgen: IdGenerator
-    processedProcs: HashSet[ItemId]
+    processedProcs, pendingProcsAsSet: HashSet[ItemId]
     pendingProcs: seq[PSym] # procs we still need to generate code for
+    noModularity*: bool
+    inProc: int
+    toSymId: Table[ItemId, SymId]
+    symIdCounter: int32
 
   ProcCon* = object
     config*: ConfigRef
@@ -39,7 +44,7 @@ type
     lastFileVal: LitId
     labelGen: int
     exitLabel: LabelId
-    code*: Tree
+    #code*: Tree
     blocks: seq[(PSym, LabelId)]
     sm: SlotManager
     idgen: IdGenerator
@@ -47,10 +52,13 @@ type
     prc: PSym
     options: TOptions
 
-proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym): ModuleCon =
-  let lit = Literals() # must be shared
-  result = ModuleCon(graph: graph, types: initTypesCon(config, lit),
-    idgen: idgen, module: module, lit: lit)
+template code(c: ProcCon): Tree = c.m.nirm.code
+
+proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym;
+                    nirm: ref NirModule): ModuleCon =
+  #let lit = Literals() # must be shared
+  result = ModuleCon(graph: graph, types: initTypesCon(config), nirm: nirm,
+    idgen: idgen, module: module)
   case config.target.intSize
   of 2:
     result.nativeIntId = Int16Id
@@ -61,10 +69,11 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m
   else:
     result.nativeIntId = Int64Id
     result.nativeUIntId = UInt16Id
+  result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types)
 
 proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
   ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config,
-    lit: m.lit, idgen: m.idgen,
+    lit: m.nirm.lit, idgen: m.idgen,
     options: if prc != nil: prc.options
              else: config.options)
 
@@ -77,7 +86,7 @@ proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo =
     # remember the entry:
     c.lastFileKey = i.fileIndex
     c.lastFileVal = val
-  result = pack(c.m.man, val, int32 i.line, int32 i.col)
+  result = pack(c.m.nirm.man, val, int32 i.line, int32 i.col)
 
 proc bestEffort(c: ProcCon): TLineInfo =
   if c.prc != nil:
@@ -112,15 +121,41 @@ proc freeTemp(c: var ProcCon; tmp: Value) =
   if s != SymId(-1):
     freeTemp(c.sm, s)
 
+proc typeToIr(m: ModuleCon; t: PType): TypeId =
+  typeToIr(m.types, m.nirm.types, t)
+
+proc allocTemp(c: var ProcCon; t: TypeId): SymId =
+  if c.m.noModularity:
+    result = allocTemp(c.sm, t, c.m.symIdCounter)
+  else:
+    result = allocTemp(c.sm, t, c.idgen.symId)
+
+const
+  ListSymId = -1
+
+proc toSymId(c: var ProcCon; s: PSym): SymId =
+  if c.m.noModularity:
+    result = c.m.toSymId.getOrDefault(s.itemId, SymId(-1))
+    if result.int < 0:
+      inc c.m.symIdCounter
+      result = SymId(c.m.symIdCounter)
+      c.m.toSymId[s.itemId] = result
+      when ListSymId != -1:
+        if result.int == ListSymId or s.name.s == "echoBinSafe":
+          echo result.int, " is ", s.name.s, " ", c.m.graph.config $ s.info, " ", s.flags
+          writeStackTrace()
+  else:
+    result = SymId(s.itemId.item)
+
 proc getTemp(c: var ProcCon; n: PNode): Value =
   let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m.types, n.typ)
-  let tmp = allocTemp(c.sm, t, c.idgen.symId)
+  let t = typeToIr(c.m, n.typ)
+  let tmp = allocTemp(c, t)
   c.code.addSummon info, tmp, t
   result = localToValue(info, tmp)
 
 proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
-  let tmp = allocTemp(c.sm, t, c.idgen.symId)
+  let tmp = allocTemp(c, t)
   c.code.addSummon info, tmp, t
   result = localToValue(info, tmp)
 
@@ -274,7 +309,7 @@ proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) =
   else:
     let info = toLineInfo(c, n.info)
     build c.code, info, Asgn:
-      c.code.addTyped info, typeToIr(c.m.types, n.typ)
+      c.code.addTyped info, typeToIr(c.m, n.typ)
       c.code.copyTree d
       c.code.copyTree tmp
     freeTemp(c, tmp)
@@ -369,7 +404,7 @@ proc genCase(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   withTemp(tmp, n[0]):
     build c.code, info, Select:
-      c.code.addTyped info, typeToIr(c.m.types, n[0].typ)
+      c.code.addTyped info, typeToIr(c.m, n[0].typ)
       c.gen(n[0], tmp)
       for i in 1..<n.len:
         let section = newLabel(c.labelGen)
@@ -437,7 +472,7 @@ proc genCall(c: var ProcCon; n: PNode; d: var Value) =
     else:
       args.add genx(c, n[i])
 
-  let tb = typeToIr(c.m.types, n.typ)
+  let tb = typeToIr(c.m, n.typ)
   if not isEmptyType(n.typ):
     if isEmpty(d): d = getTemp(c, n)
     # XXX Handle problematic aliasing here: `a = f_canRaise(a)`.
@@ -450,7 +485,7 @@ proc genCall(c: var ProcCon; n: PNode; d: var Value) =
 
 proc genRaise(c: var ProcCon; n: PNode) =
   let info = toLineInfo(c, n.info)
-  let tb = typeToIr(c.m.types, n[0].typ)
+  let tb = typeToIr(c.m, n[0].typ)
 
   let d = genx(c, n[0])
   build c.code, info, SetExc:
@@ -494,7 +529,7 @@ proc genTry(c: var ProcCon; n: PNode; d: var Value) =
         let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
         let itinfo = toLineInfo(c, it[j].info)
         build c.code, itinfo, TestExc:
-          c.code.addTyped itinfo, typeToIr(c.m.types, typ)
+          c.code.addTyped itinfo, typeToIr(c.m, typ)
       if it.len == 1:
         let itinfo = toLineInfo(c, it.info)
         build c.code, itinfo, TestExc:
@@ -549,26 +584,30 @@ proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
       d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
       d.Tree.addLabel info, CheckedGoto, c.exitLabel
 
-proc genNew(c: var ProcCon; n: PNode; needsInit: bool) =
-  # If in doubt, always follow the blueprint of the C code generator for `mm:orc`.
-  let refType = n[1].typ.skipTypes(abstractInstOwned)
+proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) =
   assert refType.kind == tyRef
   let baseType = refType.lastSon
 
-  let info = toLineInfo(c, n.info)
+  let info = toLineInfo(c, ninfo)
   let codegenProc = magicsys.getCompilerProc(c.m.graph,
     if needsInit: "nimNewObj" else: "nimNewObjUninit")
-  let x = genx(c, n[1])
-  let refTypeIr = typeToIr(c.m.types, refType)
+  let refTypeIr = typeToIr(c.m, refType)
   buildTyped c.code, info, Asgn, refTypeIr:
-    copyTree c.code, x
+    copyTree c.code, d
     buildTyped c.code, info, Cast, refTypeIr:
       buildTyped c.code, info, Call, VoidPtrId:
-        let theProc = c.genx newSymNode(codegenProc, n.info)
+        let theProc = c.genx newSymNode(codegenProc, ninfo)
         copyTree c.code, theProc
         c.code.addImmediateVal info, int(getSize(c.config, baseType))
         c.code.addImmediateVal info, int(getAlign(c.config, baseType))
 
+proc genNew(c: var ProcCon; n: PNode; needsInit: bool) =
+  # If in doubt, always follow the blueprint of the C code generator for `mm:orc`.
+  let refType = n[1].typ.skipTypes(abstractInstOwned)
+  let d = genx(c, n[1])
+  rawGenNew c, d, refType, n.info, needsInit
+  freeTemp c, d
+
 proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let seqtype = skipTypes(n.typ, abstractVarRange)
@@ -577,15 +616,15 @@ proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
   if isEmpty(d): d = getTemp(c, n)
   # $1.len = 0
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    buildTyped c.code, info, FieldAt, c.m.nativeIntId:
+    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
       copyTree c.code, d
       c.code.addImmediateVal info, 0
     c.code.addImmediateVal info, 0
   # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
-  let payloadPtr = seqPayloadPtrType(c.m.types, seqtype)
+  let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
   buildTyped c.code, info, Asgn, payloadPtr:
     # $1.p
-    buildTyped c.code, info, FieldAt, payloadPtr:
+    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
       copyTree c.code, d
       c.code.addImmediateVal info, 1
     # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
@@ -602,18 +641,18 @@ proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
 proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) =
   let baseType = seqtype.lastSon
   # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
-  let payloadPtr = seqPayloadPtrType(c.m.types, seqtype)
+  let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
 
   # $1.len = $2
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    buildTyped c.code, info, FieldAt, c.m.nativeIntId:
+    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
       copyTree c.code, d
       c.code.addImmediateVal info, 0
     copyTree c.code, b
 
   buildTyped c.code, info, Asgn, payloadPtr:
     # $1.p
-    buildTyped c.code, info, FieldAt, payloadPtr:
+    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
       copyTree c.code, d
       c.code.addImmediateVal info, 1
     # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
@@ -651,7 +690,7 @@ template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ:
   if isEmpty(d):
     body(Tree d)
   else:
-    buildTyped c.code, info, Asgn, typeToIr(c.m.types, typ):
+    buildTyped c.code, info, Asgn, typeToIr(c.m, typ):
       copyTree c.code, d
       body(c.code)
 
@@ -659,7 +698,7 @@ proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
   let tmp2 = c.genx(n[2])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   template body(target) =
     buildTyped target, info, opc, t:
       if optOverflowCheck in c.options and opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}:
@@ -674,7 +713,7 @@ proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
   let tmp2 = c.genx(n[2])
-  let t = typeToIr(c.m.types, n[1].typ)
+  let t = typeToIr(c.m, n[1].typ)
   template body(target) =
     buildTyped target, info, opc, t:
       copyTree target, tmp
@@ -686,7 +725,7 @@ proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
 proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   template body(target) =
     buildTyped target, info, opc, t:
       copyTree target, tmp
@@ -695,7 +734,7 @@ proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
 
 proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) =
   let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m.types, skipTypes(n[1].typ, abstractVar))
+  let t = typeToIr(c.m, skipTypes(n[1].typ, abstractVar))
 
   let d = c.genx(n[1])
   let tmp = c.genx(n[2])
@@ -719,7 +758,7 @@ proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
   of tyOpenArray, tyVarargs:
     let xa = c.genx(a)
     template body(target) =
-      buildTyped target, info, FieldAt, c.m.nativeIntId:
+      buildTyped target, info, FieldAt, typeToIr(c.m, typ):
         copyTree target, xa
         target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1
     intoDest d, info, c.m.nativeIntId, body
@@ -742,7 +781,7 @@ proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
       if isEmpty(d): d = getTemp(c, n)
 
     template body(target) =
-      buildTyped target, info, FieldAt, c.m.nativeIntId:
+      buildTyped target, info, FieldAt, typeToIr(c.m, typ):
         copyTree target, xa
         target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
     intoDest d, info, c.m.nativeIntId, body
@@ -756,7 +795,7 @@ proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
 proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   template body(target) =
     buildTyped target, info, Sub, t:
       # Little hack: This works because we know that `0.0` is all 0 bits:
@@ -767,7 +806,7 @@ proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) =
 
 proc genHigh(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   var x = default(Value)
   genArrayLen(c, n, x)
   template body(target) =
@@ -783,7 +822,7 @@ proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) =
   let xb = c.genx(n[2])
   if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
 
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   template body(target) =
     buildTyped target, info, Call, t:
       let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
@@ -802,7 +841,7 @@ proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; ar
   let xa = c.genx(n[argAt])
   if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
 
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   template body(target) =
     buildTyped target, info, Call, t:
       let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
@@ -830,7 +869,7 @@ template sizeOfLikeMsg(name): string =
 proc genIsNil(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
-  let t = typeToIr(c.m.types, n[1].typ)
+  let t = typeToIr(c.m, n[1].typ)
   template body(target) =
     buildTyped target, info, Eq, t:
       copyTree target, tmp
@@ -855,8 +894,8 @@ proc genInBitset(c: var ProcCon; n: PNode; d: var Value) =
   let a = c.genx(n[1])
   let b = c.genx(n[2])
 
-  let t = bitsetBasetype(c.m.types, n[1].typ)
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
   let mask =
     case t
     of UInt8Id: 7
@@ -871,10 +910,10 @@ proc genInBitset(c: var ProcCon; n: PNode; d: var Value) =
     buildTyped target, info, BoolNot, Bool8Id:
       buildTyped target, info, Eq, t:
         buildTyped target, info, BitAnd, t:
-          if c.m.types.g[setType].kind != ArrayTy:
+          if c.m.nirm.types[setType].kind != ArrayTy:
             copyTree target, a
           else:
-            buildTyped target, info, ArrayAt, t:
+            buildTyped target, info, ArrayAt, setType:
               copyTree target, a
               buildTyped target, info, BitShr, t:
                 buildTyped target, info, Cast, expansion:
@@ -931,19 +970,19 @@ proc genInSet(c: var ProcCon; n: PNode; d: var Value) =
 proc genCard(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let a = c.genx(n[1])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
 
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
   if isEmpty(d): d = getTemp(c, n)
 
   buildTyped c.code, info, Asgn, t:
     copyTree c.code, d
     buildTyped c.code, info, Call, t:
-      if c.m.types.g[setType].kind == ArrayTy:
+      if c.m.nirm.types[setType].kind == ArrayTy:
         let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet")
         let theProc = c.genx newSymNode(codegenProc, n.info)
         copyTree c.code, theProc
-        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType):
+        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
           copyTree c.code, a
         c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
       elif t == UInt64Id:
@@ -963,11 +1002,11 @@ proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let a = c.genx(n[1])
   let b = c.genx(n[2])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
 
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
 
-  if c.m.types.g[setType].kind == ArrayTy:
+  if c.m.nirm.types[setType].kind == ArrayTy:
     if isEmpty(d): d = getTemp(c, n)
 
     buildTyped c.code, info, Asgn, t:
@@ -977,9 +1016,9 @@ proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
           let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem")
           let theProc = c.genx newSymNode(codegenProc, n.info)
           copyTree c.code, theProc
-          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType):
+          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
             copyTree c.code, a
-          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType):
+          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
             copyTree c.code, b
           c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
         c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
@@ -995,7 +1034,7 @@ proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
   freeTemp c, a
 
 proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) =
-  let tmp = allocTemp(c.sm, c.m.nativeIntId, c.idgen.symId)
+  let tmp = allocTemp(c, c.m.nativeIntId)
   c.code.addSummon info, tmp, c.m.nativeIntId
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
     c.code.addSymUse info, tmp
@@ -1013,7 +1052,7 @@ proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (Sy
       c.code.gotoLabel info, Goto, result[2]
 
 proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) =
-  let tmp = allocTemp(c.sm, c.m.nativeIntId, c.idgen.symId)
+  let tmp = allocTemp(c, c.m.nativeIntId)
   c.code.addSummon info, tmp, c.m.nativeIntId
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
     c.code.addSymUse info, tmp
@@ -1044,12 +1083,12 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   let a = c.genx(n[1])
   let b = c.genx(n[2])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
 
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
 
-  if c.m.types.g[setType].kind == ArrayTy:
-    let elemType = bitsetBasetype(c.m.types, n[1].typ)
+  if c.m.nirm.types[setType].kind == ArrayTy:
+    let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
     if isEmpty(d): d = getTemp(c, n)
     #    "for ($1 = 0; $1 < $2; $1++):"
     #    "  $3 = (($4[$1] & ~ $5[$1]) == 0)"
@@ -1059,11 +1098,11 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
       copyTree c.code, d
       buildTyped c.code, info, Eq, elemType:
         buildTyped c.code, info, BitAnd, elemType:
-          buildTyped c.code, info, ArrayAt, elemType:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, a
             c.code.addSymUse info, idx
           buildTyped c.code, info, BitNot, elemType:
-            buildTyped c.code, info, ArrayAt, elemType:
+            buildTyped c.code, info, ArrayAt, setType:
               copyTree c.code, b
               c.code.addSymUse info, idx
         c.code.addIntVal c.lit.numbers, info, elemType, 0
@@ -1099,32 +1138,32 @@ proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
   let info = toLineInfo(c, n.info)
   let a = c.genx(n[1])
   let b = c.genx(n[2])
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
 
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
 
-  if c.m.types.g[setType].kind == ArrayTy:
-    let elemType = bitsetBasetype(c.m.types, n[1].typ)
+  if c.m.nirm.types[setType].kind == ArrayTy:
+    let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
     if isEmpty(d): d = getTemp(c, n)
     #    "for ($1 = 0; $1 < $2; $1++):"
     #    "  $3 = (($4[$1] & ~ $5[$1]) == 0)"
     #    "  if (!$3) break;"
     let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ)))
     buildTyped c.code, info, Asgn, elemType:
-      buildTyped c.code, info, ArrayAt, elemType:
+      buildTyped c.code, info, ArrayAt, setType:
         copyTree c.code, d
         c.code.addSymUse info, idx
       buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType:
-        buildTyped c.code, info, ArrayAt, elemType:
+        buildTyped c.code, info, ArrayAt, setType:
           copyTree c.code, a
           c.code.addSymUse info, idx
         if m == mMinusSet:
           buildTyped c.code, info, BitNot, elemType:
-            buildTyped c.code, info, ArrayAt, elemType:
+            buildTyped c.code, info, ArrayAt, setType:
               copyTree c.code, b
               c.code.addSymUse info, idx
         else:
-          buildTyped c.code, info, ArrayAt, elemType:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, b
             c.code.addSymUse info, idx
 
@@ -1150,9 +1189,9 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
   let a = c.genx(n[1])
   let b = c.genx(n[2])
 
-  let setType = typeToIr(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m, n[1].typ)
 
-  let t = bitsetBasetype(c.m.types, n[1].typ)
+  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
   let mask =
     case t
     of UInt8Id: 7
@@ -1161,17 +1200,17 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
     else: 63
 
   buildTyped c.code, info, Asgn, setType:
-    if c.m.types.g[setType].kind == ArrayTy:
+    if c.m.nirm.types[setType].kind == ArrayTy:
       if m == mIncl:
         # $1[(NU)($2)>>3] |=(1U<<($2&7U))
-        buildTyped c.code, info, ArrayAt, t:
+        buildTyped c.code, info, ArrayAt, setType:
           copyTree c.code, a
           buildTyped c.code, info, BitShr, t:
             buildTyped c.code, info, Cast, c.m.nativeUIntId:
               copyTree c.code, b
             addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
         buildTyped c.code, info, BitOr, t:
-          buildTyped c.code, info, ArrayAt, t:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, a
             buildTyped c.code, info, BitShr, t:
               buildTyped c.code, info, Cast, c.m.nativeUIntId:
@@ -1184,14 +1223,14 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
               c.code.addIntVal c.lit.numbers, info, t, 7
       else:
         # $1[(NU)($2)>>3] &= ~(1U<<($2&7U))
-        buildTyped c.code, info, ArrayAt, t:
+        buildTyped c.code, info, ArrayAt, setType:
           copyTree c.code, a
           buildTyped c.code, info, BitShr, t:
             buildTyped c.code, info, Cast, c.m.nativeUIntId:
               copyTree c.code, b
             addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
         buildTyped c.code, info, BitAnd, t:
-          buildTyped c.code, info, ArrayAt, t:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, a
             buildTyped c.code, info, BitShr, t:
               buildTyped c.code, info, Cast, c.m.nativeUIntId:
@@ -1234,9 +1273,9 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
   # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
   # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
   let info = toLineInfo(c, n.info)
-  let setType = typeToIr(c.m.types, n.typ)
+  let setType = typeToIr(c.m, n.typ)
   let size = int(getSize(c.config, n.typ))
-  let t = bitsetBasetype(c.m.types, n.typ)
+  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
   let mask =
     case t
     of UInt8Id: 7
@@ -1245,7 +1284,7 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
     else: 63
 
   if isEmpty(d): d = getTemp(c, n)
-  if c.m.types.g[setType].kind != ArrayTy:
+  if c.m.nirm.types[setType].kind != ArrayTy:
     buildTyped c.code, info, Asgn, setType:
       copyTree c.code, d
       c.code.addIntVal c.lit.numbers, info, t, 0
@@ -1300,14 +1339,14 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
         let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
 
         buildTyped c.code, info, Asgn, t:
-          buildTyped c.code, info, ArrayAt, t:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, d
             buildTyped c.code, info, BitShr, t:
               buildTyped c.code, info, Cast, c.m.nativeUIntId:
                 c.code.addSymUse info, idx
               addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
           buildTyped c.code, info, BitOr, t:
-            buildTyped c.code, info, ArrayAt, t:
+            buildTyped c.code, info, ArrayAt, setType:
               copyTree c.code, d
               buildTyped c.code, info, BitShr, t:
                 buildTyped c.code, info, Cast, c.m.nativeUIntId:
@@ -1327,14 +1366,14 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
         let a = genx(c, it)
         # $1[(NU)($2)>>3] |=(1U<<($2&7U))
         buildTyped c.code, info, Asgn, t:
-          buildTyped c.code, info, ArrayAt, t:
+          buildTyped c.code, info, ArrayAt, setType:
             copyTree c.code, d
             buildTyped c.code, info, BitShr, t:
               buildTyped c.code, info, Cast, c.m.nativeUIntId:
                 copyTree c.code, a
               addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
           buildTyped c.code, info, BitOr, t:
-            buildTyped c.code, info, ArrayAt, t:
+            buildTyped c.code, info, ArrayAt, setType:
               copyTree c.code, d
               buildTyped c.code, info, BitShr, t:
                 buildTyped c.code, info, Cast, c.m.nativeUIntId:
@@ -1350,16 +1389,16 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
 proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) =
   if isDeepConstExpr(n):
     let info = toLineInfo(c, n.info)
-    let setType = typeToIr(c.m.types, n.typ)
+    let setType = typeToIr(c.m, n.typ)
     let size = int(getSize(c.config, n.typ))
     let cs = toBitSet(c.config, n)
 
-    if c.m.types.g[setType].kind != ArrayTy:
+    if c.m.nirm.types[setType].kind != ArrayTy:
       template body(target) =
         target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
       intoDest d, info, setType, body
     else:
-      let t = bitsetBasetype(c.m.types, n.typ)
+      let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
       template body(target) =
         buildTyped target, info, ArrayConstr, setType:
           for i in 0..high(cs):
@@ -1387,32 +1426,36 @@ proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
   #    asgn(s, tmp0);
   #  }
   var args: seq[Value] = @[]
+  var argsRuntimeLen: seq[Value] = @[]
+
   var precomputedLen = 0
   for i in 1 ..< n.len:
     let it = n[i]
+    args.add genx(c, it)
     if skipTypes(it.typ, abstractVarRange).kind == tyChar:
       inc precomputedLen
     elif it.kind in {nkStrLit..nkTripleStrLit}:
       inc precomputedLen, it.strVal.len
-    args.add genx(c, it)
+    else:
+      argsRuntimeLen.add args[^1]
 
   # generate length computation:
-  var tmpLen = allocTemp(c.sm, c.m.nativeIntId, c.idgen.symId)
+  var tmpLen = allocTemp(c, c.m.nativeIntId)
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
     c.code.addSymUse info, tmpLen
     c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen
-  for a in mitems(args):
+  for a in mitems(argsRuntimeLen):
     buildTyped c.code, info, Asgn, c.m.nativeIntId:
       c.code.addSymUse info, tmpLen
       buildTyped c.code, info, CheckedAdd, c.m.nativeIntId:
         c.code.addSymUse info, tmpLen
-        buildTyped c.code, info, FieldAt, c.m.nativeIntId:
+        buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ):
           copyTree c.code, a
           c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
 
   var tmpStr = getTemp(c, n)
   #    ^ because of aliasing, we always go through a temporary
-  let t = typeToIr(c.m.types, n.typ)
+  let t = typeToIr(c.m, n.typ)
   buildTyped c.code, info, Asgn, t:
     copyTree c.code, tmpStr
     buildTyped c.code, info, Call, t:
@@ -1432,7 +1475,7 @@ proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
       #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
       let theProc = c.genx newSymNode(codegenProc, n.info)
       copyTree c.code, theProc
-      buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, t):
+      buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, t):
         copyTree c.code, tmpStr
       copyTree c.code, args[i-1]
     freeTemp c, args[i-1]
@@ -1469,13 +1512,14 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) =
     # if ($1.p == $2.p) goto lab1
     let lab1 = newLabel(c.labelGen)
 
-    let payloadType = seqPayloadPtrType(c.m.types, n1.typ)
+    let n1t = typeToIr(c.m, n1.typ)
+    let payloadType = seqPayloadPtrType(c.m.types, c.m.nirm.types, n1.typ)[0]
     buildTyped c.code, info, Select, Bool8Id:
       buildTyped c.code, info, Eq, payloadType:
-        buildTyped c.code, info, FieldAt, payloadType:
+        buildTyped c.code, info, FieldAt, n1t:
           copyTree c.code, a
           c.code.addImmediateVal info, 1 # (len, p)-pair
-        buildTyped c.code, info, FieldAt, payloadType:
+        buildTyped c.code, info, FieldAt, n1t:
           copyTree c.code, src
           c.code.addImmediateVal info, 1 # (len, p)-pair
 
@@ -1487,13 +1531,13 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) =
     gen(c, n[3])
     c.patch n, lab1
 
-    buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ):
+    buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
       copyTree c.code, a
       copyTree c.code, src
 
   else:
     if isEmpty(d): d = getTemp(c, n)
-    buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ):
+    buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
       copyTree c.code, d
       copyTree c.code, a
     var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved)
@@ -1502,9 +1546,9 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) =
       gen c, m, a
     else:
       var opB = c.genx(newSymNode(op))
-      buildTyped c.code, info, Call, typeToIr(c.m.types, n.typ):
+      buildTyped c.code, info, Call, typeToIr(c.m, n.typ):
         copyTree c.code, opB
-        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, typeToIr(c.m.types, n1.typ)):
+        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, typeToIr(c.m, n1.typ)):
           copyTree c.code, a
 
 template fieldAt(x: Value; i: int; t: TypeId): Tree =
@@ -1542,13 +1586,13 @@ proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) =
   let x = c.genx(n[1])
   let baseType = t.lastSon
 
-  let seqType = seqPayloadPtrType(c.m.types, t)
+  let seqType = typeToIr(c.m, t)
   let p = fieldAt(x, 0, seqType)
 
   # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0:
   #   alignedDealloc($1.p, NIM_ALIGNOF($2))
   buildIfNot p.eqNil(seqType):
-    buildIf fieldAt(Value(p), 0, c.m.nativeIntId).bitOp(BitAnd, 0).eqZero():
+    buildIf fieldAt(Value(p), 0, seqPayloadPtrType(c.m.types, c.m.nirm.types, t)[0]).bitOp(BitAnd, 0).eqZero():
       let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc")
       buildTyped c.code, info, Call, VoidId:
         let theProc = c.genx newSymNode(codegenProc, n.info)
@@ -1572,7 +1616,7 @@ type
   IndexFor = enum
     ForSeq, ForStr, ForOpenArray, ForArray
 
-proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType = nil): Value =
+proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType): Value =
   if optBoundsCheck in c.options:
     let info = toLineInfo(c, n.info)
     result = default(Value)
@@ -1581,11 +1625,11 @@ proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PTyp
       copyTree result.Tree, idx
       case kind
       of ForSeq, ForStr:
-        buildTyped result, info, FieldAt, c.m.nativeIntId:
+        buildTyped result, info, FieldAt, typeToIr(c.m, arr):
           copyTree result.Tree, a
           result.addImmediateVal info, 0 # (len, p)-pair
       of ForOpenArray:
-        buildTyped result, info, FieldAt, c.m.nativeIntId:
+        buildTyped result, info, FieldAt, typeToIr(c.m, arr):
           copyTree result.Tree, a
           result.addImmediateVal info, 1 # (p, len)-pair
       of ForArray:
@@ -1598,22 +1642,21 @@ proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PTyp
 
 proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
                     x: Value; n: PNode; arrType: PType) =
-  let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon))
+  let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon))
   case arrType.kind
   of tyString, tySequence:
-    let t = typeToIr(c.m.types, arrType.lastSon)
     let checkKind = if arrType.kind == tyString: ForStr else: ForSeq
-    let pay = if checkKind == ForStr: strPayloadPtrType(c.m.types)
-              else: seqPayloadPtrType(c.m.types, arrType)
+    let pay = if checkKind == ForStr: c.m.strPayloadId
+              else: seqPayloadPtrType(c.m.types, c.m.nirm.types, arrType)
 
-    let y = genIndexCheck(c, n[2], x, checkKind)
-    let z = genIndexCheck(c, n[3], x, checkKind)
+    let y = genIndexCheck(c, n[2], x, checkKind, arrType)
+    let z = genIndexCheck(c, n[3], x, checkKind, arrType)
 
-    buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
       target.addImmediateVal info, 0
       buildTyped target, info, AddrOf, elemType:
-        buildTyped target, info, ArrayAt, t:
-          buildTyped target, info, FieldAt, pay:
+        buildTyped target, info, ArrayAt, pay[1]:
+          buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
             copyTree target, x
             target.addImmediateVal info, 1 # (len, p)-pair
           copyTree target, y
@@ -1628,19 +1671,16 @@ proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
 
     freeTemp c, z
     freeTemp c, y
-  of tyArray, tyOpenArray:
-    let t = typeToIr(c.m.types, arrType.lastSon)
+  of tyArray:
     # XXX This evaluates the index check for `y` twice.
     # This check is also still insufficient for non-zero based arrays.
-    let checkKind = if arrType.kind == tyArray: ForArray else: ForOpenArray
-
-    let y = genIndexCheck(c, n[2], x, checkKind, arrType)
-    let z = genIndexCheck(c, n[3], x, checkKind, arrType)
+    let y = genIndexCheck(c, n[2], x, ForArray, arrType)
+    let z = genIndexCheck(c, n[3], x, ForArray, arrType)
 
-    buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
       target.addImmediateVal info, 0
       buildTyped target, info, AddrOf, elemType:
-        buildTyped target, info, ArrayAt, t:
+        buildTyped target, info, ArrayAt, typeToIr(c.m, arrType):
           copyTree target, x
           copyTree target, y
 
@@ -1653,6 +1693,30 @@ proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
 
     freeTemp c, z
     freeTemp c, y
+  of tyOpenArray:
+    # XXX This evaluates the index check for `y` twice.
+    let y = genIndexCheck(c, n[2], x, ForOpenArray, arrType)
+    let z = genIndexCheck(c, n[3], x, ForOpenArray, arrType)
+    let pay = openArrayPayloadType(c.m.types, c.m.nirm.types, arrType)
+
+    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
+      target.addImmediateVal info, 0
+      buildTyped target, info, AddrOf, elemType:
+        buildTyped target, info, ArrayAt, pay:
+          buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
+            copyTree target, x
+            target.addImmediateVal info, 0 # (p, len)-pair
+          copyTree target, y
+
+      target.addImmediateVal info, 1
+      buildTyped target, info, Add, c.m.nativeIntId:
+        buildTyped target, info, Sub, c.m.nativeIntId:
+          copyTree target, z
+          copyTree target, y
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
+
+    freeTemp c, z
+    freeTemp c, y
   else:
     raiseAssert "addSliceFields: " & typeToString(arrType)
 
@@ -1875,7 +1939,7 @@ proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[0], flags)
   template body(target) =
-    buildTyped target, info, AddrOf, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, AddrOf, typeToIr(c.m, n.typ):
       copyTree target, tmp
 
   valueIntoDest c, info, d, n.typ, body
@@ -1885,7 +1949,7 @@ proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[0], flags)
   template body(target) =
-    buildTyped target, info, Load, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, Load, typeToIr(c.m, n.typ):
       copyTree target, tmp
 
   valueIntoDest c, info, d, n.typ, body
@@ -1893,47 +1957,47 @@ proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
 
 proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) =
   let arrType = typ.skipTypes(abstractVar)
-  let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon))
+  let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon))
   case arrType.kind
   of tyString:
-    let t = typeToIr(c.m.types, typ.lastSon)
+    let t = typeToIr(c.m, typ)
     target.addImmediateVal info, 0
     buildTyped target, info, AddrOf, elemType:
-      buildTyped target, info, ArrayAt, t:
-        buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types):
+      buildTyped target, info, ArrayAt, c.m.strPayloadId[1]:
+        buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
           copyTree target, tmp
           target.addImmediateVal info, 1 # (len, p)-pair
         target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     # len:
     target.addImmediateVal info, 1
-    buildTyped target, info, FieldAt, c.m.nativeIntId:
+    buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
       copyTree target, tmp
       target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
 
   of tySequence:
-    let t = typeToIr(c.m.types, typ.lastSon)
+    let t = typeToIr(c.m, typ)
     target.addImmediateVal info, 0
     buildTyped target, info, AddrOf, elemType:
-      buildTyped target, info, ArrayAt, t:
-        buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, typ):
+      buildTyped target, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]:
+        buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
           copyTree target, tmp
           target.addImmediateVal info, 1 # (len, p)-pair
         target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     # len:
     target.addImmediateVal info, 1
-    buildTyped target, info, FieldAt, c.m.nativeIntId:
+    buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
       copyTree target, tmp
       target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
 
   of tyArray:
-    let t = typeToIr(c.m.types, typ.lastSon)
+    let t = typeToIr(c.m, arrType)
     target.addImmediateVal info, 0
     buildTyped target, info, AddrOf, elemType:
       buildTyped target, info, ArrayAt, t:
         copyTree target, tmp
         target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     target.addImmediateVal info, 1
-    target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
+    target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, arrType))
   else:
     raiseAssert "addAddrOfFirstElem: " & typeToString(typ)
 
@@ -1942,7 +2006,7 @@ proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlag
   let tmp = c.genx(arg, flags)
   let arrType = destType.skipTypes(abstractVar)
   template body(target) =
-    buildTyped target, info, ObjConstr, typeToIr(c.m.types, arrType):
+    buildTyped target, info, ObjConstr, typeToIr(c.m, arrType):
       c.addAddrOfFirstElem target, info, tmp, arg.typ
 
   valueIntoDest c, info, d, arrType, body
@@ -1966,7 +2030,7 @@ proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc:
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(arg, flags)
   template body(target) =
-    buildTyped target, info, opc, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, opc, typeToIr(c.m, n.typ):
       if opc == CheckedObjConv:
         target.addLabel info, CheckedGoto, c.exitLabel
       copyTree target, tmp
@@ -1974,11 +2038,11 @@ proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc:
   valueIntoDest c, info, d, n.typ, body
   freeTemp c, tmp
 
-proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) =
+proc genObjOrTupleConstr(c: var ProcCon; n: PNode; d: var Value; t: PType) =
   # XXX x = (x.old, 22)  produces wrong code ... stupid self assignments
   let info = toLineInfo(c, n.info)
   template body(target) =
-    buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, ObjConstr, typeToIr(c.m, t):
       for i in ord(n.kind == nkObjConstr)..<n.len:
         let it = n[i]
         if it.kind == nkExprColonExpr:
@@ -1992,7 +2056,23 @@ proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) =
           copyTree target, tmp
           c.freeTemp(tmp)
 
-  valueIntoDest c, info, d, n.typ, body
+      if isException(t):
+        target.addImmediateVal info, 1 # "name" field is at position after the "parent". See system.nim
+        target.addStrVal c.lit.strings, info, t.skipTypes(abstractInst).sym.name.s
+
+  valueIntoDest c, info, d, t, body
+
+proc genRefObjConstr(c: var ProcCon; n: PNode; d: var Value) =
+  if isEmpty(d): d = getTemp(c, n)
+  let info = toLineInfo(c, n.info)
+  let refType = n.typ.skipTypes(abstractInstOwned)
+  let objType = refType.lastSon
+
+  rawGenNew(c, d, refType, n.info, needsInit = nfAllFieldsSet notin n.flags)
+  var deref = default(Value)
+  deref.buildTyped info, Load, typeToIr(c.m, objType):
+    deref.Tree.copyTree d
+  genObjOrTupleConstr c, n, deref, objType
 
 proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) =
   if isEmpty(d): d = getTemp(c, n)
@@ -2008,8 +2088,8 @@ proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) =
 
   for i in 0..<n.len:
     var dd = default(Value)
-    buildTyped dd, info, ArrayAt, typeToIr(c.m.types, seqtype):
-      buildTyped dd, info, FieldAt, seqPayloadPtrType(c.m.types, seqtype):
+    buildTyped dd, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]:
+      buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype):
         copyTree Tree(dd), d
         dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i
     gen(c, n[i], dd)
@@ -2024,7 +2104,7 @@ proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) =
 
   let info = toLineInfo(c, n.info)
   template body(target) =
-    buildTyped target, info, ArrayConstr, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, ArrayConstr, typeToIr(c.m, n.typ):
       for i in 0..<n.len:
         let tmp = c.genx(n[i])
         copyTree target, tmp
@@ -2056,9 +2136,11 @@ proc genVarSection(c: var ProcCon; n: PNode) =
           opc = SummonGlobal
         else:
           opc = Summon
-        let t = typeToIr(c.m.types, s.typ)
+        let t = typeToIr(c.m, s.typ)
         #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info)
-        c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), t, opc
+        let symId = toSymId(c, s)
+        c.code.addSummon toLineInfo(c, a.info), symId, t, opc
+        c.m.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
         if a[2].kind != nkEmpty:
           genAsgn2(c, vn, a[2])
       else:
@@ -2081,10 +2163,13 @@ proc irModule(c: var ProcCon; owner: PSym): string =
   #if owner == c.m.module: "" else:
   customPath(toFullPath(c.config, owner.info))
 
+proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
+  result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
+
 proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   let info = toLineInfo(c, n.info)
   let s = n.sym
-  if ast.originatingModule(s) != c.m.module:
+  if fromForeignModule(c, s):
     template body(target) =
       build target, info, ModuleSymUse:
         target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
@@ -2093,7 +2178,7 @@ proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     valueIntoDest c, info, d, s.typ, body
   else:
     template body(target) =
-      target.addSymUse info, SymId(s.itemId.item)
+      target.addSymUse info, toSymId(c, s)
     valueIntoDest c, info, d, s.typ, body
 
 proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
@@ -2102,16 +2187,17 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
   of skVar, skForVar, skTemp, skLet, skResult, skParam, skConst:
     genRdVar(c, n, d, flags)
   of skProc, skFunc, skConverter, skMethod, skIterator:
-    if ast.originatingModule(s) == c.m.module:
+    if not fromForeignModule(c, s):
       # anon and generic procs have no AST so we need to remember not to forget
       # to emit these:
-      if not c.m.processedProcs.containsOrIncl(s.itemId):
-        c.m.pendingProcs.add s
+      if not c.m.processedProcs.contains(s.itemId):
+        if not c.m.pendingProcsAsSet.containsOrIncl(s.itemId):
+          c.m.pendingProcs.add s
     genRdVar(c, n, d, flags)
   of skEnumField:
     let info = toLineInfo(c, n.info)
     template body(target) =
-      target.addIntVal c.lit.numbers, info, typeToIr(c.m.types, n.typ), s.position
+      target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), s.position
     valueIntoDest c, info, d, n.typ, body
   else:
     localError(c.config, n.info, "cannot generate code for: " & s.name.s)
@@ -2119,7 +2205,7 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
 proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) =
   let info = toLineInfo(c, n.info)
   template body(target) =
-    target.addIntVal c.lit.numbers, info, typeToIr(c.m.types, n.typ), bits
+    target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), bits
   valueIntoDest c, info, d, n.typ, body
 
 proc genStringLit(c: var ProcCon; n: PNode; d: var Value) =
@@ -2131,7 +2217,7 @@ proc genStringLit(c: var ProcCon; n: PNode; d: var Value) =
 proc genNilLit(c: var ProcCon; n: PNode; d: var Value) =
   let info = toLineInfo(c, n.info)
   template body(target) =
-    target.addNilVal info, typeToIr(c.m.types, n.typ)
+    target.addNilVal info, typeToIr(c.m, n.typ)
   valueIntoDest c, info, d, n.typ, body
 
 proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
@@ -2141,7 +2227,7 @@ proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
     let a = c.genx n[1]
     let b = c.genx n[2]
     template body(target) =
-      buildTyped target, info, CheckedRange, typeToIr(c.m.types, n.typ):
+      buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ):
         copyTree target, tmp
         copyTree target, a
         copyTree target, b
@@ -2154,16 +2240,17 @@ proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
     gen c, n[0], d
 
 proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
-  let arrayKind = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
+  let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
+  let arrayKind = arrayType.kind
   let info = toLineInfo(c, n.info)
   case arrayKind
   of tyString:
     let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForStr)
-    let t = typeToIr(c.m.types, n.typ)
+    let b = genIndexCheck(c, n[1], a, ForStr, arrayType)
+    let t = typeToIr(c.m, n.typ)
     template body(target) =
-      buildTyped target, info, ArrayAt, t:
-        buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types):
+      buildTyped target, info, ArrayAt, c.m.strPayloadId[1]:
+        buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
           copyTree target, a
           target.addImmediateVal info, 1 # (len, p)-pair
         copyTree target, b
@@ -2175,7 +2262,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     let a = genx(c, n[0], flags)
     let b = genx(c, n[1])
     template body(target) =
-      buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ):
+      buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType):
         copyTree target, a
         copyTree target, b
     valueIntoDest c, info, d, n.typ, body
@@ -2186,7 +2273,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     let a = genx(c, n[0], flags)
     let b = int n[1].intVal
     template body(target) =
-      buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ):
+      buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
         copyTree target, a
         target.addImmediateVal info, b
     valueIntoDest c, info, d, n.typ, body
@@ -2194,11 +2281,11 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     freeTemp c, a
   of tyOpenArray, tyVarargs:
     let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForOpenArray)
-    let t = typeToIr(c.m.types, n.typ)
+    let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType)
+    let t = typeToIr(c.m, n.typ)
     template body(target) =
-      buildTyped target, info, ArrayAt, t:
-        buildTyped target, info, FieldAt, openArrayPayloadType(c.m.types, n[0].typ):
+      buildTyped target, info, ArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ):
+        buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
           copyTree target, a
           target.addImmediateVal info, 0 # (p, len)-pair
         copyTree target, b
@@ -2212,7 +2299,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     genIndex(c, n[1], n[0].typ, b)
 
     template body(target) =
-      buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ):
+      buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType):
         copyTree target, a
         copyTree target, b
     valueIntoDest c, info, d, n.typ, body
@@ -2220,11 +2307,11 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
     freeTemp c, a
   of tySequence:
     let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForSeq)
-    let t = typeToIr(c.m.types, n.typ)
+    let b = genIndexCheck(c, n[1], a, ForSeq, arrayType)
+    let t = typeToIr(c.m, n.typ)
     template body(target) =
-      buildTyped target, info, ArrayAt, t:
-        buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, n[0].typ):
+      buildTyped target, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]:
+        buildTyped target, info, FieldAt, t:
           copyTree target, a
           target.addImmediateVal info, 1 # (len, p)-pair
         copyTree target, b
@@ -2239,20 +2326,28 @@ proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   let a = genx(c, n[0], flags)
 
   template body(target) =
-    buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ):
+    buildTyped target, info, FieldAt, typeToIr(c.m, n[0].typ):
       copyTree target, a
       genField c, n[1], Value(target)
 
   valueIntoDest c, info, d, n.typ, body
   freeTemp c, a
 
-proc genParams(c: var ProcCon; params: PNode) =
+proc genParams(c: var ProcCon; params: PNode; prc: PSym) =
+  if params.len > 0 and resultPos < prc.ast.len:
+    let resNode = prc.ast[resultPos]
+    let res = resNode.sym # get result symbol
+    c.code.addSummon toLineInfo(c, res.info), toSymId(c, res),
+      typeToIr(c.m, res.typ), SummonResult
+
   for i in 1..<params.len:
     let s = params[i].sym
     if not isCompileTimeOnly(s.typ):
-      let t = typeToIr(c.m.types, s.typ)
+      let t = typeToIr(c.m, s.typ)
       assert t.int != -1, typeToString(s.typ)
-      c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), t, SummonParam
+      let symId = toSymId(c, s)
+      c.code.addSummon toLineInfo(c, params[i].info), symId, t, SummonParam
+      c.m.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
 
 proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) =
   template ann(s: untyped) = c.code.addPragmaId info, s
@@ -2270,6 +2365,12 @@ proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvent
 proc genProc(cOuter: var ProcCon; prc: PSym) =
   if cOuter.m.processedProcs.containsOrIncl(prc.itemId):
     return
+  #assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s
+  if cOuter.m.inProc > 0:
+    if not cOuter.m.pendingProcsAsSet.containsOrIncl(prc.itemId):
+      cOuter.m.pendingProcs.add prc
+    return
+  inc cOuter.m.inProc
 
   var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config)
 
@@ -2277,8 +2378,14 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
 
   let info = toLineInfo(c, body.info)
   build c.code, info, ProcDecl:
-    addSymDef c.code, info, SymId(prc.itemId.item)
+    let symId = toSymId(c, prc)
+    addSymDef c.code, info, symId
+    c.m.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
     addCallConv c, info, prc.typ.callConv
+    if sfCompilerProc in prc.flags:
+      build c.code, info, PragmaPair:
+        c.code.addPragmaId info, CoreName
+        c.code.addStrVal c.lit.strings, info, prc.name.s
     if {sfImportc, sfExportc} * prc.flags != {}:
       build c.code, info, PragmaPair:
         c.code.addPragmaId info, ExternName
@@ -2302,18 +2409,35 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
         else:
           c.code.addPragmaId info, ObjExport
 
-    genParams(c, prc.typ.n)
+    genParams(c, prc.typ.n, prc)
     gen(c, body)
     patch c, body, c.exitLabel
 
-  copyTree cOuter.code, c.code
+  #copyTree cOuter.code, c.code
+  dec cOuter.m.inProc
 
 proc genProc(cOuter: var ProcCon; n: PNode) =
   if n.len == 0 or n[namePos].kind != nkSym: return
   let prc = n[namePos].sym
-  if isGenericRoutineStrict(prc) or isCompileTimeProc(prc): return
+  if isGenericRoutineStrict(prc) or isCompileTimeProc(prc) or sfForward in prc.flags: return
   genProc cOuter, prc
 
+proc genClosureCall(c: var ProcCon; n: PNode; d: var Value) =
+  let typ = skipTypes(n[0].typ, abstractInstOwned)
+  if tfIterator in typ.flags:
+    const PatIter = "$1.ClP_0($3, $1.ClE_0)" # we know the env exists
+
+  else:
+    const PatProc = "$1.ClE_0? $1.ClP_0($3, $1.ClE_0):(($4)($1.ClP_0))($2)"
+
+
+proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) =
+  if n[0].typ != nil and n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
+    # XXX genClosureCall p, n, d
+    genCall c, n, d
+  else:
+    genCall c, n, d
+
 proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
   when defined(nimCompilerStacktraceHints):
     setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
@@ -2328,11 +2452,9 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
         localError(c.config, n.info, "cannot call method " & s.name.s &
           " at compile time")
       else:
-        genCall(c, n, d)
-        clearDest(c, n, d)
+        genComplexCall(c, n, d)
     else:
-      genCall(c, n, d)
-      clearDest(c, n, d)
+      genComplexCall(c, n, d)
   of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
     genNumericLit(c, n, d, n.intVal)
   of nkFloatLit..nkFloat128Lit:
@@ -2401,8 +2523,13 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
   of nkCStringToString: convCStrToStr(c, n, d)
   of nkBracket: genArrayConstr(c, n, d)
   of nkCurly: genSetConstr(c, n, d)
-  of nkObjConstr, nkPar, nkClosure, nkTupleConstr:
-    genObjOrTupleConstr(c, n, d)
+  of nkObjConstr:
+    if n.typ.skipTypes(abstractInstOwned).kind == tyRef:
+      genRefObjConstr(c, n, d)
+    else:
+      genObjOrTupleConstr(c, n, d, n.typ)
+  of nkPar, nkClosure, nkTupleConstr:
+    genObjOrTupleConstr(c, n, d, n.typ)
   of nkCast:
     genConv(c, n, n[1], d, flags, Cast)
   of nkComesFrom:
@@ -2419,8 +2546,8 @@ proc genPendingProcs(c: var ProcCon) =
     for v in procs:
       genProc(c, v)
 
-proc genStmt*(c: var ProcCon; n: PNode): int =
-  result = c.code.len
+proc genStmt*(c: var ProcCon; n: PNode): NodePos =
+  result = NodePos c.code.len
   var d = default(Value)
   c.gen(n, d)
   unused c, n, d
diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim
index 0669bc222..c4fb5322d 100644
--- a/compiler/nir/nir.nim
+++ b/compiler/nir/nir.nim
@@ -12,8 +12,9 @@
 
 from os import addFileExt, `/`, createDir
 
+import std / assertions
 import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
-import nirtypes, nirinsts, ast2ir, nirlineinfos
+import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm
 
 import ".." / ic / [rodfiles, bitabs]
 
@@ -22,13 +23,18 @@ type
     m: ModuleCon
     c: ProcCon
     oldErrorCount: int
+    bytecode: Bytecode
 
 proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx =
-  let m = initModuleCon(g, g.config, idgen, module)
-  PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
+  var lit = Literals()
+  var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
+  var m = initModuleCon(g, g.config, idgen, module, nirm)
+  m.noModularity = true
+  PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm))
 
 proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
-  c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module)
+  #c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm)
+  #c.m.noModularity = true
   c.c = initProcCon(c.m, nil, c.m.graph.config)
   c.idgen = idgen
 
@@ -46,14 +52,13 @@ proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPa
 proc evalStmt(c: PCtx; n: PNode) =
   let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
   let pc = genStmt(c.c, n)
-
-  var res = ""
-  if pc < c.c.code.len:
-    toString c.c.code, NodePos(pc), c.m.lit.strings, c.m.lit.numbers, res
+  #var res = ""
+  #toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res
   #res.add "\n--------------------------\n"
   #toString res, c.m.types.g
-  echo res
-
+  if pc.int < c.m.nirm.code.len:
+    execCode c.bytecode, c.m.nirm.code, pc
+  #echo res
 
 proc runCode*(c: PPassContext; n: PNode): PNode =
   let c = PCtx(c)
@@ -71,7 +76,9 @@ type
     c: ProcCon
 
 proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
-  let m = initModuleCon(g, g.config, idgen, module)
+  var lit = Literals()
+  var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
+  let m = initModuleCon(g, g.config, idgen, module, nirm)
   NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
 
 proc gen(c: NirPassContext; n: PNode) =
@@ -89,27 +96,9 @@ proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
   let nimcache = getNimcacheDir(c.c.config).string
   createDir nimcache
   let outp = nimcache / c.m.module.name.s.addFileExt("nir")
-  var r = rodfiles.create(outp)
+  #c.m.nirm.code = move c.c.code
   try:
-    r.storeHeader(nirCookie)
-    r.storeSection stringsSection
-    r.store c.m.lit.strings
-
-    r.storeSection numbersSection
-    r.store c.m.lit.numbers
-
-    r.storeSection bodiesSection
-    r.store c.c.code
-
-    r.storeSection typesSection
-    r.store c.m.types.g
-
-    r.storeSection sideChannelSection
-    r.store c.m.man
-
-  finally:
-    r.close()
-  if r.err != ok:
-    rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
-  else:
+    store c.m.nirm[], outp
     echo "created: ", outp
+  except IOError:
+    rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim
index 3d8c8e6ff..363507ca6 100644
--- a/compiler/nir/nirc.nim
+++ b/compiler/nir/nirc.nim
@@ -10,41 +10,46 @@
 ## Nir Compiler. Currently only supports a "view" command.
 
 import ".." / ic / [bitabs, rodfiles]
-import nirinsts, nirtypes, nirlineinfos
+import nirinsts, nirtypes, nirlineinfos, nirfiles #, nir2gcc
 
 proc view(filename: string) =
-  var lit = Literals()
-
-  var r = rodfiles.open(filename)
-  var code = default Tree
-  var man = default LineInfoManager
-  var types = initTypeGraph(lit)
-  try:
-    r.loadHeader(nirCookie)
-    r.loadSection stringsSection
-    r.load lit.strings
-
-    r.loadSection numbersSection
-    r.load lit.numbers
-
-    r.loadSection bodiesSection
-    r.load code
-
-    r.loadSection typesSection
-    r.load types
-
-    r.loadSection sideChannelSection
-    r.load man
-
-  finally:
-    r.close()
-
+  let m = load(filename)
   var res = ""
-  allTreesToString code, lit.strings, lit.numbers, res
+  allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res
   res.add "\n# TYPES\n"
-  nirtypes.toString res, types
+  nirtypes.toString res, m.types
   echo res
 
-import std / os
-
-view paramStr(1)
+proc libgcc(filename: string) =
+  let m = load(filename)
+  #gcc m, filename
+
+import std / [syncio, parseopt]
+
+proc writeHelp =
+  echo """Usage: nirc view|gcc <file.nir>"""
+  quit 0
+
+proc main =
+  var inp = ""
+  var cmd = ""
+  for kind, key, val in getopt():
+    case kind
+    of cmdArgument:
+      if cmd.len == 0: cmd = key
+      elif inp.len == 0: inp = key
+      else: quit "Error: too many arguments"
+    of cmdLongOption, cmdShortOption:
+      case key
+      of "help", "h": writeHelp()
+      of "version", "v": stdout.write "1.0\n"
+    of cmdEnd: discard
+  if inp.len == 0:
+    quit "Error: no input file specified"
+  case cmd
+  of "", "view":
+    view inp
+  of "gcc":
+   libgcc inp
+
+main()
diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim
new file mode 100644
index 000000000..f6c73178b
--- /dev/null
+++ b/compiler/nir/nirfiles.nim
@@ -0,0 +1,73 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import ".." / ic / [bitabs, rodfiles]
+import nirinsts, nirtypes, nirlineinfos
+
+type
+  NirModule* = object
+    code*: Tree
+    man*: LineInfoManager
+    types*: TypeGraph
+    lit*: Literals
+    symnames*: SymNames
+
+proc load*(filename: string): NirModule =
+  let lit = Literals()
+  result = NirModule(lit: lit, types: initTypeGraph(lit))
+  var r = rodfiles.open(filename)
+  try:
+    r.loadHeader(nirCookie)
+    r.loadSection stringsSection
+    r.load result.lit.strings
+
+    r.loadSection numbersSection
+    r.load result.lit.numbers
+
+    r.loadSection bodiesSection
+    r.load result.code
+
+    r.loadSection typesSection
+    r.load result.types
+
+    r.loadSection sideChannelSection
+    r.load result.man
+
+    r.loadSection symnamesSection
+    r.load result.symnames
+
+  finally:
+    r.close()
+
+proc store*(m: NirModule; outp: string) =
+  var r = rodfiles.create(outp)
+  try:
+    r.storeHeader(nirCookie)
+    r.storeSection stringsSection
+    r.store m.lit.strings
+
+    r.storeSection numbersSection
+    r.store m.lit.numbers
+
+    r.storeSection bodiesSection
+    r.store m.code
+
+    r.storeSection typesSection
+    r.store m.types
+
+    r.storeSection sideChannelSection
+    r.store m.man
+
+    r.storeSection symnamesSection
+    r.store m.symnames
+
+  finally:
+    r.close()
+  if r.err != ok:
+    raise newException(IOError, "could store into: " & outp)
diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim
index d95cd061d..7b281e876 100644
--- a/compiler/nir/nirinsts.nim
+++ b/compiler/nir/nirinsts.nim
@@ -57,6 +57,7 @@ type
     SummonGlobal,
     SummonThreadLocal,
     Summon, # x = Summon Typed <Type ID>; x begins to live
+    SummonResult,
     SummonParam,
     SummonConst,
     Kill, # `Kill x`: scope end for `x`
@@ -110,6 +111,7 @@ type
 type
   PragmaKey* = enum
     FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
+    CoreName,
     ExternName,
     HeaderImport,
     DllImport,
@@ -167,7 +169,7 @@ const
 type
   Instr* = object     # 8 bytes
     x: uint32
-    info: PackedLineInfo
+    info*: PackedLineInfo
 
 template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
 template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
@@ -234,6 +236,10 @@ proc nextChild(tree: Tree; pos: var int) {.inline.} =
   else:
     inc pos
 
+proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
+
+template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
+
 iterator sons*(tree: Tree; n: NodePos): NodePos =
   var pos = n.int
   assert tree.nodes[pos].kind > LastAtomicValue
@@ -243,6 +249,16 @@ iterator sons*(tree: Tree; n: NodePos): NodePos =
     yield NodePos pos
     nextChild tree, pos
 
+iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
+  var pos = n.int
+  assert tree.nodes[pos].kind > LastAtomicValue
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  nextChild tree, pos
+  while pos < last:
+    yield NodePos pos
+    nextChild tree, pos
+
 template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
 
 proc span(tree: Tree; pos: int): int {.inline.} =
@@ -257,9 +273,46 @@ proc copyTree*(dest: var Tree; src: Tree) =
   for i in 0..<L:
     dest.nodes[d+i] = src.nodes[pos+i]
 
+proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  result = (NodePos a, NodePos b)
+
+proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  result = (NodePos a, NodePos b, NodePos c)
+
+proc typeId*(ins: Instr): TypeId {.inline.} =
+  assert ins.kind == Typed
+  result = TypeId(ins.operand)
+
+proc symId*(ins: Instr): SymId {.inline.} =
+  assert ins.kind in {SymUse, SymDef}
+  result = SymId(ins.operand)
+
+proc immediateVal*(ins: Instr): int {.inline.} =
+  assert ins.kind == ImmediateVal
+  result = cast[int](ins.operand)
+
+proc litId*(ins: Instr): LitId {.inline.} =
+  assert ins.kind in {StrVal, IntVal}
+  result = LitId(ins.operand)
+
+
 type
   LabelId* = distinct int
 
+proc `==`*(a, b: LabelId): bool {.borrow.}
+proc hash*(a: LabelId): Hash {.borrow.}
+
+proc label*(ins: Instr): LabelId {.inline.} =
+  assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
+  result = LabelId(ins.operand)
+
 proc newLabel*(labelGen: var int): LabelId {.inline.} =
   result = LabelId labelGen
   inc labelGen
@@ -297,7 +350,7 @@ proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
 
 proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
   assert typ.int >= 0
-  assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam}
+  assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
   let x = prepare(t, info, opc)
   t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
   t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
@@ -345,7 +398,32 @@ proc escapeToNimLit(s: string; result: var string) =
       result.add c
   result.add '"'
 
+type
+  SymNames* = object
+    s: seq[LitId]
+
+proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
+  let k = int(key)
+  if k >= t.s.len: t.s.setLen k+1
+  t.s[k] = val
+
+proc `[]`*(t: SymNames; key: SymId): LitId =
+  let k = int(key)
+  if k < t.s.len: result = t.s[k]
+  else: result = LitId(0)
+
+template localName(s: SymId): string =
+  let name = names[s]
+  if name != LitId(0):
+    strings[name]
+  else:
+    $s.int
+
+proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
+proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
+
 proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
+               names: SymNames;
                r: var string; nesting = 0) =
   if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
     r.add ' '
@@ -361,10 +439,10 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl
     escapeToNimLit(strings[LitId t[pos].operand], r)
   of SymDef:
     r.add "SymDef "
-    r.add $t[pos].operand
+    r.add localName(SymId t[pos].operand)
   of SymUse:
     r.add "SymUse "
-    r.add $t[pos].operand
+    r.add localName(SymId t[pos].operand)
   of PragmaId:
     r.add $cast[PragmaKey](t[pos].operand)
   of Typed:
@@ -374,7 +452,11 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl
   of NilVal:
     r.add "NilVal"
   of Label:
-    r.add "L"
+    # undo the nesting:
+    var spaces = r.len-1
+    while spaces >= 0 and r[spaces] == ' ': dec spaces
+    r.setLen spaces+1
+    r.add "\n  L"
     r.add $t[pos].operand
   of Goto, CheckedGoto, LoopLabel, GotoLoop:
     r.add $t[pos].kind
@@ -385,16 +467,17 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl
     r.add "{\n"
     for i in 0..<(nesting+1)*2: r.add ' '
     for p in sons(t, pos):
-      toString t, p, strings, integers, r, nesting+1
+      toString t, p, strings, integers, names, r, nesting+1
     r.add "\n"
     for i in 0..<nesting*2: r.add ' '
     r.add "}"
 
 proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
+                       names: SymNames;
                        r: var string) =
   var i = 0
   while i < t.len:
-    toString t, NodePos(i), strings, integers, r
+    toString t, NodePos(i), strings, integers, names, r
     nextChild t, i
 
 type
diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim
index e07f1395d..d1c7c6084 100644
--- a/compiler/nir/nirtypes.nim
+++ b/compiler/nir/nirtypes.nim
@@ -42,6 +42,11 @@ type
 template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask)
 template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits)
 
+proc integralBits*(n: TypeNode): int {.inline.} =
+  # Number of bits in the IntTy, etc. Only valid for integral types.
+  assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy}
+  result = int(n.operand)
+
 template toX(k: NirTypeKind; operand: uint32): uint32 =
   uint32(k) or (operand shl TypeKindBits)
 
@@ -150,6 +155,10 @@ proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
   assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy}
   result = TypeId(n.int+1)
 
+proc litId*(n: TypeNode): LitId {.inline.} =
+  assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy}
+  result = LitId(n.operand)
+
 proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind
 
 proc span(tree: TypeGraph; pos: int): int {.inline.} =
@@ -170,7 +179,8 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
 
 proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
   assert tree[n].kind == ArrayTy
-  result = tree.lit.numbers[LitId tree[n].operand]
+  let (_, b) = sons2(tree, n)
+  result = tree.lit.numbers[LitId tree[b].operand]
 
 proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
   assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
@@ -227,6 +237,10 @@ proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
   assert kind in {ObjectTy, UnionTy}
   tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
 
+proc getTypeTag*(tree: TypeGraph; t: TypeId): string =
+  assert tree[t].kind in {ObjectTy, UnionTy}
+  result = tree.lit.strings[LitId tree[t].operand]
+
 proc addVarargs*(tree: var TypeGraph) =
   tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
 
@@ -237,7 +251,7 @@ proc getFloat128Type*(tree: var TypeGraph): TypeId =
 proc addBuiltinType*(g: var TypeGraph; id: TypeId) =
   g.nodes.add g[id]
 
-template firstSon(n: TypeId): TypeId = TypeId(n.int+1)
+template firstSon*(n: TypeId): TypeId = TypeId(n.int+1)
 
 proc addType*(g: var TypeGraph; t: TypeId) =
   # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead:
@@ -360,7 +374,9 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
     dest.add g.lit.strings[LitId g[i].operand]
   of ProcTy:
     dest.add "proc["
-    for t in sons(g, i): toString(dest, g, t)
+    for t in sons(g, i):
+      dest.add ' '
+      toString(dest, g, t)
     dest.add "]"
   of ObjectDecl:
     dest.add "object["
@@ -399,6 +415,12 @@ proc toString*(dest: var string; g: TypeGraph) =
     dest.add '\n'
     nextChild g, i
 
+iterator allTypes*(g: TypeGraph; start = 0): TypeId =
+  var i = start
+  while i < g.len:
+    yield TypeId i
+    nextChild g, i
+
 proc `$`(g: TypeGraph): string =
   result = ""
   toString(result, g)
diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim
index dcb5ded6f..b58f48272 100644
--- a/compiler/nir/nirvm.nim
+++ b/compiler/nir/nirvm.nim
@@ -15,9 +15,9 @@ We also split the instruction stream into separate (code, debug) seqs while
 we're at it.
 ]##
 
-import std / [tables, intsets]
+import std / [syncio, assertions, tables, intsets]
 import ".." / ic / bitabs
-import nirinsts, nirtypes
+import nirinsts, nirtypes, nirfiles, nirlineinfos
 
 type
   OpcodeM = enum
@@ -25,15 +25,14 @@ type
     IntValM,
     StrValM,
     LoadLocalM, # with local ID
+    LoadGlobalM,
+    LoadProcM,
     TypedM,   # with type ID
     PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum
     NilValM,
     GotoM,
     CheckedGotoM, # last atom
 
-    LoadProcM,
-    LoadGlobalM, # `"module".x`
-
     ArrayConstrM,
     ObjConstrM,
     RetM,
@@ -44,18 +43,16 @@ type
     SelectListM,  # (values...)
     SelectValueM, # (value)
     SelectRangeM, # (valueA..valueB)
-    SummonGlobalM,
-    SummonThreadLocalM,
-    SummonM, # x = Summon Typed <Type ID>; x begins to live
+    AllocLocals,
     SummonParamM,
 
     AddrOfM,
-    ArrayAtM, # addr(a[i])
+    ArrayAtM, # (elemSize, addr(a), i)
     FieldAtM, # addr(obj.field)
 
     LoadM, # a[]
-    StoreM, # a[] = b
     AsgnM,  # a = b
+    StoreM, # a[] = b
     SetExcM,
     TestExcM,
 
@@ -63,9 +60,7 @@ type
     CheckedIndexM,
 
     CallM,
-    IndirectCallM,
     CheckedCallM, # call that can raise
-    CheckedIndirectCallM, # call that can raise
     CheckedAddM, # with overflow checking etc.
     CheckedSubM,
     CheckedMulM,
@@ -103,8 +98,8 @@ const
 type
   Instr = distinct uint32
 
-template kind(n: Instr): OpcodeM = OpcodeM(n and OpcodeMask)
-template operand(n: Instr): uint32 = (n shr OpcodeBits)
+template kind(n: Instr): OpcodeM = OpcodeM(n.uint32 and OpcodeMask)
+template operand(n: Instr): uint32 = (n.uint32 shr OpcodeBits)
 
 template toIns(k: OpcodeM; operand: uint32): Instr =
   Instr(uint32(k) or (operand shl OpcodeBits))
@@ -112,24 +107,108 @@ template toIns(k: OpcodeM; operand: uint32): Instr =
 template toIns(k: OpcodeM; operand: LitId): Instr =
   Instr(uint32(k) or (operand.uint32 shl OpcodeBits))
 
+const
+  GlobalsSize = 1024*24
+
 type
   PatchPos = distinct int
   CodePos = distinct int
 
-  Unit = ref object ## a NIR module
+  Bytecode* = object
+    code: seq[Instr]
+    debug: seq[PackedLineInfo]
+    m: ref NirModule
     procs: Table[SymId, CodePos]
     globals: Table[SymId, uint32]
-    integers: BiTable[int64]
-    strings: BiTable[string]
-    globalsGen: uint32
+    globalData: pointer
+    globalsAddr: uint32
+    typeImpls: Table[string, TypeId]
+    offsets: Table[TypeId, seq[(int, TypeId)]]
+    sizes: Table[TypeId, (int, int)] # (size, alignment)
+    oldTypeLen: int
+    procUsagesToPatch: Table[SymId, seq[CodePos]]
 
   Universe* = object ## all units: For interpretation we need that
-    units: Table[string, Unit]
+    units: seq[Bytecode]
+    unitNames: Table[string, int]
+    current: int
 
-  Bytecode = object
-    code: seq[Instr]
-    debug: seq[PackedLineInfo]
-    u: Unit
+proc initBytecode*(m: ref NirModule): Bytecode = Bytecode(m: m, globalData: alloc0(GlobalsSize))
+
+proc debug(bc: Bytecode; t: TypeId) =
+  var buf = ""
+  toString buf, bc.m.types, t
+  echo buf
+
+proc debug(bc: Bytecode; info: PackedLineInfo) =
+  let (litId, line, col) = bc.m.man.unpack(info)
+  echo bc.m.lit.strings[litId], ":", line, ":", col
+
+template `[]`(t: seq[Instr]; n: CodePos): Instr = t[n.int]
+
+proc traverseObject(b: var Bytecode; t, offsetKey: TypeId) =
+  var size = -1
+  var align = -1
+  for x in sons(b.m.types, t):
+    case b.m.types[x].kind
+    of FieldDecl:
+      var offset = -1
+      for y in sons(b.m.types, x):
+        if b.m.types[y].kind == OffsetVal:
+          offset = b.m.lit.numbers[b.m.types[y].litId]
+          break
+      b.offsets.mgetOrPut(offsetKey, @[]).add (offset, x.firstSon)
+    of SizeVal:
+      size = b.m.lit.numbers[b.m.types[x].litId]
+    of AlignVal:
+      align = b.m.lit.numbers[b.m.types[x].litId]
+    of ObjectTy:
+      # inheritance
+      let impl = b.typeImpls.getOrDefault(b.m.lit.strings[b.m.types[x].litId])
+      assert impl.int > 0
+      traverseObject b, impl, offsetKey
+    else: discard
+  if t == offsetKey:
+    b.sizes[t] = (size, align)
+
+proc computeSize(b: var Bytecode; t: TypeId): (int, int) =
+  case b.m.types[t].kind
+  of ObjectDecl, UnionDecl:
+    result = b.sizes[t]
+  of ObjectTy, UnionTy:
+    let impl = b.typeImpls[b.m.lit.strings[b.m.types[t].litId]]
+    result = computeSize(b, impl)
+  of IntTy, UIntTy, FloatTy, BoolTy, CharTy:
+    let s = b.m.types[t].integralBits div 8
+    result = (s, s)
+  of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ProcTy:
+    result = (sizeof(pointer), sizeof(pointer))
+  of ArrayTy:
+    let e = elementType(b.m.types, t)
+    let n = arrayLen(b.m.types, t)
+    let inner = computeSize(b, e)
+    result = (inner[0] * n.int, inner[1])
+  else:
+    result = (0, 0)
+
+proc computeElemSize(b: var Bytecode; t: TypeId): int =
+  case b.m.types[t].kind
+  of ArrayTy, APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, LastArrayTy:
+    result = computeSize(b, elementType(b.m.types, t))[0]
+  else:
+    raiseAssert "not an array type"
+
+proc traverseTypes(b: var Bytecode) =
+  for t in allTypes(b.m.types, b.oldTypeLen):
+    if b.m.types[t].kind in {ObjectDecl, UnionDecl}:
+      assert b.m.types[t.firstSon].kind == NameVal
+      b.typeImpls[b.m.lit.strings[b.m.types[t.firstSon].litId]] = t
+
+  for t in allTypes(b.m.types, b.oldTypeLen):
+    if b.m.types[t].kind in {ObjectDecl, UnionDecl}:
+      assert b.m.types[t.firstSon].kind == NameVal
+      traverseObject b, t, t
+  b.oldTypeLen = b.m.types.len
 
 const
   InvalidPatchPos* = PatchPos(-1)
@@ -146,7 +225,7 @@ proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) =
   bc.debug.add info
 
 proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) =
-  add bc, info, kind, uint(lit)
+  add bc, info, kind, uint32(lit)
 
 proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue
 proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue
@@ -175,6 +254,8 @@ proc nextChild(bc: Bytecode; pos: var int) {.inline.} =
   else:
     inc pos
 
+proc next(bc: Bytecode; pos: var CodePos) {.inline.} = nextChild bc, int(pos)
+
 iterator sons(bc: Bytecode; n: CodePos): CodePos =
   var pos = n.int
   assert bc.code[pos].kind > LastAtomicValue
@@ -184,88 +265,175 @@ iterator sons(bc: Bytecode; n: CodePos): CodePos =
     yield CodePos pos
     nextChild bc, pos
 
-template `[]`*(t: Bytecode; n: CodePos): Instr = t.code[n.int]
+iterator sonsFrom1(bc: Bytecode; n: CodePos): CodePos =
+  var pos = n.int
+  assert bc.code[pos].kind > LastAtomicValue
+  let last = pos + bc.code[pos].rawSpan
+  inc pos
+  nextChild bc, pos
+  while pos < last:
+    yield CodePos pos
+    nextChild bc, pos
+
+iterator sonsFrom2(bc: Bytecode; n: CodePos): CodePos =
+  var pos = n.int
+  assert bc.code[pos].kind > LastAtomicValue
+  let last = pos + bc.code[pos].rawSpan
+  inc pos
+  nextChild bc, pos
+  nextChild bc, pos
+  while pos < last:
+    yield CodePos pos
+    nextChild bc, pos
+
+template firstSon(n: CodePos): CodePos = CodePos(n.int+1)
+
+template `[]`(t: Bytecode; n: CodePos): Instr = t.code[n.int]
 
 proc span(bc: Bytecode; pos: int): int {.inline.} =
   if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand)
 
+iterator triples*(bc: Bytecode; n: CodePos): (uint32, int, CodePos) =
+  var pos = n.int
+  assert bc.code[pos].kind > LastAtomicValue
+  let last = pos + bc.code[pos].rawSpan
+  inc pos
+  while pos < last:
+    let offset = bc.code[pos].operand
+    nextChild bc, pos
+    let size = bc.code[pos].operand.int
+    nextChild bc, pos
+    let val = CodePos pos
+    yield (offset, size, val)
+    nextChild bc, pos
+
 type
   Preprocessing = object
+    u: ref Universe
     known: Table[LabelId, CodePos]
     toPatch: Table[LabelId, seq[CodePos]]
     locals: Table[SymId, uint32]
-    c: Bytecode # to be moved out
-    thisModule: LitId
+    thisModule: uint32
+    localsAddr: uint32
     markedWithLabel: IntSet
 
-proc genGoto(c: var Preprocessing; lab: LabelId; opc: OpcodeM) =
+proc align(address, alignment: uint32): uint32 =
+  result = (address + (alignment - 1'u32)) and not (alignment - 1'u32)
+
+proc genGoto(c: var Preprocessing; bc: var Bytecode; info: PackedLineInfo; lab: LabelId; opc: OpcodeM) =
   let dest = c.known.getOrDefault(lab, CodePos(-1))
   if dest.int >= 0:
-    c.bc.add info, opc, uint32 dest
+    bc.add info, opc, uint32 dest
   else:
-    let here = CodePos(c.bc.code.len)
+    let here = CodePos(bc.code.len)
     c.toPatch.mgetOrPut(lab, @[]).add here
-    c.bc.add info, opc, 1u32 # will be patched once we traversed the label
+    bc.add info, opc, 1u32 # will be patched once we traversed the label
+
+type
+  AddrMode = enum
+    InDotExpr, WantAddr
+
+template maybeDeref(doDeref: bool; body: untyped) =
+  var pos = PatchPos(-1)
+  if doDeref:
+    pos = prepare(bc, info, LoadM)
+    bc.add info, TypedM, 0'u32
+  body
+  if doDeref:
+    patch(bc, pos)
+
+const
+  ForwardedProc = 10_000_000'u32
 
-proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
+proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; flags: set[AddrMode]) =
   let info = t[n].info
 
   template recurse(opc) =
-    build c.bc, info, opc:
-      for c in sons(t, n): preprocess(c, u, t, c)
+    build bc, info, opc:
+      for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr})
 
   case t[n].kind
   of Nop:
     discard "don't use Nop"
   of ImmediateVal:
-    c.bc.add info, ImmediateValM, t[n].rawOperand
+    bc.add info, ImmediateValM, t[n].rawOperand
   of IntVal:
-    c.bc.add info, IntValM, t[n].rawOperand
+    bc.add info, IntValM, t[n].rawOperand
   of StrVal:
-    c.bc.add info, StrValM, t[n].rawOperand
+    bc.add info, StrValM, t[n].rawOperand
   of SymDef:
-    assert false, "SymDef outside of declaration context"
+    discard "happens for proc decls. Don't copy the node as we don't need it"
   of SymUse:
     let s = t[n].symId
     if c.locals.hasKey(s):
-      c.bc.add info, LoadLocalM, c.locals[s]
-    elif c.bc.u.procs.hasKey(s):
-      build c.bc, info, LoadProcM:
-        c.bc.add info, StrValM, thisModule
-        c.bc.add info, LoadLocalM, uint32 c.bc.u.procs[s]
-    elif c.bc.u.globals.hasKey(s):
-      build c.bc, info, LoadGlobalM:
-        c.bc.add info, StrValM, thisModule
-        c.bc.add info, LoadLocalM, uint32 s
+      maybeDeref(WantAddr notin flags):
+        bc.add info, LoadLocalM, c.locals[s]
+    elif bc.procs.hasKey(s):
+      bc.add info, LoadProcM, uint32 bc.procs[s]
+    elif bc.globals.hasKey(s):
+      maybeDeref(WantAddr notin flags):
+        bc.add info, LoadGlobalM, uint32 s
     else:
-      assert false, "don't understand SymUse ID"
+      let here = CodePos(bc.code.len)
+      bc.add info, LoadProcM, ForwardedProc + uint32(s)
+      bc.procUsagesToPatch.mgetOrPut(s, @[]).add here
+      #raiseAssert "don't understand SymUse ID " & $int(s)
 
   of ModuleSymUse:
-    let moduleName {.cursor.} = c.bc.u.strings[t[n.firstSon].litId]
-    let unit = u.units.getOrDefault(moduleName)
+    when false:
+      let (x, y) = sons2(t, n)
+      let unit = c.u.unitNames.getOrDefault(bc.m.lit.strings[t[x].litId], -1)
+      let s = t[y].symId
+      if c.u.units[unit].procs.hasKey(s):
+        bc.add info, LoadProcM, uint32 c.u.units[unit].procs[s]
+      elif bc.globals.hasKey(s):
+        maybeDeref(WantAddr notin flags):
+          build bc, info, LoadGlobalM:
+            bc.add info, ImmediateValM, uint32 unit
+            bc.add info, LoadLocalM, uint32 s
+      else:
+        raiseAssert "don't understand ModuleSymUse ID"
 
+    raiseAssert "don't understand ModuleSymUse ID"
   of Typed:
-    c.bc.add info, TypedM, t[n].rawOperand
+    bc.add info, TypedM, t[n].rawOperand
   of PragmaId:
-    c.bc.add info, TypedM, t[n].rawOperand
+    bc.add info, PragmaIdM, t[n].rawOperand
   of NilVal:
-    c.bc.add info, NilValM, t[n].rawOperand
+    bc.add info, NilValM, t[n].rawOperand
   of LoopLabel, Label:
     let lab = t[n].label
-    let here = CodePos(c.bc.code.len-1)
+    let here = CodePos(bc.code.len-1)
     c.known[lab] = here
-    var p: seq[CodePos]
+    var p: seq[CodePos] = @[]
     if c.toPatch.take(lab, p):
-      for x in p: c.bc.code[x] = toIns(c.bc.code[x].kind, here)
+      for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here)
     c.markedWithLabel.incl here.int # for toString()
   of Goto, GotoLoop:
-    c.genGoto(t[n].label, GotoM)
+    c.genGoto(bc, info, t[n].label, GotoM)
   of CheckedGoto:
-    c.genGoto(t[n].label, CheckedGotoM)
+    c.genGoto(bc, info, t[n].label, CheckedGotoM)
   of ArrayConstr:
-    recurse ArrayConstrM
+    let typ = t[n.firstSon].typeId
+    let s = computeElemSize(bc, typ)
+    build bc, info, ArrayConstrM:
+      bc.add info, ImmediateValM, uint32 s
+      for ch in sonsFrom1(t, n):
+        preprocess(c, bc, t, ch, {WantAddr})
   of ObjConstr:
-    recurse ObjConstrM
+    var i = 0
+    let typ = t[n.firstSon].typeId
+    build bc, info, ObjConstrM:
+      for ch in sons(t, n):
+        if i > 0:
+          if (i mod 2) == 1:
+            let (offset, typ) = bc.offsets[typ][t[ch].immediateVal]
+            let size = computeSize(bc, typ)[0]
+            bc.add info, ImmediateValM, uint32(offset)
+            bc.add info, ImmediateValM, uint32(size)
+          else:
+            preprocess(c, bc, t, ch, {WantAddr})
+        inc i
   of Ret:
     recurse RetM
   of Yld:
@@ -281,25 +449,120 @@ proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
   of SelectRange:
     recurse SelectRangeM
   of SummonGlobal, SummonThreadLocal, SummonConst:
-    #let s =
-    discard "xxx"
-  of Summon, SummonParam:
-    # x = Summon Typed <Type ID>; x begins to live
-    discard "xxx"
+    let (typ, sym) = sons2(t, n)
+
+    let s = t[sym].symId
+    let tid = t[typ].typeId
+    let (size, alignment) = computeSize(bc, tid)
+
+    let global = align(bc.globalsAddr, uint32 alignment)
+    bc.globals[s] = global
+    bc.globalsAddr += uint32 size
+    assert bc.globalsAddr < GlobalsSize
+
+  of Summon:
+    let (typ, sym) = sons2(t, n)
+
+    let s = t[sym].symId
+    let tid = t[typ].typeId
+    let (size, alignment) = computeSize(bc, tid)
+
+    let local = align(c.localsAddr, uint32 alignment)
+    c.locals[s] = local
+    c.localsAddr += uint32 size
+    # allocation is combined into the frame allocation so there is no
+    # instruction to emit
+  of SummonParam, SummonResult:
+    let (typ, sym) = sons2(t, n)
+
+    let s = t[sym].symId
+    let tid = t[typ].typeId
+    let (size, alignment) = computeSize(bc, tid)
+
+    let local = align(c.localsAddr, uint32 alignment)
+    c.locals[s] = local
+    c.localsAddr += uint32 size
+    bc.add info, SummonParamM, local
+    bc.add info, ImmediateValM, uint32 size
   of Kill:
     discard "we don't care about Kill instructions"
   of AddrOf:
-    recurse AddrOfM
+    let (_, arg) = sons2(t, n)
+    preprocess(c, bc, t, arg, {WantAddr})
+    # the address of x is what the VM works with all the time so there is
+    # nothing to compute.
   of ArrayAt:
-    recurse ArrayAtM
+    let (arrayType, a, i) = sons3(t, n)
+    let tid = t[arrayType].typeId
+    let size = uint32 computeElemSize(bc, tid)
+    if t[a].kind == Load:
+      let (_, arg) = sons2(t, a)
+      build bc, info, LoadM:
+        bc.add info, ImmediateValM, size
+        build bc, info, ArrayAtM:
+          bc.add info, ImmediateValM, size
+          preprocess(c, bc, t, arg, {WantAddr})
+          preprocess(c, bc, t, i, {WantAddr})
+    else:
+      build bc, info, ArrayAtM:
+        bc.add info, ImmediateValM, size
+        preprocess(c, bc, t, a, {WantAddr})
+        preprocess(c, bc, t, i, {WantAddr})
   of FieldAt:
-    recurse FieldAtM
+    # a[] conceptually loads a block of size of T. But when applied to an object selector
+    # only a subset of the data is really requested so `(a[] : T).field`
+    # becomes `(a+offset(field))[] : T_Field`
+    # And now if this is paired with `addr` the deref disappears, as usual: `addr x.field[]`
+    # is `(x+offset(field))`.
+    let (typ, a, b) = sons3(t, n)
+    if t[a].kind == Load:
+      let (_, arg) = sons2(t, a)
+      build bc, info, LoadM:
+        bc.add info, ImmediateValM, uint32 computeSize(bc, t[typ].typeId)[0]
+        let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0]
+        build bc, info, FieldAtM:
+          preprocess(c, bc, t, arg, flags+{WantAddr})
+          bc.add info, ImmediateValM, uint32(offset)
+    else:
+      let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0]
+      build bc, info, FieldAtM:
+        preprocess(c, bc, t, a, flags+{WantAddr})
+        bc.add info, ImmediateValM, uint32(offset)
   of Load:
-    recurse LoadM
+    let (elemType, a) = sons2(t, n)
+    let tid = t[elemType].typeId
+    build bc, info, LoadM:
+      bc.add info, ImmediateValM, uint32 computeSize(bc, tid)[0]
+      preprocess(c, bc, t, a, {})
+
   of Store:
-    recurse StoreM
+    raiseAssert "Assumption was that Store is unused!"
   of Asgn:
-    recurse AsgnM
+    let (elemType, dest, src) = sons3(t, n)
+    let tid = t[elemType].typeId
+    if t[src].kind in {Call, IndirectCall}:
+      # No support for return values, these are mapped to `var T` parameters!
+      build bc, info, CallM:
+        preprocess(c, bc, t, dest, {WantAddr})
+        for ch in sons(t, src): preprocess(c, bc, t, ch, {WantAddr})
+    elif t[src].kind in {CheckedCall, CheckedIndirectCall}:
+      build bc, info, CheckedCallM:
+        preprocess(c, bc, t, src.firstSon, {WantAddr})
+        preprocess(c, bc, t, dest, {WantAddr})
+        for ch in sonsFrom1(t, src): preprocess(c, bc, t, ch, {WantAddr})
+    elif t[dest].kind == Load:
+      let (typ, a) = sons2(t, dest)
+      let s = computeSize(bc, tid)[0]
+      build bc, info, StoreM:
+        bc.add info, ImmediateValM, uint32 s
+        preprocess(c, bc, t, a, {WantAddr})
+        preprocess(c, bc, t, src, {})
+    else:
+      let s = computeSize(bc, tid)[0]
+      build bc, info, AsgnM:
+        bc.add info, ImmediateValM, uint32 s
+        preprocess(c, bc, t, dest, {WantAddr})
+        preprocess(c, bc, t, src, {})
   of SetExc:
     recurse SetExcM
   of TestExc:
@@ -308,14 +571,14 @@ proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
     recurse CheckedRangeM
   of CheckedIndex:
     recurse CheckedIndexM
-  of Call:
-    recurse CallM
-  of IndirectCall:
-    recurse IndirectCallM
-  of CheckedCall:
-    recurse CheckedCallM
-  of CheckedIndirectCall:
-    recurse CheckedIndirectCallM
+  of Call, IndirectCall:
+    # avoid the Typed thing at position 0:
+    build bc, info, CallM:
+      for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr})
+  of CheckedCall, CheckedIndirectCall:
+    # avoid the Typed thing at position 0:
+    build bc, info, CheckedCallM:
+      for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr})
   of CheckedAdd:
     recurse CheckedAddM
   of CheckedSub:
@@ -367,9 +630,20 @@ proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
   of TestOf:
     recurse TestOfM
   of Emit:
-    assert false, "cannot interpret: Emit"
+    raiseAssert "cannot interpret: Emit"
   of ProcDecl:
-    recurse ProcDeclM
+    var c2 = Preprocessing(u: c.u, thisModule: c.thisModule)
+    let sym = t[n.firstSon].symId
+    let here = CodePos(bc.len)
+    var p: seq[CodePos] = @[]
+    if bc.procUsagesToPatch.take(sym, p):
+      for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here)
+    bc.procs[sym] = here
+    build bc, info, ProcDeclM:
+      let toPatch = bc.code.len
+      bc.add info, AllocLocals, 0'u32
+      for ch in sons(t, n): preprocess(c2, bc, t, ch, {})
+      bc.code[toPatch] = toIns(AllocLocals, c2.localsAddr)
   of PragmaPair:
     recurse PragmaPairM
 
@@ -381,87 +655,399 @@ type
     payload: array[PayloadSize, byte]
     caller: StackFrame
     returnAddr: CodePos
+    jumpTo: CodePos # exception handling
+    u: ref Universe
 
 proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame =
-  result = StackFrame(caller: caller, returnAddr: returnAddr)
+  result = StackFrame(caller: caller, returnAddr: returnAddr, u: caller.u)
   if size <= PayloadSize:
     result.locals = addr(result.payload)
   else:
     result.locals = alloc0(size)
 
 proc popStackFrame(s: StackFrame): StackFrame =
-  if result.locals != addr(result.payload):
-    dealloc result.locals
+  if s.locals != addr(s.payload):
+    dealloc s.locals
   result = s.caller
 
 template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff)
 
-proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer)
+proc isAtom(tree: seq[Instr]; pos: CodePos): bool {.inline.} = tree[pos.int].kind <= LastAtomicValue
+
+proc span(bc: seq[Instr]; pos: int): int {.inline.} =
+  if bc[pos].kind <= LastAtomicValue: 1 else: int(bc[pos].operand)
+
+proc sons2(tree: seq[Instr]; n: CodePos): (CodePos, CodePos) =
+  assert(not isAtom(tree, n))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  result = (CodePos a, CodePos b)
+
+proc sons3(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos) =
+  assert(not isAtom(tree, n))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  result = (CodePos a, CodePos b, CodePos c)
+
+proc sons4(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos, CodePos) =
+  assert(not isAtom(tree, n))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  let d = c + span(tree, c)
+  result = (CodePos a, CodePos b, CodePos c, CodePos d)
 
-proc evalAddr(c: seq[Instr]; pc: CodePos; s: StackFrame): pointer =
-  case c[pc].kind
+proc typeId*(ins: Instr): TypeId {.inline.} =
+  assert ins.kind == TypedM
+  result = TypeId(ins.operand)
+
+proc immediateVal*(ins: Instr): int {.inline.} =
+  assert ins.kind == ImmediateValM
+  result = cast[int](ins.operand)
+
+proc litId*(ins: Instr): LitId {.inline.} =
+  assert ins.kind in {StrValM, IntValM}
+  result = LitId(ins.operand)
+
+proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int)
+
+proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer =
+  case c.code[pc].kind
   of LoadLocalM:
-    result = s.locals +! c[pc].operand
+    result = s.locals +! c.code[pc].operand
   of FieldAtM:
-    result = eval(c, pc+1, s)
-    result = result +! c[pc+2].operand
+    let (x, offset) = sons2(c.code, pc)
+    result = evalAddr(c, x, s)
+    result = result +! c.code[offset].operand
   of ArrayAtM:
-    let elemSize = c[pc+1].operand
-    result = eval(c, pc+2, s)
-    var idx: int
-    eval(c, pc+3, addr idx)
-    result = result +! (idx * elemSize)
-
-proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer) =
-  case c[pc].kind
-  of AddM:
-    # assume `int` here for now:
-    var x, y: int
-    eval c, pc+1, s, addr x
-    eval c, pc+2, s, addr y
-    cast[ptr int](res)[] = x + y
+    let (e, a, i) = sons3(c.code, pc)
+    let elemSize = c.code[e].operand
+    result = evalAddr(c, a, s)
+    var idx: int = 0
+    eval(c, i, s, addr idx, sizeof(int))
+    result = result +! (uint32(idx) * elemSize)
+  of LoadM:
+    let (_, arg) = sons2(c.code, pc)
+    let p = evalAddr(c, arg, s)
+    result = cast[ptr pointer](p)[]
+  of LoadGlobalM:
+    result = c.globalData +! c.code[pc].operand
+  else:
+    raiseAssert("unimplemented addressing mode")
+
+proc `div`(x, y: float32): float32 {.inline.} = x / y
+proc `div`(x, y: float64): float64 {.inline.} = x / y
+
+from math import `mod`
+
+template binop(opr) {.dirty.} =
+  template impl(typ) {.dirty.} =
+    var x = default(typ)
+    var y = default(typ)
+    eval c, a, s, addr x, sizeof(typ)
+    eval c, b, s, addr y, sizeof(typ)
+    cast[ptr typ](result)[] = opr(x, y)
+
+  let (t, a, b) = sons3(c.code, pc)
+  let tid = TypeId c.code[t].operand
+  case tid
+  of Bool8Id, Char8Id, UInt8Id: impl uint8
+  of Int8Id: impl int8
+  of Int16Id: impl int16
+  of Int32Id: impl int32
+  of Int64Id: impl int64
+  of UInt16Id: impl uint16
+  of UInt32Id: impl uint32
+  of UInt64Id: impl uint64
+  of Float32Id: impl float32
+  of Float64Id: impl float64
+  else: discard
+
+template checkedBinop(opr) {.dirty.} =
+  template impl(typ) {.dirty.} =
+    var x = default(typ)
+    var y = default(typ)
+    eval c, a, s, addr x, sizeof(typ)
+    eval c, b, s, addr y, sizeof(typ)
+    try:
+      cast[ptr typ](result)[] = opr(x, y)
+    except OverflowDefect, DivByZeroDefect:
+      s.jumpTo = CodePos c.code[j].operand
+
+  let (t, j, a, b) = sons4(c.code, pc)
+  let tid = TypeId c.code[t].operand
+  case tid
+  of Bool8Id, Char8Id, UInt8Id: impl uint8
+  of Int8Id: impl int8
+  of Int16Id: impl int16
+  of Int32Id: impl int32
+  of Int64Id: impl int64
+  of UInt16Id: impl uint16
+  of UInt32Id: impl uint32
+  of UInt64Id: impl uint64
+  of Float32Id: impl float32
+  of Float64Id: impl float64
+  else: discard
+
+template bitop(opr) {.dirty.} =
+  template impl(typ) {.dirty.} =
+    var x = default(typ)
+    var y = default(typ)
+    eval c, a, s, addr x, sizeof(typ)
+    eval c, b, s, addr y, sizeof(typ)
+    cast[ptr typ](result)[] = opr(x, y)
+
+  let (t, a, b) = sons3(c.code, pc)
+  let tid = c.code[t].typeId
+  case tid
+  of Bool8Id, Char8Id, UInt8Id: impl uint8
+  of Int8Id: impl int8
+  of Int16Id: impl int16
+  of Int32Id: impl int32
+  of Int64Id: impl int64
+  of UInt16Id: impl uint16
+  of UInt32Id: impl uint32
+  of UInt64Id: impl uint64
+  else: discard
+
+template cmpop(opr) {.dirty.} =
+  template impl(typ) {.dirty.} =
+    var x = default(typ)
+    var y = default(typ)
+    eval c, a, s, addr x, sizeof(typ)
+    eval c, b, s, addr y, sizeof(typ)
+    cast[ptr bool](result)[] = opr(x, y)
+
+  let (t, a, b) = sons3(c.code, pc)
+  let tid = c.code[t].typeId
+  case tid
+  of Bool8Id, Char8Id, UInt8Id: impl uint8
+  of Int8Id: impl int8
+  of Int16Id: impl int16
+  of Int32Id: impl int32
+  of Int64Id: impl int64
+  of UInt16Id: impl uint16
+  of UInt32Id: impl uint32
+  of UInt64Id: impl uint64
+  of Float32Id: impl float32
+  of Float64Id: impl float64
+  else: discard
+
+proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos =
+  template impl(typ) {.dirty.} =
+    var selector = default(typ)
+    eval c, sel, s, addr selector, sizeof(typ)
+    for pair in sonsFrom2(c, pc):
+      assert c.code[pair].kind == SelectPairM
+      let (values, action) = sons2(c.code, pair)
+      assert c.code[values].kind == SelectListM
+      for v in sons(c, values):
+        case c.code[v].kind
+        of SelectValueM:
+          var a = default(typ)
+          eval c, v.firstSon, s, addr a, sizeof(typ)
+          if selector == a:
+            return CodePos c.code[action].operand
+        of SelectRangeM:
+          let (va, vb) = sons2(c.code, v)
+          var a = default(typ)
+          eval c, va, s, addr a, sizeof(typ)
+          var b = default(typ)
+          eval c, vb, s, addr a, sizeof(typ)
+          if a <= selector and selector <= b:
+            return CodePos c.code[action].operand
+        else: raiseAssert "unreachable"
+    result = CodePos(-1)
+
+  let (t, sel) = sons2(c.code, pc)
+  let tid = c.code[t].typeId
+  case tid
+  of Bool8Id, Char8Id, UInt8Id: impl uint8
+  of Int8Id: impl int8
+  of Int16Id: impl int16
+  of Int32Id: impl int32
+  of Int64Id: impl int64
+  of UInt16Id: impl uint16
+  of UInt32Id: impl uint32
+  of UInt64Id: impl uint64
+  else: raiseAssert "unreachable"
+
+proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) =
+  case c.code[pc].kind
+  of LoadLocalM:
+    let dest = s.locals +! c.code[pc].operand
+    copyMem dest, result, size
+  of FieldAtM, ArrayAtM, LoadM:
+    let dest = evalAddr(c, pc, s)
+    copyMem dest, result, size
+  of CheckedAddM: checkedBinop `+`
+  of CheckedSubM: checkedBinop `-`
+  of CheckedMulM: checkedBinop `*`
+  of CheckedDivM: checkedBinop `div`
+  of CheckedModM: checkedBinop `mod`
+  of AddM: binop `+`
+  of SubM: binop `-`
+  of MulM: binop `*`
+  of DivM: binop `div`
+  of ModM: binop `mod`
+  of BitShlM: bitop `shl`
+  of BitShrM: bitop `shr`
+  of BitAndM: bitop `and`
+  of BitOrM: bitop `or`
+  of BitXorM: bitop `xor`
+  of EqM: cmpop `==`
+  of LeM: cmpop `<=`
+  of LtM: cmpop `<`
+
   of StrValM:
-    cast[ptr StringDesc](res)[] = addr(c.strings[c[pc].litId])
+    # binary compatible and no deep copy required:
+    copyMem(cast[ptr string](result), addr(c.m.lit.strings[c[pc].litId]), sizeof(string))
+    # XXX not correct!
   of ObjConstrM:
-    for ch in sons(c, pc):
-      let offset = c[ch]
-      eval c, ch+2, s, result+!offset
+    for offset, size, val in triples(c, pc):
+      eval c, val, s, result+!offset, size
   of ArrayConstrM:
-    let elemSize = c[pc+1].operand
+    let elemSize = c.code[pc.firstSon].operand
     var r = result
-    for ch in sons(c, pc):
-      eval c, ch, s, r
+    for ch in sonsFrom1(c, pc):
+      eval c, ch, s, r, elemSize.int
       r = r+!elemSize # can even do strength reduction here!
+  of NumberConvM:
+    let (t, x) = sons2(c.code, pc)
+    let word = if c[x].kind == NilValM: 0'i64 else: c.m.lit.numbers[c[x].litId]
+
+    template impl(typ: typedesc) {.dirty.} =
+      cast[ptr typ](result)[] = cast[typ](word)
+
+    let tid = c.code[t].typeId
+    case tid
+    of Bool8Id, Char8Id, UInt8Id: impl uint8
+    of Int8Id: impl int8
+    of Int16Id: impl int16
+    of Int32Id: impl int32
+    of Int64Id: impl int64
+    of UInt16Id: impl uint16
+    of UInt32Id: impl uint32
+    of UInt64Id: impl uint64
+    else:
+      case c.m.types[tid].kind
+      of ProcTy, UPtrTy, APtrTy, AArrayPtrTy, UArrayPtrTy:
+        # the VM always uses 64 bit pointers:
+        impl uint64
+      else:
+        raiseAssert "cannot happen"
   else:
-    assert false, "cannot happen"
+    #debug c, c.debug[pc.int]
+    raiseAssert "cannot happen: " & $c.code[pc].kind
 
-proc exec(c: seq[Instr]; pc: CodePos) =
-  var pc = pc
-  var currentFrame: StackFrame = nil
+proc evalProc(c: Bytecode; pc: CodePos; s: StackFrame): CodePos =
+  assert c.code[pc].kind == LoadProcM
+  let procSym = c[pc].operand
+  when false:
+    if procSym >= ForwardedProc:
+      for k, v in c.procUsagesToPatch:
+        if uint32(k) == procSym - ForwardedProc:
+          echo k.int, " ", v.len, " <-- this one"
+        else:
+          echo k.int, " ", v.len
+
+  assert procSym < ForwardedProc
+  result = CodePos(procSym)
+
+proc echoImpl(c: Bytecode; pc: CodePos; s: StackFrame) =
+  type StringArray = object
+    len: int
+    data: ptr UncheckedArray[string]
+  var sa = default(StringArray)
+  for a in sonsFrom1(c, pc):
+    eval(c, a, s, addr(sa), sizeof(sa))
+  for i in 0..<sa.len:
+    stdout.write sa.data[i]
+  stdout.write "\n"
+  stdout.flushFile()
+
+proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; didEval: var bool): CodePos =
+  var prc = prc
   while true:
-    case c[pc].kind
+    case c[prc].kind
+    of PragmaPairM:
+      let (x, y) = sons2(c.code, prc)
+      if cast[PragmaKey](c[x]) == CoreName:
+        let lit = c[y].litId
+        case c.m.lit.strings[lit]
+        of "echoBinSafe": echoImpl(c, pc, s)
+        else: discard
+        echo "running compilerproc: ", c.m.lit.strings[lit]
+        didEval = true
+    of PragmaIdM: discard
+    else: break
+    next c, prc
+  result = prc
+
+proc exec(c: Bytecode; pc: CodePos; u: ref Universe) =
+  var pc = pc
+  var s = StackFrame(u: u)
+  while pc.int < c.code.len:
+    case c.code[pc].kind
     of GotoM:
-      pc = CodePos(c[pc].operand)
-    of Asgn:
-      let (size, a, b) = sons3(c, pc)
+      pc = CodePos(c.code[pc].operand)
+    of AsgnM:
+      let (sz, a, b) = sons3(c.code, pc)
       let dest = evalAddr(c, a, s)
-      eval(c, b, s, dest)
+      eval(c, b, s, dest, c.code[sz].operand.int)
+      next c, pc
+    of StoreM:
+      let (sz, a, b) = sons3(c.code, pc)
+      let destPtr = evalAddr(c, a, s)
+      let dest = cast[ptr pointer](destPtr)[]
+      eval(c, b, s, dest, c.code[sz].operand.int)
+      next c, pc
     of CallM:
       # No support for return values, these are mapped to `var T` parameters!
-      let prc = evalProc(c, pc+1)
-      # setup storage for the proc already:
-      let s2 = newStackFrame(prc.frameSize, currentFrame, pc)
-      var i = 0
-      for a in sons(c, pc):
-        eval(c, a, s2, paramAddr(s2, i))
-        inc i
-      currentFrame = s2
-      pc = pcOf(prc)
+      var prc = evalProc(c, pc.firstSon, s)
+      assert c.code[prc.firstSon].kind == AllocLocals
+      let frameSize = int c.code[prc.firstSon].operand
+      # skip stupid stuff:
+      var didEval = false
+      prc = evalBuiltin(c, pc, s, prc.firstSon, didEval)
+      if didEval:
+        next c, pc
+      else:
+        # setup storage for the proc already:
+        let callInstr = pc
+        next c, pc
+        let s2 = newStackFrame(frameSize, s, pc)
+        for a in sonsFrom1(c, callInstr):
+          assert c[prc].kind == SummonParamM
+          let paramAddr = c[prc].operand
+          assert c[prc.firstSon].kind == ImmediateValM
+          let paramSize = c[prc.firstSon].operand.int
+          eval(c, a, s2, s2.locals +! paramAddr, paramSize)
+          next c, prc
+          next c, prc
+        s = s2
+        pc = prc
     of RetM:
-      pc = currentFrame.returnAddr
-      currentFrame = popStackFrame(currentFrame)
+      pc = s.returnAddr
+      s = popStackFrame(s)
     of SelectM:
-      var x: bool
-      eval(c, b, addr x)
-      # follow the selection instructions...
-      pc = activeBranch(c, b, x)
+      let pc2 = evalSelect(c, pc, s)
+      if pc2.int >= 0:
+        pc = pc2
+      else:
+        next c, pc
+    of ProcDeclM:
+      next c, pc
+    else:
+      raiseAssert "unreachable"
+
+proc execCode*(bc: var Bytecode; t: Tree; n: NodePos) =
+  traverseTypes bc
+  var c = Preprocessing(u: nil, thisModule: 1'u32)
+  let start = CodePos(bc.code.len)
+  var pc = n
+  while pc.int < t.len:
+    preprocess c, bc, t, pc, {}
+    next t, pc
+  exec bc, start, nil
diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim
index 76907d587..4c3ce7001 100644
--- a/compiler/nir/types2ir.nim
+++ b/compiler/nir/types2ir.nim
@@ -15,11 +15,11 @@ type
   TypesCon* = object
     processed: Table[ItemId, TypeId]
     recursionCheck: HashSet[ItemId]
-    g*: TypeGraph
     conf: ConfigRef
+    stringType: TypeId
 
-proc initTypesCon*(conf: ConfigRef; lit: Literals): TypesCon =
-  TypesCon(g: initTypeGraph(lit), conf: conf)
+proc initTypesCon*(conf: ConfigRef): TypesCon =
+  TypesCon(conf: conf, stringType: TypeId(-1))
 
 proc mangle(c: var TypesCon; t: PType): string =
   result = $sighashes.hashType(t, c.conf)
@@ -30,131 +30,131 @@ template cached(c: var TypesCon; t: PType; body: untyped) =
     body
     c.processed[t.itemId] = result
 
-proc typeToIr*(c: var TypesCon; t: PType): TypeId
+proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
 
-proc collectFieldTypes(c: var TypesCon; n: PNode; dest: var Table[ItemId, TypeId]) =
+proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
   case n.kind
   of nkRecList:
     for i in 0..<n.len:
-      collectFieldTypes(c, n[i], dest)
+      collectFieldTypes(c, g, n[i], dest)
   of nkRecCase:
     assert(n[0].kind == nkSym)
-    collectFieldTypes(c, n[0], dest)
+    collectFieldTypes(c, g, n[0], dest)
     for i in 1..<n.len:
       case n[i].kind
       of nkOfBranch, nkElse:
-        collectFieldTypes c, lastSon(n[i]), dest
+        collectFieldTypes c, g, lastSon(n[i]), dest
       else: discard
   of nkSym:
-    dest[n.sym.itemId] = typeToIr(c, n.sym.typ)
+    dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ)
   else:
     assert false, "unknown node kind: " & $n.kind
 
-proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
+proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
   case n.kind
   of nkRecList:
     for i in 0..<n.len:
-      objectToIr(c, n[i], fieldTypes, unionId)
+      objectToIr(c, g, n[i], fieldTypes, unionId)
   of nkRecCase:
     assert(n[0].kind == nkSym)
-    objectToIr(c, n[0], fieldTypes, unionId)
-    let u = openType(c.g, UnionDecl)
-    c.g.addName "u_" & $unionId
+    objectToIr(c, g, n[0], fieldTypes, unionId)
+    let u = openType(g, UnionDecl)
+    g.addName "u_" & $unionId
     inc unionId
     for i in 1..<n.len:
       case n[i].kind
       of nkOfBranch, nkElse:
-        let subObj = openType(c.g, ObjectDecl)
-        c.g.addName "uo_" & $unionId & "_" & $i
-        objectToIr c, lastSon(n[i]), fieldTypes, unionId
-        sealType(c.g, subObj)
+        let subObj = openType(g, ObjectDecl)
+        g.addName "uo_" & $unionId & "_" & $i
+        objectToIr c, g, lastSon(n[i]), fieldTypes, unionId
+        sealType(g, subObj)
       else: discard
-    sealType(c.g, u)
+    sealType(g, u)
   of nkSym:
-    c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
+    g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
   else:
     assert false, "unknown node kind: " & $n.kind
 
-proc objectToIr(c: var TypesCon; t: PType): TypeId =
+proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   if t[0] != nil:
     # ensure we emitted the base type:
-    discard typeToIr(c, t[0])
+    discard typeToIr(c, g, t[0])
 
   var unionId = 0
   var fieldTypes = initTable[ItemId, TypeId]()
-  collectFieldTypes c, t.n, fieldTypes
-  let obj = openType(c.g, ObjectDecl)
-  c.g.addName mangle(c, t)
-  c.g.addSize c.conf.getSize(t)
-  c.g.addAlign c.conf.getAlign(t)
+  collectFieldTypes c, g, t.n, fieldTypes
+  let obj = openType(g, ObjectDecl)
+  g.addName mangle(c, t)
+  g.addSize c.conf.getSize(t)
+  g.addAlign c.conf.getAlign(t)
 
   if t[0] != nil:
-    c.g.addNominalType(ObjectTy, mangle(c, t[0]))
+    g.addNominalType(ObjectTy, mangle(c, t[0]))
   else:
-    c.g.addBuiltinType VoidId # object does not inherit
+    g.addBuiltinType VoidId # object does not inherit
     if not lacksMTypeField(t):
-      let f2 = c.g.openType FieldDecl
-      let voidPtr = openType(c.g, APtrTy)
-      c.g.addBuiltinType(VoidId)
-      sealType(c.g, voidPtr)
-      c.g.addOffset 0 # type field is always at offset 0
-      c.g.addName "m_type"
-      sealType(c.g, f2) # FieldDecl
+      let f2 = g.openType FieldDecl
+      let voidPtr = openType(g, APtrTy)
+      g.addBuiltinType(VoidId)
+      sealType(g, voidPtr)
+      g.addOffset 0 # type field is always at offset 0
+      g.addName "m_type"
+      sealType(g, f2) # FieldDecl
 
-  objectToIr c, t.n, fieldTypes, unionId
-  result = finishType(c.g, obj)
+  objectToIr c, g, t.n, fieldTypes, unionId
+  result = finishType(g, obj)
 
-proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId =
-  result = c.g.nominalType(ObjectTy, mangle(c, t))
+proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
+  result = g.nominalType(ObjectTy, mangle(c, t))
 
-proc tupleToIr(c: var TypesCon; t: PType): TypeId =
+proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   var fieldTypes = newSeq[TypeId](t.len)
   for i in 0..<t.len:
-    fieldTypes[i] = typeToIr(c, t[i])
-  let obj = openType(c.g, ObjectDecl)
-  c.g.addName mangle(c, t)
-  c.g.addSize c.conf.getSize(t)
-  c.g.addAlign c.conf.getAlign(t)
+    fieldTypes[i] = typeToIr(c, g, t[i])
+  let obj = openType(g, ObjectDecl)
+  g.addName mangle(c, t)
+  g.addSize c.conf.getSize(t)
+  g.addAlign c.conf.getAlign(t)
 
   var accum = OffsetAccum(maxAlign: 1)
   for i in 0..<t.len:
     let child = t[i]
-    c.g.addField "f_" & $i, fieldTypes[i], accum.offset
+    g.addField "f_" & $i, fieldTypes[i], accum.offset
 
     computeSizeAlign(c.conf, child)
     accum.align(child.align)
     accum.inc(int32(child.size))
-  result = finishType(c.g, obj)
+  result = finishType(g, obj)
 
-proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
+proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId =
   var fieldTypes = newSeq[TypeId](0)
   for i in 0..<t.len:
     if t[i] == nil or not isCompileTimeOnly(t[i]):
-      fieldTypes.add typeToIr(c, t[i])
-  let obj = openType(c.g, ProcTy)
+      fieldTypes.add typeToIr(c, g, t[i])
+  let obj = openType(g, ProcTy)
 
   case t.callConv
-  of ccNimCall, ccFastCall, ccClosure: c.g.addAnnotation "__fastcall"
-  of ccStdCall: c.g.addAnnotation "__stdcall"
-  of ccCDecl: c.g.addAnnotation "__cdecl"
-  of ccSafeCall: c.g.addAnnotation "__safecall"
-  of ccSysCall: c.g.addAnnotation "__syscall"
-  of ccInline: c.g.addAnnotation "__inline"
-  of ccNoInline: c.g.addAnnotation "__noinline"
-  of ccThisCall: c.g.addAnnotation "__thiscall"
-  of ccNoConvention: c.g.addAnnotation ""
+  of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall"
+  of ccStdCall: g.addAnnotation "__stdcall"
+  of ccCDecl: g.addAnnotation "__cdecl"
+  of ccSafeCall: g.addAnnotation "__safecall"
+  of ccSysCall: g.addAnnotation "__syscall"
+  of ccInline: g.addAnnotation "__inline"
+  of ccNoInline: g.addAnnotation "__noinline"
+  of ccThisCall: g.addAnnotation "__thiscall"
+  of ccNoConvention: g.addAnnotation ""
 
   for i in 0..<fieldTypes.len:
-    c.g.addType fieldTypes[i]
+    g.addType fieldTypes[i]
 
   if addEnv:
-    let a = openType(c.g, APtrTy)
-    c.g.addBuiltinType(VoidId)
-    sealType(c.g, a)
+    let a = openType(g, APtrTy)
+    g.addBuiltinType(VoidId)
+    sealType(g, a)
 
   if tfVarargs in t.flags:
-    c.g.addVarargs()
-  result = finishType(c.g, obj)
+    g.addVarargs()
+  result = finishType(g, obj)
 
 proc nativeInt(c: TypesCon): TypeId =
   case c.conf.target.intSize
@@ -162,65 +162,65 @@ proc nativeInt(c: TypesCon): TypeId =
   of 4: result = Int32Id
   else: result = Int64Id
 
-proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId =
+proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   let e = lastSon(t)
-  let elementType = typeToIr(c, e)
-  let arr = c.g.openType AArrayPtrTy
-  c.g.addType elementType
-  result = finishType(c.g, arr) # LastArrayTy
+  let elementType = typeToIr(c, g, e)
+  let arr = g.openType AArrayPtrTy
+  g.addType elementType
+  result = finishType(g, arr) # LastArrayTy
 
-proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
+proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   # object (a: ArrayPtr[T], len: int)
   let e = lastSon(t)
   let mangledBase = mangle(c, e)
   let typeName = "NimOpenArray" & mangledBase
 
-  let elementType = typeToIr(c, e)
+  let elementType = typeToIr(c, g, e)
   #assert elementType.int >= 0, typeToString(t)
 
-  let p = openType(c.g, ObjectDecl)
-  c.g.addName typeName
-  c.g.addSize c.conf.target.ptrSize*2
-  c.g.addAlign c.conf.target.ptrSize
-
-  let f = c.g.openType FieldDecl
-  let arr = c.g.openType AArrayPtrTy
-  c.g.addType elementType
-  sealType(c.g, arr) # LastArrayTy
-  c.g.addOffset 0
-  c.g.addName "data"
-  sealType(c.g, f) # FieldDecl
-
-  c.g.addField "len", c.nativeInt, c.conf.target.ptrSize
-
-  result = finishType(c.g, p) # ObjectDecl
-
-proc strPayloadType(c: var TypesCon): string =
-  result = "NimStrPayload"
-  let p = openType(c.g, ObjectDecl)
-  c.g.addName result
-  c.g.addSize c.conf.target.ptrSize*2
-  c.g.addAlign c.conf.target.ptrSize
-
-  c.g.addField "cap", c.nativeInt, 0
-
-  let f = c.g.openType FieldDecl
-  let arr = c.g.openType LastArrayTy
-  c.g.addBuiltinType Char8Id
-  sealType(c.g, arr) # LastArrayTy
-  c.g.addOffset c.conf.target.ptrSize # comes after the len field
-  c.g.addName "data"
-  sealType(c.g, f) # FieldDecl
-
-  sealType(c.g, p)
-
-proc strPayloadPtrType*(c: var TypesCon): TypeId =
-  let mangled = strPayloadType(c)
-  let ffp = c.g.openType APtrTy
-  c.g.addNominalType ObjectTy, mangled
-  result = finishType(c.g, ffp) # APtrTy
-
-proc stringToIr(c: var TypesCon): TypeId =
+  let p = openType(g, ObjectDecl)
+  g.addName typeName
+  g.addSize c.conf.target.ptrSize*2
+  g.addAlign c.conf.target.ptrSize
+
+  let f = g.openType FieldDecl
+  let arr = g.openType AArrayPtrTy
+  g.addType elementType
+  sealType(g, arr) # LastArrayTy
+  g.addOffset 0
+  g.addName "data"
+  sealType(g, f) # FieldDecl
+
+  g.addField "len", c.nativeInt, c.conf.target.ptrSize
+
+  result = finishType(g, p) # ObjectDecl
+
+proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) =
+  result = ("NimStrPayload", TypeId(-1))
+  let p = openType(g, ObjectDecl)
+  g.addName result[0]
+  g.addSize c.conf.target.ptrSize*2
+  g.addAlign c.conf.target.ptrSize
+
+  g.addField "cap", c.nativeInt, 0
+
+  let f = g.openType FieldDecl
+  let arr = g.openType LastArrayTy
+  g.addBuiltinType Char8Id
+  result[1] = finishType(g, arr) # LastArrayTy
+  g.addOffset c.conf.target.ptrSize # comes after the len field
+  g.addName "data"
+  sealType(g, f) # FieldDecl
+
+  sealType(g, p)
+
+proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) =
+  let (mangled, arrayType) = strPayloadType(c, g)
+  let ffp = g.openType APtrTy
+  g.addNominalType ObjectTy, mangled
+  result = (finishType(g, ffp), arrayType) # APtrTy
+
+proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId =
   #[
 
     NimStrPayload = object
@@ -232,86 +232,86 @@ proc stringToIr(c: var TypesCon): TypeId =
       p: ptr NimStrPayload
 
   ]#
-  let payload = strPayloadType(c)
+  let payload = strPayloadType(c, g)
 
-  let str = openType(c.g, ObjectDecl)
-  c.g.addName "NimStringV2"
-  c.g.addSize c.conf.target.ptrSize*2
-  c.g.addAlign c.conf.target.ptrSize
+  let str = openType(g, ObjectDecl)
+  g.addName "NimStringV2"
+  g.addSize c.conf.target.ptrSize*2
+  g.addAlign c.conf.target.ptrSize
 
-  c.g.addField "len", c.nativeInt, 0
+  g.addField "len", c.nativeInt, 0
 
-  let fp = c.g.openType FieldDecl
-  let ffp = c.g.openType APtrTy
-  c.g.addNominalType ObjectTy, "NimStrPayload"
-  sealType(c.g, ffp) # APtrTy
-  c.g.addOffset c.conf.target.ptrSize # comes after 'len' field
-  c.g.addName "p"
-  sealType(c.g, fp) # FieldDecl
+  let fp = g.openType FieldDecl
+  let ffp = g.openType APtrTy
+  g.addNominalType ObjectTy, "NimStrPayload"
+  sealType(g, ffp) # APtrTy
+  g.addOffset c.conf.target.ptrSize # comes after 'len' field
+  g.addName "p"
+  sealType(g, fp) # FieldDecl
 
-  result = finishType(c.g, str) # ObjectDecl
+  result = finishType(g, str) # ObjectDecl
 
-proc seqPayloadType(c: var TypesCon; t: PType): string =
+proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) =
   #[
     NimSeqPayload[T] = object
       cap: int
       data: UncheckedArray[T]
   ]#
   let e = lastSon(t)
-  result = mangle(c, e)
-  let payloadName = "NimSeqPayload" & result
-
-  let elementType = typeToIr(c, e)
-
-  let p = openType(c.g, ObjectDecl)
-  c.g.addName payloadName
-  c.g.addSize c.conf.target.intSize
-  c.g.addAlign c.conf.target.intSize
-
-  c.g.addField "cap", c.nativeInt, 0
-
-  let f = c.g.openType FieldDecl
-  let arr = c.g.openType LastArrayTy
-  c.g.addType elementType
-  sealType(c.g, arr) # LastArrayTy
-  c.g.addOffset c.conf.target.ptrSize
-  c.g.addName "data"
-  sealType(c.g, f) # FieldDecl
-  sealType(c.g, p)
-
-proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId =
-  let mangledBase = seqPayloadType(c, t)
-  let ffp = c.g.openType APtrTy
-  c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  result = finishType(c.g, ffp) # APtrTy
-
-proc seqToIr(c: var TypesCon; t: PType): TypeId =
+  result = (mangle(c, e), TypeId(-1))
+  let payloadName = "NimSeqPayload" & result[0]
+
+  let elementType = typeToIr(c, g, e)
+
+  let p = openType(g, ObjectDecl)
+  g.addName payloadName
+  g.addSize c.conf.target.intSize
+  g.addAlign c.conf.target.intSize
+
+  g.addField "cap", c.nativeInt, 0
+
+  let f = g.openType FieldDecl
+  let arr = g.openType LastArrayTy
+  g.addType elementType
+  result[1] = finishType(g, arr) # LastArrayTy
+  g.addOffset c.conf.target.ptrSize
+  g.addName "data"
+  sealType(g, f) # FieldDecl
+  sealType(g, p)
+
+proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
+  let (mangledBase, arrayType) = seqPayloadType(c, g, t)
+  let ffp = g.openType APtrTy
+  g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
+  result = (finishType(g, ffp), arrayType) # APtrTy
+
+proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   #[
     NimSeqV2*[T] = object
       len: int
       p: ptr NimSeqPayload[T]
   ]#
-  let mangledBase = seqPayloadType(c, t)
+  let (mangledBase, _) = seqPayloadType(c, g, t)
 
-  let sq = openType(c.g, ObjectDecl)
-  c.g.addName "NimSeqV2" & mangledBase
-  c.g.addSize c.conf.getSize(t)
-  c.g.addAlign c.conf.getAlign(t)
+  let sq = openType(g, ObjectDecl)
+  g.addName "NimSeqV2" & mangledBase
+  g.addSize c.conf.getSize(t)
+  g.addAlign c.conf.getAlign(t)
 
-  c.g.addField "len", c.nativeInt, 0
+  g.addField "len", c.nativeInt, 0
 
-  let fp = c.g.openType FieldDecl
-  let ffp = c.g.openType APtrTy
-  c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  sealType(c.g, ffp) # APtrTy
-  c.g.addOffset c.conf.target.ptrSize
-  c.g.addName "p"
-  sealType(c.g, fp) # FieldDecl
+  let fp = g.openType FieldDecl
+  let ffp = g.openType APtrTy
+  g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
+  sealType(g, ffp) # APtrTy
+  g.addOffset c.conf.target.ptrSize
+  g.addName "p"
+  sealType(g, fp) # FieldDecl
 
-  result = finishType(c.g, sq) # ObjectDecl
+  result = finishType(g, sq) # ObjectDecl
 
 
-proc closureToIr(c: var TypesCon; t: PType): TypeId =
+proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   # struct {fn(args, void* env), env}
   # typedef struct {$n" &
   #        "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
@@ -319,31 +319,31 @@ proc closureToIr(c: var TypesCon; t: PType): TypeId =
   let mangledBase = mangle(c, t)
   let typeName = "NimClosure" & mangledBase
 
-  let procType = procToIr(c, t, addEnv=true)
+  let procType = procToIr(c, g, t, addEnv=true)
 
-  let p = openType(c.g, ObjectDecl)
-  c.g.addName typeName
-  c.g.addSize c.conf.getSize(t)
-  c.g.addAlign c.conf.getAlign(t)
+  let p = openType(g, ObjectDecl)
+  g.addName typeName
+  g.addSize c.conf.getSize(t)
+  g.addAlign c.conf.getAlign(t)
 
-  let f = c.g.openType FieldDecl
-  c.g.addType procType
-  c.g.addOffset 0
-  c.g.addName "ClP_0"
-  sealType(c.g, f) # FieldDecl
+  let f = g.openType FieldDecl
+  g.addType procType
+  g.addOffset 0
+  g.addName "ClP_0"
+  sealType(g, f) # FieldDecl
 
-  let f2 = c.g.openType FieldDecl
-  let voidPtr = openType(c.g, APtrTy)
-  c.g.addBuiltinType(VoidId)
-  sealType(c.g, voidPtr)
+  let f2 = g.openType FieldDecl
+  let voidPtr = openType(g, APtrTy)
+  g.addBuiltinType(VoidId)
+  sealType(g, voidPtr)
 
-  c.g.addOffset c.conf.target.ptrSize
-  c.g.addName "ClE_0"
-  sealType(c.g, f2) # FieldDecl
+  g.addOffset c.conf.target.ptrSize
+  g.addName "ClE_0"
+  sealType(g, f2) # FieldDecl
 
-  result = finishType(c.g, p) # ObjectDecl
+  result = finishType(g, p) # ObjectDecl
 
-proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId =
+proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   let s = int(getSize(c.conf, t))
   case s
   of 1: result = UInt8Id
@@ -352,7 +352,7 @@ proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId =
   of 8: result = UInt64Id
   else: result = UInt8Id
 
-proc typeToIr*(c: var TypesCon; t: PType): TypeId =
+proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
   if t == nil: return VoidId
   case t.kind
   of tyInt:
@@ -370,7 +370,7 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
     else: result = Float64Id
   of tyFloat32: result = Float32Id
   of tyFloat64: result = Float64Id
-  of tyFloat128: result = getFloat128Type(c.g)
+  of tyFloat128: result = getFloat128Type(g)
   of tyUInt:
     case int(getSize(c.conf, t))
     of 2: result = UInt16Id
@@ -384,7 +384,7 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
   of tyChar: result = Char8Id
   of tyVoid: result = VoidId
   of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange:
-    result = typeToIr(c, t.lastSon)
+    result = typeToIr(c, g, t.lastSon)
   of tyEnum:
     if firstOrd(c.conf, t) < 0:
       result = Int32Id
@@ -397,47 +397,47 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
       else: result = Int32Id
   of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
     if t.len > 0:
-      result = typeToIr(c, t.lastSon)
+      result = typeToIr(c, g, t.lastSon)
     else:
       result = TypeId(-1)
   of tyFromExpr:
     if t.n != nil and t.n.typ != nil:
-      result = typeToIr(c, t.n.typ)
+      result = typeToIr(c, g, t.n.typ)
     else:
       result = TypeId(-1)
   of tyArray:
     cached(c, t):
       var n = toInt64(lengthOrd(c.conf, t))
       if n <= 0: n = 1   # make an array of at least one element
-      let elemType = typeToIr(c, t[1])
-      let a = openType(c.g, ArrayTy)
-      c.g.addType(elemType)
-      c.g.addArrayLen n
-      result = finishType(c.g, a)
+      let elemType = typeToIr(c, g, t[1])
+      let a = openType(g, ArrayTy)
+      g.addType(elemType)
+      g.addArrayLen n
+      result = finishType(g, a)
   of tyPtr, tyRef:
     cached(c, t):
       let e = t.lastSon
       if e.kind == tyUncheckedArray:
-        let elemType = typeToIr(c, e.lastSon)
-        let a = openType(c.g, AArrayPtrTy)
-        c.g.addType(elemType)
-        result = finishType(c.g, a)
+        let elemType = typeToIr(c, g, e.lastSon)
+        let a = openType(g, AArrayPtrTy)
+        g.addType(elemType)
+        result = finishType(g, a)
       else:
-        let elemType = typeToIr(c, t.lastSon)
-        let a = openType(c.g, APtrTy)
-        c.g.addType(elemType)
-        result = finishType(c.g, a)
+        let elemType = typeToIr(c, g, t.lastSon)
+        let a = openType(g, APtrTy)
+        g.addType(elemType)
+        result = finishType(g, a)
   of tyVar, tyLent:
     cached(c, t):
       let e = t.lastSon
       if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
         # skip the modifier, `var openArray` is a (ptr, len) pair too:
-        result = typeToIr(c, e)
+        result = typeToIr(c, g, e)
       else:
-        let elemType = typeToIr(c, e)
-        let a = openType(c.g, APtrTy)
-        c.g.addType(elemType)
-        result = finishType(c.g, a)
+        let elemType = typeToIr(c, g, e)
+        let a = openType(g, APtrTy)
+        g.addType(elemType)
+        result = finishType(g, a)
   of tySet:
     let s = int(getSize(c.conf, t))
     case s
@@ -448,54 +448,57 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
     else:
       # array[U8, s]
       cached(c, t):
-        let a = openType(c.g, ArrayTy)
-        c.g.addType(UInt8Id)
-        c.g.addArrayLen s
-        result = finishType(c.g, a)
+        let a = openType(g, ArrayTy)
+        g.addType(UInt8Id)
+        g.addArrayLen s
+        result = finishType(g, a)
   of tyPointer, tyNil:
     # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
-    let a = openType(c.g, APtrTy)
-    c.g.addBuiltinType(VoidId)
-    result = finishType(c.g, a)
+    let a = openType(g, APtrTy)
+    g.addBuiltinType(VoidId)
+    result = finishType(g, a)
   of tyObject:
     # Objects are special as they can be recursive in Nim. This is easily solvable.
     # We check if we are already "processing" t. If so, we produce `ObjectTy`
     # instead of `ObjectDecl`.
     cached(c, t):
       if not c.recursionCheck.containsOrIncl(t.itemId):
-        result = objectToIr(c, t)
+        result = objectToIr(c, g, t)
       else:
-        result = objectHeaderToIr(c, t)
+        result = objectHeaderToIr(c, g, t)
   of tyTuple:
     cached(c, t):
-      result = tupleToIr(c, t)
+      result = tupleToIr(c, g, t)
   of tyProc:
     cached(c, t):
       if t.callConv == ccClosure:
-        result = closureToIr(c, t)
+        result = closureToIr(c, g, t)
       else:
-        result = procToIr(c, t)
+        result = procToIr(c, g, t)
   of tyVarargs, tyOpenArray:
     cached(c, t):
-      result = openArrayToIr(c, t)
+      result = openArrayToIr(c, g, t)
   of tyString:
-    cached(c, t):
-      result = stringToIr(c)
+    if c.stringType.int < 0:
+      result = stringToIr(c, g)
+      c.stringType = result
+    else:
+      result = c.stringType
   of tySequence:
     cached(c, t):
-      result = seqToIr(c, t)
+      result = seqToIr(c, g, t)
   of tyCstring:
     cached(c, t):
-      let a = openType(c.g, AArrayPtrTy)
-      c.g.addBuiltinType Char8Id
-      result = finishType(c.g, a)
+      let a = openType(g, AArrayPtrTy)
+      g.addBuiltinType Char8Id
+      result = finishType(g, a)
   of tyUncheckedArray:
     # We already handled the `ptr UncheckedArray` in a special way.
     cached(c, t):
-      let elemType = typeToIr(c, t.lastSon)
-      let a = openType(c.g, LastArrayTy)
-      c.g.addType(elemType)
-      result = finishType(c.g, a)
+      let elemType = typeToIr(c, g, t.lastSon)
+      let a = openType(g, LastArrayTy)
+      g.addType(elemType)
+      result = finishType(g, a)
   of tyUntyped, tyTyped:
     # this avoids a special case for system.echo which is not a generic but
     # uses `varargs[typed]`:
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index be931ed14..1c8a79fd8 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -65,7 +65,7 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(13)
-    SIG_DFL* = cast[CSighandlerT](0)
+    SIG_DFL* = CSighandlerT(nil)
 elif defined(haiku):
   const
     SIGABRT* = cint(6)
@@ -75,7 +75,7 @@ elif defined(haiku):
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(7)
-    SIG_DFL* = cast[CSighandlerT](0)
+    SIG_DFL* = CSighandlerT(nil)
 else:
   when defined(nimscript):
     {.error: "SIGABRT not ported to your platform".}