summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-10-11 17:44:14 +0200
committerGitHub <noreply@github.com>2023-10-11 17:44:14 +0200
commit816589b6674e3af281766f1279420758dcacedc4 (patch)
tree3e4f825e29e2068466a35edc69cc27967b743687
parentecaccafa6c18e0b92cd5f75d3363d61c0866dec9 (diff)
downloadNim-816589b6674e3af281766f1279420758dcacedc4.tar.gz
NIR: Nim intermediate representation (#22777)
Theoretical Benefits / Plans: 

- Typed assembler-like language.
- Allows for a CPS transformation.
- Can replace the existing C backend by a new C backend.
- Can replace the VM.
- Can do more effective "not nil" checking and static array bounds
checking.
- Can be used instead of the DFA.
- Easily translatable to LLVM.
- Reasonably easy to produce native code from.
- Tiny memory consumption. No pointers, no cry.

**In very early stages of development.**

Todo:
- [x] Map Nim types to IR types.
- [ ] Map Nim AST to IR instructions:
  - [x] Map bitsets to bitops.
  - [ ] Implement string cases.
  - [ ] Implement range and index checks.
  - [x] Implement `default(T)` builtin.
  - [x] Implement multi string concat.
- [ ] Write some analysis passes.
- [ ] Write a backend.
- [x] Integrate into the compilation pipeline.
-rw-r--r--compiler/ast.nim6
-rw-r--r--compiler/bitsets.nim5
-rw-r--r--compiler/ccgexprs.nim42
-rw-r--r--compiler/ccgreset.nim4
-rw-r--r--compiler/ccgstmts.nim3
-rw-r--r--compiler/ccgtypes.nim36
-rw-r--r--compiler/cgen.nim14
-rw-r--r--compiler/dfa.nim4
-rw-r--r--compiler/expanddefaults.nim131
-rw-r--r--compiler/ic/bitabs.nim2
-rw-r--r--compiler/magicsys.nim9
-rw-r--r--compiler/main.nim11
-rw-r--r--compiler/modulegraphs.nim2
-rw-r--r--compiler/nim.nim3
-rw-r--r--compiler/nir/ast2ir.nim2189
-rw-r--r--compiler/nir/cir.nim78
-rw-r--r--compiler/nir/nir.nim71
-rw-r--r--compiler/nir/nirinsts.nim418
-rw-r--r--compiler/nir/nirlineinfos.nim78
-rw-r--r--compiler/nir/nirslots.nim105
-rw-r--r--compiler/nir/nirtypes.nim347
-rw-r--r--compiler/nir/types2ir.nim466
-rw-r--r--compiler/pipelines.nim8
-rw-r--r--compiler/semobjconstr.nim16
-rw-r--r--compiler/semparallel.nim2
-rw-r--r--compiler/trees.nim4
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmgen.nim4
-rw-r--r--lib/std/formatfloat.nim31
-rw-r--r--lib/system.nim15
-rw-r--r--lib/system/ansi_c.nim2
-rw-r--r--lib/system/cgprocs.nim10
-rw-r--r--lib/system/memory.nim1
-rw-r--r--lib/system/strmantle.nim8
-rw-r--r--lib/system/strs_v2.nim4
35 files changed, 4009 insertions, 122 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index dd7561264..4fe72929f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -936,7 +936,7 @@ type
                               # it won't cause problems
                               # for skModule the string literal to output for
                               # deprecated modules.
-    instantiatedFrom*: PSym   # for instances, the generic symbol where it came from.                
+    instantiatedFrom*: PSym   # for instances, the generic symbol where it came from.
     when defined(nimsuggest):
       allUsages*: seq[TLineInfo]
 
@@ -2173,3 +2173,7 @@ const
     nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
     nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
     nkTypeOfExpr, nkMixinStmt, nkBindStmt}
+
+proc isTrue*(n: PNode): bool =
+  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
+    n.kind == nkIntLit and n.intVal != 0
diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim
index 756d93217..7d142b01d 100644
--- a/compiler/bitsets.nim
+++ b/compiler/bitsets.nim
@@ -90,3 +90,8 @@ proc bitSetCard*(x: TBitSet): BiggestInt =
   result = 0
   for it in x:
     result.inc int(populationCount[it])
+
+proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
+  result = 0
+  for j in 0..<size:
+    if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index b2c5a5ca8..44d04d763 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -112,11 +112,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) =
 proc genLiteral(p: BProc, n: PNode; result: var Rope) =
   genLiteral(p, n, n.typ, result)
 
-proc bitSetToWord(s: TBitSet, size: int): BiggestUInt =
-  result = 0
-  for j in 0..<size:
-    if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
-
 proc genRawSetData(cs: TBitSet, size: int; result: var Rope) =
   if size > 8:
     var res = "{\n"
@@ -1561,7 +1556,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
     if e[i].len == 3 and optFieldCheck in p.options:
       check = e[i][2]
     genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info)
-  
+
   if useTemp:
     if d.k == locNone:
       d = tmp
@@ -1868,8 +1863,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
           else:
             unaryExpr(p, e, d, "$1.Field1")
   of tyCstring:
-    if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
-    else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
+    if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)")
+    else: unaryExpr(p, e, d, "#nimCStrLen($1)")
   of tyString:
     var a: TLoc = initLocExpr(p, e[1])
     var x = lenExpr(p, a)
@@ -1889,13 +1884,6 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
   else: internalError(p.config, e.info, "genArrayLen()")
 
-proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
-  if n.kind == nkHiddenAddr:
-    result = n
-  else:
-    result = newTree(nkHiddenAddr, n)
-    result.typ = makePtrType(n.typ, idgen)
-
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   if optSeqDestructors in p.config.globalOptions:
     e[1] = makeAddr(e[1], p.module.idgen)
@@ -2521,8 +2509,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mOrd: genOrd(p, e, d)
   of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
     genArrayLen(p, e, d, op)
-  of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
-  of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
+  of mGCref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
+  of mGCunref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
   of mSetLengthStr: genSetLengthStr(p, e, d)
   of mSetLengthSeq: genSetLengthSeq(p, e, d)
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
@@ -3217,22 +3209,6 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
 
-proc caseObjDefaultBranch(obj: PNode; branch: Int128): int =
-  result = 0
-  for i in 1 ..< obj.len:
-    for j in 0 .. obj[i].len - 2:
-      if obj[i][j].kind == nkRange:
-        let x = getOrdValue(obj[i][j][0])
-        let y = getOrdValue(obj[i][j][1])
-        if branch >= x and branch <= y:
-          return i
-      elif getOrdValue(obj[i][j]) == branch:
-        return i
-    if obj[i].len == 1:
-      # else branch
-      return i
-  assert(false, "unreachable")
-
 proc isEmptyCaseObjectBranch(n: PNode): bool =
   for it in n:
     if it.kind == nkSym and not isEmptyType(it.sym.typ): return false
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
index bd0e2a58a..47b6a1e15 100644
--- a/compiler/ccgreset.nim
+++ b/compiler/ccgreset.nim
@@ -95,11 +95,11 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
       lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
     else:
       raiseAssert "unexpected set type kind"
-  of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
+  of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
       tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
       tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
       tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
-      tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
+      tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
     discard
 
 proc specializeReset(p: BProc, a: TLoc) =
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index c8f68c124..45a234332 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1457,7 +1457,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
         let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
         if optTinyRtti in p.config.globalOptions:
           let checkFor = $getObjDepth(t[i][j].typ)
-          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor,  $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
+          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
+              [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
         else:
           let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
           appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 2f92cb12b..a591ce93b 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -24,7 +24,7 @@ type
     dkResult #skResult
     dkConst #skConst
     dkOther #skType, skTemp, skLet and skForVar so far
-   
+
 proc descKindFromSymKind(kind: TSymKind): TypeDescKind =
   case kind
   of skParam: dkParam
@@ -33,7 +33,7 @@ proc descKindFromSymKind(kind: TSymKind): TypeDescKind =
   of skResult: dkResult
   of skConst: dkConst
   else: dkOther
- 
+
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
   # it's more efficient to test the whole Nim keywords range
@@ -464,7 +464,7 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr
     var num = 0
     while i < frmt.len:
       if frmt[i] == c:
-        inc(i)                  
+        inc(i)
         case frmt[i]
         of c:
           res.add(c)
@@ -521,7 +521,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
     types.add getTypeDescWeak(m, this.typ, check, dkParam)
 
   let firstParam = if isCtor: 1 else: 2
-  for i in firstParam..<t.n.len: 
+  for i in firstParam..<t.n.len:
     if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
     var param = t.n[i].sym
     var descKind = dkParam
@@ -649,7 +649,7 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
     result = rope(mangleField(m, field.name))
   if result == "": internalError(m.config, field.info, "mangleRecFieldName")
 
-proc hasCppCtor(m: BModule; typ: PType): bool = 
+proc hasCppCtor(m: BModule; typ: PType): bool =
   result = false
   if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType:
     for prc in m.g.graph.memberProcsPerType[typ.itemId]:
@@ -763,7 +763,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
     if isCtorGen and not isDefaultCtorGen:
       var ch: IntSet
       result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
-    
+
 proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
   # this fact here.
@@ -771,7 +771,7 @@ proc fillObjectFields*(m: BModule; typ: PType) =
   discard getRecordFields(m, typ, check)
 
 proc mangleDynLibProc(sym: PSym): Rope
-  
+
 proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope,
                    check: var IntSet, hasField:var bool): Rope =
   result = ""
@@ -816,7 +816,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope,
   else:
     structOrUnion = structOrUnion(typ)
   var baseType: string = ""
-  if typ[0] != nil: 
+  if typ[0] != nil:
     baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField)
   if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
     result = structOrUnion & " " & name
@@ -1128,8 +1128,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
     result = ""
   # fixes bug #145:
   excl(check, t.id)
-  
-  
+
+
 proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope =
   var check = initIntSet()
   result = getTypeDescAux(m, typ, check, kind)
@@ -1214,7 +1214,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
   var name, params, rettype, superCall: string = ""
   var isFnConst, isOverride, isMemberVirtual: bool = false
   parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor)
-  genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) 
+  genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false)
   let isVirtual = sfVirtual in prc.flags or isMemberVirtual
   var fnConst, override: string = ""
   if isCtor:
@@ -1224,7 +1224,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
   if isFwdDecl:
     if isVirtual:
       rettype = "virtual " & rettype
-      if isOverride: 
+      if isOverride:
         override = " override"
     superCall = ""
   else:
@@ -1232,14 +1232,14 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
       prc.loc.r = "$1$2(@)" % [memberOp, name]
     elif superCall != "":
       superCall = " : " & superCall
-    
+
     name = "$1::$2" % [typDesc, name]
 
   result.add "N_LIB_PRIVATE "
   result.addf("$1$2($3, $4)$5$6$7$8",
         [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
         params, fnConst, override, superCall])
-  
+
 proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) =
   # using static is needed for inline procs
   var check = initIntSet()
@@ -1575,10 +1575,6 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
 
 proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
 
-proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
-  result = newType(tyPtr, nextTypeId idgen, baseType.owner)
-  addSonSkipIntLit(result, baseType, idgen)
-
 proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
               info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym =
   # the wrapper is roughly like:
@@ -1946,14 +1942,14 @@ proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rop
   else:
     result = genTypeInfoV1(m, t, info)
 
-proc genTypeSection(m: BModule, n: PNode) = 
+proc genTypeSection(m: BModule, n: PNode) =
   var intSet = initIntSet()
   for i in 0..<n.len:
     if len(n[i]) == 0: continue
     if n[i][0].kind != nkPragmaExpr: continue
     for p in 0..<n[i][0].len:
       if (n[i][0][p].kind != nkSym): continue
-      if sfExportc in n[i][0][p].sym.flags:        
+      if sfExportc in n[i][0][p].sym.flags:
         discard getTypeDescAux(m, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind))
         if m.g.generatedHeader != nil:
           discard getTypeDescAux(m.g.generatedHeader, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind))
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index e9045b066..adee8f845 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -17,6 +17,8 @@ import
   lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
   injectdestructors, astmsgs, modulepaths, backendpragmas
 
+from expanddefaults import caseObjDefaultBranch
+
 import pipelineutils
 
 when defined(nimPreviewSlimSystem):
@@ -499,8 +501,8 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
       let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))
-      if p.module.compileToCpp and isOrHasImportedCppType(typ): 
-        if lfIndirect in loc.flags: 
+      if p.module.compileToCpp and isOrHasImportedCppType(typ):
+        if lfIndirect in loc.flags:
           #C++ cant be just zeroed. We need to call the ctors
           var tmp = getTemp(p, loc.t)
           linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
@@ -508,7 +510,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       else:
         linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
                 [addrLoc(p.config, loc), tyDesc])
-      
+
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
@@ -551,7 +553,7 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
   result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
                 storage: OnStack, flags: {})
   if p.module.compileToCpp and isOrHasImportedCppType(t):
-    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, 
+    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r,
       genCppInitializer(p.module, p, t)])
   else:
     linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
@@ -607,8 +609,8 @@ proc assignLocalVar(p: BProc, n: PNode) =
   # this need not be fulfilled for inline procs; they are regenerated
   # for each module that uses them!
   let nl = if optLineDir in p.config.options: "" else: "\n"
-  var decl = localVarDecl(p, n) 
-  if p.module.compileToCpp and isOrHasImportedCppType(n.typ): 
+  var decl = localVarDecl(p, n)
+  if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
     decl.add genCppInitializer(p.module, p, n.typ)
   decl.add ";" & nl
   line(p, cpsLocals, decl)
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 1459bde45..4cae9ec42 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -129,10 +129,6 @@ template withBlock(labl: PSym; body: untyped) =
   body
   popBlock(c, oldLen)
 
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
 template forkT(body) =
   let lab1 = c.forkI()
   body
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
new file mode 100644
index 000000000..988433278
--- /dev/null
+++ b/compiler/expanddefaults.nim
@@ -0,0 +1,131 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import lineinfos, ast, types
+
+proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
+  result = 0
+  for i in 1 ..< obj.len:
+    for j in 0 .. obj[i].len - 2:
+      if obj[i][j].kind == nkRange:
+        let x = getOrdValue(obj[i][j][0])
+        let y = getOrdValue(obj[i][j][1])
+        if branch >= x and branch <= y:
+          return i
+      elif getOrdValue(obj[i][j]) == branch:
+        return i
+    if obj[i].len == 1:
+      # else branch
+      return i
+  return 1
+
+template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
+
+proc expandDefault*(t: PType; info: TLineInfo): PNode
+
+proc expandField(s: PSym; info: TLineInfo): PNode =
+  result = newNodeIT(nkExprColonExpr, info, s.typ)
+  result.add newSymNode(s)
+  result.add expandDefault(s.typ, info)
+
+proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      expandDefaultN(n[i], info, res)
+  of nkRecCase:
+    res.add expandField(n[0].sym, info)
+    var branch = Zero
+    let constOrNil = n[0].sym.astdef
+    if constOrNil != nil:
+      branch = getOrdValue(constOrNil)
+
+    let selectedBranch = caseObjDefaultBranch(n, branch)
+    let b = lastSon(n[selectedBranch])
+    expandDefaultN b, info, res
+  of nkSym:
+    res.add expandField(n.sym, info)
+  else:
+    discard
+
+proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
+  if t[0] != nil:
+    expandDefaultObj(t[0], info, res)
+  expandDefaultN(t.n, info, res)
+
+proc expandDefault(t: PType; info: TLineInfo): PNode =
+  case t.kind
+  of tyInt:     result = newZero(t, info, nkIntLit)
+  of tyInt8:    result = newZero(t, info, nkInt8Lit)
+  of tyInt16:   result = newZero(t, info, nkInt16Lit)
+  of tyInt32:   result = newZero(t, info, nkInt32Lit)
+  of tyInt64:   result = newZero(t, info, nkInt64Lit)
+  of tyUInt:    result = newZero(t, info, nkUIntLit)
+  of tyUInt8:   result = newZero(t, info, nkUInt8Lit)
+  of tyUInt16:  result = newZero(t, info, nkUInt16Lit)
+  of tyUInt32:  result = newZero(t, info, nkUInt32Lit)
+  of tyUInt64:  result = newZero(t, info, nkUInt64Lit)
+  of tyFloat:   result = newZero(t, info, nkFloatLit)
+  of tyFloat32: result = newZero(t, info, nkFloat32Lit)
+  of tyFloat64: result = newZero(t, info, nkFloat64Lit)
+  of tyFloat128: result = newZero(t, info, nkFloat64Lit)
+  of tyChar:    result = newZero(t, info, nkCharLit)
+  of tyBool:    result = newZero(t, info, nkIntLit)
+  of tyEnum:
+    # Could use low(T) here to finally fix old language quirks
+    result = newZero(t, info, nkIntLit)
+  of tyRange:
+    # Could use low(T) here to finally fix old language quirks
+    result = expandDefault(t[0], info)
+  of tyVoid: result = newZero(t, info, nkEmpty)
+  of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
+    result = expandDefault(t.lastSon, info)
+  of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
+    if t.len > 0:
+      result = expandDefault(t.lastSon, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyFromExpr:
+    if t.n != nil and t.n.typ != nil:
+      result = expandDefault(t.n.typ, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyArray:
+    result = newZero(t, info, nkBracket)
+    let n = toInt64(lengthOrd(nil, t))
+    for i in 0..<n:
+      result.add expandDefault(t[1], info)
+  of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
+    result = newZero(t, info, nkNilLit)
+  of tyVar, tyLent:
+    let e = t.lastSon
+    if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
+      # skip the modifier, `var openArray` is a (ptr, len) pair too:
+      result = expandDefault(e, info)
+    else:
+      result = newZero(t.lastSon, info, nkNilLit)
+  of tySet:
+    result = newZero(t, info, nkCurly)
+  of tyObject:
+    result = newNodeIT(nkObjConstr, info, t)
+    result.add newNodeIT(nkType, info, t)
+    expandDefaultObj(t, info, result)
+  of tyTuple:
+    result = newZero(t, info, nkTupleConstr)
+    for it in t:
+      result.add expandDefault(it, info)
+  of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
+    result = newZero(t, info, nkBracket)
+  of tyString:
+    result = newZero(t, info, nkStrLit)
+  of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
+     tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
+     tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
+     tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
+    result = newZero(t, info, nkEmpty) # bug indicator
diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim
index a55131051..edbaeb240 100644
--- a/compiler/ic/bitabs.nim
+++ b/compiler/ic/bitabs.nim
@@ -13,6 +13,8 @@ type
     vals: seq[T] # indexed by LitId
     keys: seq[LitId]  # indexed by hash(val)
 
+proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
+
 proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = (h + 1) and maxHash
 
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index bb8c6a5b9..63fe0369c 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -150,4 +150,13 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
     globalError(g.config, info,
       "can't find magic equals operator for type kind " & $t.kind)
 
+proc makePtrType*(baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, nextTypeId idgen, baseType.owner)
+  addSonSkipIntLit(result, baseType, idgen)
 
+proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
+  if n.kind == nkHiddenAddr:
+    result = n
+  else:
+    result = newTree(nkHiddenAddr, n)
+    result.typ = makePtrType(n.typ, idgen)
diff --git a/compiler/main.nim b/compiler/main.nim
index 7118a253c..e5a5be56c 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -22,6 +22,8 @@ import
   modules,
   modulegraphs, lineinfos, pathutils, vmprofiler
 
+# ensure NIR compiles:
+import nir / nir
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
@@ -173,13 +175,14 @@ proc commandCompileToJS(graph: ModuleGraph) =
     if optGenScript in conf.globalOptions:
       writeDepsFile(graph)
 
-proc commandInteractive(graph: ModuleGraph) =
+proc commandInteractive(graph: ModuleGraph; useNir: bool) =
   graph.config.setErrorMaxHighMaybe
   initDefines(graph.config.symbols)
-  defineSymbol(graph.config.symbols, "nimscript")
+  if not useNir:
+    defineSymbol(graph.config.symbols, "nimscript")
   # note: seems redundant with -d:nimHasLibFFI
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  setPipeLinePass(graph, InterpreterPass)
+  setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass)
   compilePipelineSystemModule(graph)
   if graph.config.commandArgs.len > 0:
     discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
@@ -407,7 +410,7 @@ proc mainCommand*(graph: ModuleGraph) =
     wantMainModule(conf)
     commandView(graph)
     #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
-  of cmdInteractive: commandInteractive(graph)
+  of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir"))
   of cmdNimscript:
     if conf.projectIsCmd or conf.projectIsStdin: discard
     elif not fileExists(conf.projectFull):
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 4dadb45b6..f6abb0a60 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -65,6 +65,7 @@ type
     CgenPass
     EvalPass
     InterpreterPass
+    NirReplPass
     GenDependPass
     Docgen2TexPass
     Docgen2JsonPass
@@ -99,6 +100,7 @@ type
     cache*: IdentCache
     vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
                  # be clarified in later compiler implementations.
+    repl*: RootRef # REPL state is shared project-wise.
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
diff --git a/compiler/nim.nim b/compiler/nim.nim
index d0aa888c4..023a76ff9 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -116,7 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     conf.backend = backendC
 
   if conf.selectedGC == gcUnselected:
-    if conf.backend in {backendC, backendCpp, backendObjc}:
+    if conf.backend in {backendC, backendCpp, backendObjc} or
+        (conf.cmd == cmdInteractive and isDefined(conf, "nir")):
       initOrcDefines(conf)
 
   mainCommand(graph)
diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim
new file mode 100644
index 000000000..f06bb3ae8
--- /dev/null
+++ b/compiler/nir/ast2ir.nim
@@ -0,0 +1,2189 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import std / [assertions, tables, sets]
+import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys,
+  modulegraphs, guards, renderer, transf, bitsets, trees, nimsets,
+  expanddefaults]
+from ".." / lowerings import lowerSwap, lowerTupleUnpacking
+from ".." / pathutils import customPath
+import .. / ic / bitabs
+
+import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir
+
+type
+  ModuleCon* = ref object
+    strings*: BiTable[string]
+    integers*: BiTable[int64]
+    man*: LineInfoManager
+    types*: TypesCon
+    slotGenerator: ref int
+    module*: PSym
+    graph*: ModuleGraph
+    nativeIntId, nativeUIntId: TypeId
+    idgen: IdGenerator
+    pendingProcs: Table[ItemId, PSym] # procs we still need to generate code for
+
+  ProcCon* = object
+    config: ConfigRef
+    lastFileKey: FileIndex
+    lastFileVal: LitId
+    labelGen: int
+    exitLabel: LabelId
+    code*: Tree
+    blocks: seq[(PSym, LabelId)]
+    sm: SlotManager
+    locGen: int
+    m: ModuleCon
+    prc: PSym
+
+proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym): ModuleCon =
+  result = ModuleCon(graph: graph, types: initTypesCon(config), slotGenerator: new(int),
+    idgen: idgen, module: module)
+  case config.target.intSize
+  of 2:
+    result.nativeIntId = Int16Id
+    result.nativeUIntId = UInt16Id
+  of 4:
+    result.nativeIntId = Int32Id
+    result.nativeUIntId = UInt16Id
+  else:
+    result.nativeIntId = Int64Id
+    result.nativeUIntId = UInt16Id
+
+proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
+  ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config)
+
+proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo =
+  var val: LitId
+  if c.lastFileKey == i.fileIndex:
+    val = c.lastFileVal
+  else:
+    val = c.m.strings.getOrIncl(toFullPath(c.config, i.fileIndex))
+    # remember the entry:
+    c.lastFileKey = i.fileIndex
+    c.lastFileVal = val
+  result = pack(c.m.man, val, int32 i.line, int32 i.col)
+
+proc bestEffort(c: ProcCon): TLineInfo =
+  if c.prc != nil:
+    c.prc.info
+  else:
+    c.m.module.info
+
+proc popBlock(c: var ProcCon; oldLen: int) =
+  c.blocks.setLen(oldLen)
+
+template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} =
+  var oldLen {.gensym.} = c.blocks.len
+  c.blocks.add (labl, asmLabl)
+  body
+  popBlock(c, oldLen)
+
+type
+  GenFlag = enum
+    gfAddrOf # load the address of the expression
+    gfToOutParam # the expression is passed to an `out` parameter
+  GenFlags = set[GenFlag]
+
+proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {})
+
+proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
+  openScope c.sm
+  gen c, n, d, flags
+  closeScope c.sm
+
+proc freeTemp(c: var ProcCon; tmp: Value) =
+  let s = extractTemp(tmp)
+  if s != SymId(-1):
+    freeTemp(c.sm, s)
+
+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.code.addSummon info, tmp, t
+  result = localToValue(info, tmp)
+
+template withTemp(tmp, n, body: untyped) {.dirty.} =
+  var tmp = getTemp(c, n)
+  body
+  c.freeTemp(tmp)
+
+proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
+  var tmp = default(Value)
+  gen(c, n, tmp, flags)
+  freeTemp c, tmp
+
+proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
+  openScope c.sm
+  gen c, n, flags
+  closeScope c.sm
+
+proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value =
+  result = default(Value)
+  gen(c, n, result, flags)
+  assert Tree(result).len > 0, $n
+
+proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} =
+  when false:
+    if n.typ.isNil or n.typ.kind == tyVoid:
+      let s = extractTemp(d)
+      if s != SymId(-1):
+        freeLoc(c.sm, s)
+
+proc isNotOpr(n: PNode): bool =
+  n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot
+
+proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) =
+  c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab
+
+type
+  JmpKind = enum opcFJmp, opcTJmp
+
+proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId =
+  result = newLabel(c.labelGen)
+  let info = toLineInfo(c, n.info)
+  buildTyped c.code, info, Select, Bool8Id:
+    c.code.copyTree Tree(v)
+    build c.code, info, SelectPair:
+      build c.code, info, SelectValue:
+        c.code.boolVal(info, jk == opcTJmp)
+      c.code.gotoLabel info, Goto, result
+
+proc patch(c: var ProcCon; n: PNode; L: LabelId) =
+  addLabel c.code, toLineInfo(c, n.info), Label, L
+
+proc genWhile(c: var ProcCon; n: PNode) =
+  # lab1:
+  #   cond, tmp
+  #   fjmp tmp, lab2
+  #   body
+  #   jmp lab1
+  # lab2:
+  let info = toLineInfo(c, n.info)
+  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
+  withBlock(nil, info, lab1):
+    if isTrue(n[0]):
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+    elif isNotOpr(n[0]):
+      var tmp = c.genx(n[0][1])
+      let lab2 = c.xjmp(n, opcTJmp, tmp)
+      c.freeTemp(tmp)
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+      c.patch(n, lab2)
+    else:
+      var tmp = c.genx(n[0])
+      let lab2 = c.xjmp(n, opcFJmp, tmp)
+      c.freeTemp(tmp)
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+      c.patch(n, lab2)
+
+proc genBlock(c: var ProcCon; n: PNode; d: var Value) =
+  openScope c.sm
+  let info = toLineInfo(c, n.info)
+  let lab1 = newLabel(c.labelGen)
+
+  withBlock(n[0].sym, info, lab1):
+    c.gen(n[1], d)
+
+  c.code.addLabel(info, Label, lab1)
+  closeScope c.sm
+  c.clearDest(n, d)
+
+proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) =
+  c.code.addLabel(toLineInfo(c, n.info), Goto, L)
+
+proc genBreak(c: var ProcCon; n: PNode) =
+  if n[0].kind == nkSym:
+    for i in countdown(c.blocks.len-1, 0):
+      if c.blocks[i][0] == n[0].sym:
+        c.jumpTo n, c.blocks[i][1]
+        return
+    localError(c.config, n.info, "NIR problem: cannot find 'break' target")
+  else:
+    c.jumpTo n, c.blocks[c.blocks.high][1]
+
+proc genIf(c: var ProcCon; n: PNode; d: var Value) =
+  #  if (!expr1) goto lab1;
+  #    thenPart
+  #    goto LEnd
+  #  lab1:
+  #  if (!expr2) goto lab2;
+  #    thenPart2
+  #    goto LEnd
+  #  lab2:
+  #    elsePart
+  #  Lend:
+  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
+  var ending = newLabel(c.labelGen)
+  for i in 0..<n.len:
+    var it = n[i]
+    if it.len == 2:
+      let info = toLineInfo(c, it[0].info)
+      withTemp(tmp, it[0]):
+        var elsePos: LabelId
+        if isNotOpr(it[0]):
+          c.gen(it[0][1], tmp)
+          elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
+        else:
+          c.gen(it[0], tmp)
+          elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
+      c.clearDest(n, d)
+      if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d`
+        c.genScope(it[1])
+      else:
+        c.genScope(it[1], d) # then part
+      if i < n.len-1:
+        c.jumpTo it[1], ending
+      c.patch(it, elsePos)
+    else:
+      c.clearDest(n, d)
+      if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d`
+        c.genScope(it[0])
+      else:
+        c.genScope(it[0], d)
+  c.patch(n, ending)
+  c.clearDest(n, d)
+
+proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) =
+  if isEmpty(d):
+    d = tmp
+  else:
+    let info = toLineInfo(c, n.info)
+    build c.code, info, Asgn:
+      c.code.addTyped info, typeToIr(c.m.types, n.typ)
+      c.code.copyTree d
+      c.code.copyTree tmp
+    freeTemp(c, tmp)
+
+proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) =
+  #   asgn d, a
+  #   tjmp|fjmp lab1
+  #   asgn d, b
+  # lab1:
+  var tmp = getTemp(c, n)
+  c.gen(n[1], tmp)
+  let lab1 = c.xjmp(n, opc, tmp)
+  c.gen(n[2], tmp)
+  c.patch(n, lab1)
+  tempToDest c, n, d, tmp
+
+proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} =
+  if hasValue(x):
+    #debug(n)
+    localError(c.config, n.info, "not unused")
+
+proc caseValue(c: var ProcCon; n: PNode) =
+  let info = toLineInfo(c, n.info)
+  build c.code, info, SelectValue:
+    let x = genx(c, n)
+    c.code.copyTree x
+    freeTemp(c, x)
+
+proc caseRange(c: var ProcCon; n: PNode) =
+  let info = toLineInfo(c, n.info)
+  build c.code, info, SelectRange:
+    let x = genx(c, n[0])
+    let y = genx(c, n[1])
+    c.code.copyTree x
+    c.code.copyTree y
+    freeTemp(c, y)
+    freeTemp(c, x)
+
+proc genCase(c: var ProcCon; n: PNode; d: var Value) =
+  if not isEmptyType(n.typ):
+    if isEmpty(d): d = getTemp(c, n)
+  else:
+    unused(c, n, d)
+  var sections = newSeqOfCap[LabelId](n.len-1)
+  let ending = newLabel(c.labelGen)
+  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.gen(n[0], tmp)
+      for i in 1..<n.len:
+        let section = newLabel(c.labelGen)
+        sections.add section
+        let it = n[i]
+        let itinfo = toLineInfo(c, it.info)
+        build c.code, itinfo, SelectPair:
+          build c.code, itinfo, SelectList:
+            for j in 0..<it.len-1:
+              if it[j].kind == nkRange:
+                caseRange c, it[j]
+              else:
+                caseValue c, it[j]
+          c.code.addLabel itinfo, Goto, section
+  for i in 1..<n.len:
+    let it = n[i]
+    let itinfo = toLineInfo(c, it.info)
+    c.code.addLabel itinfo, Label, sections[i-1]
+    c.gen it.lastSon
+    if i != n.len-1:
+      c.code.addLabel itinfo, Goto, ending
+  c.code.addLabel info, Label, ending
+
+proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) =
+  build c.code, info, opc:
+    c.code.addTyped info, t
+    if opc in {CheckedCall, CheckedIndirectCall}:
+      c.code.addLabel info, CheckedGoto, c.exitLabel
+    for a in mitems(args):
+      c.code.copyTree a
+      freeTemp c, a
+
+proc canRaiseDisp(c: ProcCon; n: PNode): bool =
+  # we assume things like sysFatal cannot raise themselves
+  if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
+    result = false
+  elif optPanics in c.config.globalOptions or
+      (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
+       sfSystemRaisesDefect notin n.sym.flags):
+    # we know we can be strict:
+    result = canRaise(n)
+  else:
+    # we have to be *very* conservative:
+    result = canRaiseConservative(n)
+
+proc genCall(c: var ProcCon; n: PNode; d: var Value) =
+  let canRaise = canRaiseDisp(c, n[0])
+
+  let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds:
+              (if canRaise: CheckedCall else: Call)
+            else:
+              (if canRaise: CheckedIndirectCall else: IndirectCall)
+  let info = toLineInfo(c, n.info)
+
+  # In the IR we cannot nest calls. Thus we use two passes:
+  var args: seq[Value] = @[]
+  var t = n[0].typ
+  if t != nil: t = t.skipTypes(abstractInst)
+  args.add genx(c, n[0])
+  for i in 1..<n.len:
+    if t != nil and i < t.len:
+      if isCompileTimeOnly(t[i]): discard
+      elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam})
+      else: args.add genx(c, n[i])
+    else:
+      args.add genx(c, n[i])
+
+  let tb = typeToIr(c.m.types, n.typ)
+  if not isEmptyType(n.typ):
+    if isEmpty(d): d = getTemp(c, n)
+    # XXX Handle problematic aliasing here: `a = f_canRaise(a)`.
+    build c.code, info, Asgn:
+      c.code.addTyped info, tb
+      c.code.copyTree d
+      rawCall c, info, opc, tb, args
+  else:
+    rawCall c, info, opc, tb, args
+
+proc genRaise(c: var ProcCon; n: PNode) =
+  let info = toLineInfo(c, n.info)
+  let tb = typeToIr(c.m.types, n[0].typ)
+
+  let d = genx(c, n[0])
+  build c.code, info, SetExc:
+    c.code.addTyped info, tb
+    c.code.copyTree d
+  c.freeTemp(d)
+  c.code.addLabel info, Goto, c.exitLabel
+
+proc genReturn(c: var ProcCon; n: PNode) =
+  if n[0].kind != nkEmpty:
+    gen(c, n[0])
+  # XXX Block leave actions?
+  let info = toLineInfo(c, n.info)
+  c.code.addLabel info, Goto, c.exitLabel
+
+proc genTry(c: var ProcCon; n: PNode; d: var Value) =
+  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
+  var endings: seq[LabelId] = @[]
+  let ehPos = newLabel(c.labelGen)
+  let oldExitLab = c.exitLabel
+  c.exitLabel = ehPos
+  if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d`
+    c.gen(n[0])
+  else:
+    c.gen(n[0], d)
+  c.clearDest(n, d)
+
+  # Add a jump past the exception handling code
+  let jumpToFinally = newLabel(c.labelGen)
+  c.jumpTo n, jumpToFinally
+  # This signals where the body ends and where the exception handling begins
+  c.patch(n, ehPos)
+  c.exitLabel = oldExitLab
+  for i in 1..<n.len:
+    let it = n[i]
+    if it.kind != nkFinally:
+      # first opcExcept contains the end label of the 'except' block:
+      let endExcept = newLabel(c.labelGen)
+      for j in 0..<it.len - 1:
+        assert(it[j].kind == nkType)
+        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)
+      if it.len == 1:
+        let itinfo = toLineInfo(c, it.info)
+        build c.code, itinfo, TestExc:
+          c.code.addTyped itinfo, VoidId
+      let body = it.lastSon
+      if isEmptyType(body.typ): # maybe noreturn call, don't touch `d`
+        c.gen(body)
+      else:
+        c.gen(body, d)
+      c.clearDest(n, d)
+      if i < n.len:
+        endings.add newLabel(c.labelGen)
+      c.patch(it, endExcept)
+  let fin = lastSon(n)
+  # we always generate an 'opcFinally' as that pops the safepoint
+  # from the stack if no exception is raised in the body.
+  c.patch(fin, jumpToFinally)
+  #c.gABx(fin, opcFinally, 0, 0)
+  for endPos in endings: c.patch(n, endPos)
+  if fin.kind == nkFinally:
+    c.gen(fin[0])
+    c.clearDest(n, d)
+  #c.gABx(fin, opcFinallyEnd, 0, 0)
+
+template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
+proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym)
+
+proc genField(c: var ProcCon; n: PNode; d: var Value) =
+  var pos: int
+  if n.kind != nkSym or n.sym.kind != skField:
+    localError(c.config, n.info, "no field symbol")
+    pos = 0
+  else:
+    pos = n.sym.position
+  d.addImmediateVal toLineInfo(c, n.info), pos
+
+proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
+  if arr.skipTypes(abstractInst).kind == tyArray and
+      (let x = firstOrd(c.config, arr); x != Zero):
+    let info = toLineInfo(c, n.info)
+    buildTyped d, info, Sub, c.m.nativeIntId:
+      c.gen(n, d)
+      d.addImmediateVal toLineInfo(c, n.info), toInt(x)
+  else:
+    c.gen(n, d)
+
+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)
+  assert refType.kind == tyRef
+  let baseType = refType.lastSon
+
+  let info = toLineInfo(c, n.info)
+  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)
+  buildTyped c.code, info, Asgn, refTypeIr:
+    copyTree c.code, x
+    buildTyped c.code, info, Cast, refTypeIr:
+      buildTyped c.code, info, Call, VoidPtrId:
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        c.code.addImmediateVal info, int(getSize(c.config, baseType))
+        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
+
+proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
+  let info = toLineInfo(c, n.info)
+  let seqtype = skipTypes(n.typ, abstractVarRange)
+  let baseType = seqtype.lastSon
+  var a = c.genx(n[1])
+  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:
+      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)
+  buildTyped c.code, info, Asgn, payloadPtr:
+    # $1.p
+    buildTyped c.code, info, FieldAt, payloadPtr:
+      copyTree c.code, d
+      c.code.addImmediateVal info, 1
+    # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
+    buildTyped c.code, info, Cast, payloadPtr:
+      buildTyped c.code, info, Call, VoidPtrId:
+        let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit")
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        copyTree c.code, a
+        c.code.addImmediateVal info, int(getSize(c.config, baseType))
+        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
+  freeTemp c, a
+
+proc genNewSeq(c: var ProcCon; n: PNode) =
+  let info = toLineInfo(c, n.info)
+  let seqtype = skipTypes(n[1].typ, abstractVarRange)
+  let baseType = seqtype.lastSon
+  var d = c.genx(n[1])
+  var b = c.genx(n[2])
+
+  # $1.len = $2
+  buildTyped c.code, info, Asgn, c.m.nativeIntId:
+    buildTyped c.code, info, FieldAt, c.m.nativeIntId:
+      copyTree c.code, d
+      c.code.addImmediateVal info, 0
+    copyTree c.code, b
+    c.code.addImmediateVal info, 0
+
+  # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
+  let payloadPtr = seqPayloadPtrType(c.m.types, seqtype)
+  buildTyped c.code, info, Asgn, payloadPtr:
+    # $1.p
+    buildTyped c.code, info, FieldAt, payloadPtr:
+      copyTree c.code, d
+      c.code.addImmediateVal info, 1
+    # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
+    buildTyped c.code, info, Cast, payloadPtr:
+      buildTyped c.code, info, Call, VoidPtrId:
+        let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload")
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        copyTree c.code, b
+        c.code.addImmediateVal info, int(getSize(c.config, baseType))
+        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
+  freeTemp c, b
+  freeTemp c, d
+
+template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) =
+  if typ == VoidId:
+    body(c.code)
+  elif isEmpty(d):
+    body(Tree(d))
+  else:
+    buildTyped c.code, info, Asgn, typ:
+      copyTree c.code, d
+      body(c.code)
+
+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)
+  template body(target) =
+    buildTyped target, info, opc, t:
+      copyTree target, tmp
+      copyTree target, tmp2
+  intoDest d, info, t, body
+  c.freeTemp(tmp)
+  c.freeTemp(tmp2)
+
+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)
+  template body(target) =
+    buildTyped target, info, opc, t:
+      copyTree target, tmp
+      copyTree target, tmp2
+  intoDest d, info, Bool8Id, body
+  c.freeTemp(tmp)
+  c.freeTemp(tmp2)
+
+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)
+  template body(target) =
+    buildTyped target, info, opc, t:
+      copyTree target, tmp
+  intoDest d, info, t, body
+  c.freeTemp(tmp)
+
+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 d = c.genx(n[1])
+  let tmp = c.genx(n[2])
+  # we produce code like:  i = i + 1
+  buildTyped c.code, info, Asgn, t:
+    copyTree c.code, d
+    buildTyped c.code, info, opc, t:
+      copyTree c.code, d
+      copyTree c.code, tmp
+  c.freeTemp(tmp)
+  #c.genNarrow(n[1], d)
+  c.freeTemp(d)
+
+proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
+  #echo c.m.graph.config $ n.info, " ", n
+  let info = toLineInfo(c, n.info)
+  var a = n[1]
+  #if a.kind == nkHiddenAddr: a = a[0]
+  var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
+  case typ.kind
+  of tyOpenArray, tyVarargs:
+    let xa = c.genx(a)
+    template body(target) =
+      buildTyped target, info, FieldAt, c.m.nativeIntId:
+        copyTree target, xa
+        target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1
+    intoDest d, info, c.m.nativeIntId, body
+
+  of tyCstring:
+    let xa = c.genx(a)
+    if isEmpty(d): d = getTemp(c, n)
+    buildTyped c.code, info, Call, c.m.nativeIntId:
+      let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen")
+      assert codegenProc != nil
+      let theProc = c.genx newSymNode(codegenProc, n.info)
+      copyTree c.code, theProc
+      copyTree c.code, xa
+
+  of tyString, tySequence:
+    let xa = c.genx(a)
+
+    if typ.kind == tySequence:
+      # we go through a temporary here because people write bullshit code.
+      if isEmpty(d): d = getTemp(c, n)
+
+    template body(target) =
+      buildTyped target, info, FieldAt, c.m.nativeIntId:
+        copyTree target, xa
+        target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
+    intoDest d, info, c.m.nativeIntId, body
+
+  of tyArray:
+    template body(target) =
+      target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
+    intoDest d, info, c.m.nativeIntId, body
+  else: internalError(c.config, n.info, "genArrayLen()")
+
+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)
+  template body(target) =
+    buildTyped target, info, Sub, t:
+      # Little hack: This works because we know that `0.0` is all 0 bits:
+      target.addIntVal(c.m.integers, info, t, 0)
+      copyTree target, tmp
+  intoDest d, info, t, body
+  c.freeTemp(tmp)
+
+proc genHigh(c: var ProcCon; n: PNode; d: var Value) =
+  let subOpr = createMagic(c.m.graph, c.m.idgen, "-", mSubI)
+  let lenOpr = createMagic(c.m.graph, c.m.idgen, "len", mLengthOpenArray)
+  let asLenExpr = subOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(1))
+  c.gen asLenExpr, d
+
+proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) =
+  let info = toLineInfo(c, n.info)
+  let xa = c.genx(n[1])
+  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)
+  template body(target) =
+    buildTyped target, info, Call, t:
+      let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
+      #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
+      let theProc = c.genx newSymNode(codegenProc, n.info)
+      copyTree target, theProc
+      copyTree target, xa
+      copyTree target, xb
+
+  intoDest d, info, t, body
+  c.freeTemp xb
+  c.freeTemp xa
+
+proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) =
+  let info = toLineInfo(c, n.info)
+  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)
+  template body(target) =
+    buildTyped target, info, Call, t:
+      let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
+      let theProc = c.genx newSymNode(codegenProc, n.info)
+      copyTree target, theProc
+      copyTree target, xa
+
+  intoDest d, info, t, body
+  c.freeTemp xa
+
+proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) =
+  let t = n[1].typ.skipTypes(abstractInst+{tyRange})
+  let toStrProc = getToStringProc(c.m.graph, t)
+  # XXX need to modify this logic for IC.
+  var nb = copyTree(n)
+  nb[0] = newSymNode(toStrProc)
+  gen(c, nb, d)
+
+proc genOf(c: var ProcCon; n: PNode; d: var Value) =
+  genUnaryOp c, n, d, TestOf
+
+template sizeOfLikeMsg(name): string =
+  "'" & name & "' requires '.importc' types to be '.completeStruct'"
+
+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)
+  template body(target) =
+    buildTyped target, info, Eq, t:
+      copyTree target, tmp
+      addNilVal target, info, t
+  intoDest d, info, Bool8Id, body
+  c.freeTemp(tmp)
+
+proc fewCmps(conf: ConfigRef; s: PNode): bool =
+  # this function estimates whether it is better to emit code
+  # for constructing the set or generating a bunch of comparisons directly
+  if s.kind != nkCurly:
+    result = false
+  elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
+    result = false            # it is better to emit the set generation code
+  elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
+    result = true             # better not emit the set if int is basetype!
+  else:
+    result = s.len <= 8  # 8 seems to be a good value
+
+proc genInBitset(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 = bitsetBasetype(c.m.types, n[1].typ)
+  let setType = typeToIr(c.m.types, n[1].typ)
+  let mask =
+    case t
+    of UInt8Id: 7
+    of UInt16Id: 15
+    of UInt32Id: 31
+    else: 63
+  let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId
+    # "(($1              &(1U<<((NU)($2)&7U)))!=0)"  - or -
+    # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)"
+
+  template body(target) =
+    buildTyped target, info, BoolNot, Bool8Id:
+      buildTyped target, info, Eq, t:
+        buildTyped target, info, BitAnd, t:
+          if c.m.types.g[setType].kind != ArrayTy:
+            copyTree target, a
+          else:
+            buildTyped target, info, ArrayAt, t:
+              copyTree target, a
+              buildTyped target, info, BitShr, t:
+                buildTyped target, info, Cast, expansion:
+                  copyTree target, b
+                addIntVal target, c.m.integers, info, expansion, 3
+
+          buildTyped target, info, BitShl, t:
+            addIntVal target, c.m.integers, info, t, 1
+            buildTyped target, info, BitAnd, t:
+              buildTyped target, info, Cast, expansion:
+                copyTree target, b
+              addIntVal target, c.m.integers, info, expansion, mask
+        addIntVal target, c.m.integers, info, t, 0
+  intoDest d, info, t, body
+
+  c.freeTemp(b)
+  c.freeTemp(a)
+
+proc genInSet(c: var ProcCon; n: PNode; d: var Value) =
+  let g {.cursor.} = c.m.graph
+  if n[1].kind == nkCurly and fewCmps(g.config, n[1]):
+    # a set constructor but not a constant set:
+    # do not emit the set, but generate a bunch of comparisons; and if we do
+    # so, we skip the unnecessary range check: This is a semantical extension
+    # that code now relies on. :-/ XXX
+    let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0]
+               else: n[2]
+    let curly = n[1]
+    var ex: PNode = nil
+    for it in curly:
+      var test: PNode
+      if it.kind == nkRange:
+        test = newTree(nkCall, g.operators.opAnd.newSymNode,
+          newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem
+          newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1])
+        )
+      else:
+        test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it)
+      test.typ = getSysType(g, it.info, tyBool)
+
+      if ex == nil: ex = test
+      else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test)
+
+    if ex == nil:
+      let info = toLineInfo(c, n.info)
+      template body(target) =
+        boolVal target, info, false
+      intoDest d, info, Bool8Id, body
+    else:
+      gen c, ex, d
+  else:
+    genInBitset c, n, d
+
+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 setType = typeToIr(c.m.types, 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:
+        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):
+          copyTree c.code, a
+        c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
+      elif t == UInt64Id:
+        let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64")
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        copyTree c.code, a
+      else:
+        let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32")
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        buildTyped c.code, info, Cast, UInt32Id:
+          copyTree c.code, a
+  freeTemp c, a
+
+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 setType = typeToIr(c.m.types, n[1].typ)
+
+  if c.m.types.g[setType].kind == ArrayTy:
+    if isEmpty(d): d = getTemp(c, n)
+
+    buildTyped c.code, info, Asgn, t:
+      copyTree c.code, d
+      buildTyped c.code, info, Eq, t:
+        buildTyped c.code, info, Call, t:
+          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):
+            copyTree c.code, a
+          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType):
+            copyTree c.code, b
+          c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
+        c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 0
+
+  else:
+    template body(target) =
+      buildTyped target, info, Eq, setType:
+        copyTree target, a
+        copyTree target, b
+    intoDest d, info, Bool8Id, body
+
+  freeTemp c, b
+  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.code.addSummon info, tmp, c.m.nativeIntId
+  buildTyped c.code, info, Asgn, c.m.nativeIntId:
+    c.code.addSymUse info, tmp
+    c.code.addIntVal c.m.integers, info, c.m.nativeIntId, first
+  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
+  result = (tmp, lab1, newLabel(c.labelGen))
+
+  buildTyped c.code, info, Select, Bool8Id:
+    buildTyped c.code, info, Lt, c.m.nativeIntId:
+      c.code.addSymUse info, tmp
+      c.code.addIntVal c.m.integers, info, c.m.nativeIntId, last
+    build c.code, info, SelectPair:
+      build c.code, info, SelectValue:
+        c.code.boolVal(info, false)
+      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.code.addSummon info, tmp, c.m.nativeIntId
+  buildTyped c.code, info, Asgn, c.m.nativeIntId:
+    c.code.addSymUse info, tmp
+    copyTree c.code, first
+  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
+  result = (tmp, lab1, newLabel(c.labelGen))
+
+  buildTyped c.code, info, Select, Bool8Id:
+    buildTyped c.code, info, Le, c.m.nativeIntId:
+      c.code.addSymUse info, tmp
+      copyTree c.code, last
+    build c.code, info, SelectPair:
+      build c.code, info, SelectValue:
+        c.code.boolVal(info, false)
+      c.code.gotoLabel info, Goto, result[2]
+
+proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) =
+  buildTyped c.code, info, Asgn, c.m.nativeIntId:
+    c.code.addSymUse info, s
+    buildTyped c.code, info, Add, c.m.nativeIntId:
+      c.code.addSymUse info, s
+      c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 1
+  c.code.addLabel info, GotoLoop, back
+  c.code.addLabel info, Label, exit
+  freeTemp(c.sm, s)
+
+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 setType = typeToIr(c.m.types, n[1].typ)
+
+  if c.m.types.g[setType].kind == ArrayTy:
+    let elemType = bitsetBasetype(c.m.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, Bool8Id:
+      copyTree c.code, d
+      buildTyped c.code, info, Eq, elemType:
+        buildTyped c.code, info, BitAnd, elemType:
+          buildTyped c.code, info, ArrayAt, elemType:
+            copyTree c.code, a
+            c.code.addSymUse info, idx
+          buildTyped c.code, info, BitNot, elemType:
+            buildTyped c.code, info, ArrayAt, elemType:
+              copyTree c.code, b
+              c.code.addSymUse info, idx
+        c.code.addIntVal c.m.integers, info, elemType, 0
+
+    # if !$3: break
+    buildTyped c.code, info, Select, Bool8Id:
+      c.code.copyTree d
+      build c.code, info, SelectPair:
+        build c.code, info, SelectValue:
+          c.code.boolVal(info, false)
+        c.code.gotoLabel info, Goto, endLabel
+
+    endLoop(c, info, idx, backLabel, endLabel)
+  else:
+    # "(($1 & ~ $2)==0)"
+    template body(target) =
+      buildTyped target, info, Eq, setType:
+        buildTyped target, info, BitAnd, setType:
+          copyTree target, a
+          buildTyped target, info, BitNot, setType:
+            copyTree target, b
+        target.addIntVal c.m.integers, info, setType, 0
+
+    intoDest d, info, Bool8Id, body
+
+  freeTemp c, b
+  freeTemp c, a
+
+proc genLtSet(c: var ProcCon; n: PNode; d: var Value) =
+  localError(c.m.graph.config, n.info, "`<` for sets not implemented")
+
+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 setType = typeToIr(c.m.types, n[1].typ)
+
+  if c.m.types.g[setType].kind == ArrayTy:
+    let elemType = bitsetBasetype(c.m.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:
+        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:
+          copyTree c.code, a
+          c.code.addSymUse info, idx
+        if m == mMinusSet:
+          buildTyped c.code, info, BitNot, elemType:
+            buildTyped c.code, info, ArrayAt, elemType:
+              copyTree c.code, b
+              c.code.addSymUse info, idx
+        else:
+          buildTyped c.code, info, ArrayAt, elemType:
+            copyTree c.code, b
+            c.code.addSymUse info, idx
+
+    endLoop(c, info, idx, backLabel, endLabel)
+  else:
+    # "(($1 & ~ $2)==0)"
+    template body(target) =
+      buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType:
+        copyTree target, a
+        if m == mMinusSet:
+          buildTyped target, info, BitNot, setType:
+            copyTree target, b
+        else:
+          copyTree target, b
+
+    intoDest d, info, setType, body
+
+  freeTemp c, b
+  freeTemp c, a
+
+proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
+  let info = toLineInfo(c, n.info)
+  let a = c.genx(n[1])
+  let b = c.genx(n[2])
+
+  let setType = typeToIr(c.m.types, n[1].typ)
+
+  let t = bitsetBasetype(c.m.types, n[1].typ)
+  let mask =
+    case t
+    of UInt8Id: 7
+    of UInt16Id: 15
+    of UInt32Id: 31
+    else: 63
+
+  buildTyped c.code, info, Asgn, setType:
+    if c.m.types.g[setType].kind == ArrayTy:
+      if m == mIncl:
+        # $1[(NU)($2)>>3] |=(1U<<($2&7U))
+        buildTyped c.code, info, ArrayAt, t:
+          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.m.integers, info, c.m.nativeUIntId, 3
+        buildTyped c.code, info, BitOr, t:
+          buildTyped c.code, info, ArrayAt, t:
+            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.m.integers, info, c.m.nativeUIntId, 3
+          buildTyped c.code, info, BitShl, t:
+            c.code.addIntVal c.m.integers, info, t, 1
+            buildTyped c.code, info, BitAnd, t:
+              copyTree c.code, b
+              c.code.addIntVal c.m.integers, info, t, 7
+      else:
+        # $1[(NU)($2)>>3] &= ~(1U<<($2&7U))
+        buildTyped c.code, info, ArrayAt, t:
+          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.m.integers, info, c.m.nativeUIntId, 3
+        buildTyped c.code, info, BitAnd, t:
+          buildTyped c.code, info, ArrayAt, t:
+            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.m.integers, info, c.m.nativeUIntId, 3
+          buildTyped c.code, info, BitNot, t:
+            buildTyped c.code, info, BitShl, t:
+              c.code.addIntVal c.m.integers, info, t, 1
+              buildTyped c.code, info, BitAnd, t:
+                copyTree c.code, b
+                c.code.addIntVal c.m.integers, info, t, 7
+
+    else:
+      copyTree c.code, a
+      if m == mIncl:
+        # $1 |= ((NU8)1)<<(($2) & 7)
+        buildTyped c.code, info, BitOr, setType:
+          copyTree c.code, a
+          buildTyped c.code, info, BitShl, t:
+            c.code.addIntVal c.m.integers, info, t, 1
+            buildTyped c.code, info, BitAnd, t:
+              copyTree c.code, b
+              c.code.addIntVal c.m.integers, info, t, mask
+      else:
+        # $1 &= ~(((NU8)1) << (($2) & 7))
+        buildTyped c.code, info, BitAnd, setType:
+          copyTree c.code, a
+          buildTyped c.code, info, BitNot, t:
+            buildTyped c.code, info, BitShl, t:
+              c.code.addIntVal c.m.integers, info, t, 1
+              buildTyped c.code, info, BitAnd, t:
+                copyTree c.code, b
+                c.code.addIntVal c.m.integers, info, t, mask
+  freeTemp c, b
+  freeTemp c, a
+
+proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
+  # example: { a..b, c, d, e, f..g }
+  # we have to emit an expression of the form:
+  # 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 size = int(getSize(c.config, n.typ))
+  let t = bitsetBasetype(c.m.types, n.typ)
+  let mask =
+    case t
+    of UInt8Id: 7
+    of UInt16Id: 15
+    of UInt32Id: 31
+    else: 63
+
+  if isEmpty(d): d = getTemp(c, n)
+  if c.m.types.g[setType].kind != ArrayTy:
+    buildTyped c.code, info, Asgn, setType:
+      copyTree c.code, d
+      c.code.addIntVal c.m.integers, info, t, 0
+
+    for it in n:
+      if it.kind == nkRange:
+        let a = genx(c, it[0])
+        let b = genx(c, it[1])
+        let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
+        buildTyped c.code, info, Asgn, setType:
+          copyTree c.code, d
+          buildTyped c.code, info, BitAnd, setType:
+            copyTree c.code, d
+            buildTyped c.code, info, BitNot, t:
+              buildTyped c.code, info, BitShl, t:
+                c.code.addIntVal c.m.integers, info, t, 1
+                buildTyped c.code, info, BitAnd, t:
+                  c.code.addSymUse info, idx
+                  c.code.addIntVal c.m.integers, info, t, mask
+
+        endLoop(c, info, idx, backLabel, endLabel)
+        freeTemp c, b
+        freeTemp c, a
+
+      else:
+        let a = genx(c, it)
+        buildTyped c.code, info, Asgn, setType:
+          copyTree c.code, d
+          buildTyped c.code, info, BitAnd, setType:
+            copyTree c.code, d
+            buildTyped c.code, info, BitNot, t:
+              buildTyped c.code, info, BitShl, t:
+                c.code.addIntVal c.m.integers, info, t, 1
+                buildTyped c.code, info, BitAnd, t:
+                  copyTree c.code, a
+                  c.code.addIntVal c.m.integers, info, t, mask
+        freeTemp c, a
+
+  else:
+    # init loop:
+    let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size)
+    buildTyped c.code, info, Asgn, t:
+      copyTree c.code, d
+      c.code.addIntVal c.m.integers, info, t, 0
+    endLoop(c, info, idx, backLabel, endLabel)
+
+    # incl elements:
+    for it in n:
+      if it.kind == nkRange:
+        let a = genx(c, it[0])
+        let b = genx(c, it[1])
+        let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
+
+        buildTyped c.code, info, Asgn, t:
+          buildTyped c.code, info, ArrayAt, t:
+            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.m.integers, info, c.m.nativeUIntId, 3
+          buildTyped c.code, info, BitOr, t:
+            buildTyped c.code, info, ArrayAt, t:
+              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.m.integers, info, c.m.nativeUIntId, 3
+            buildTyped c.code, info, BitShl, t:
+              c.code.addIntVal c.m.integers, info, t, 1
+              buildTyped c.code, info, BitAnd, t:
+                c.code.addSymUse info, idx
+                c.code.addIntVal c.m.integers, info, t, 7
+
+        endLoop(c, info, idx, backLabel, endLabel)
+        freeTemp c, b
+        freeTemp c, a
+
+      else:
+        let a = genx(c, it)
+        # $1[(NU)($2)>>3] |=(1U<<($2&7U))
+        buildTyped c.code, info, Asgn, t:
+          buildTyped c.code, info, ArrayAt, t:
+            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.m.integers, info, c.m.nativeUIntId, 3
+          buildTyped c.code, info, BitOr, t:
+            buildTyped c.code, info, ArrayAt, t:
+              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.m.integers, info, c.m.nativeUIntId, 3
+            buildTyped c.code, info, BitShl, t:
+              c.code.addIntVal c.m.integers, info, t, 1
+              buildTyped c.code, info, BitAnd, t:
+                copyTree c.code, a
+                c.code.addIntVal c.m.integers, info, t, 7
+        freeTemp c, a
+
+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 size = int(getSize(c.config, n.typ))
+    let cs = toBitSet(c.config, n)
+
+    if c.m.types.g[setType].kind != ArrayTy:
+      template body(target) =
+        target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
+      intoDest d, info, setType, body
+    else:
+      let t = bitsetBasetype(c.m.types, n.typ)
+      template body(target) =
+        buildTyped target, info, ArrayConstr, setType:
+          for i in 0..high(cs):
+            target.addIntVal c.m.integers, info, t, int64 cs[i]
+      intoDest d, info, setType, body
+  else:
+    genSetConstrDyn c, n, d
+
+proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
+  let info = toLineInfo(c, n.info)
+  #   <Nim code>
+  #   s = "Hello " & name & ", how do you feel?" & 'z'
+  #
+  #   <generated code>
+  #  {
+  #    string tmp0;
+  #    ...
+  #    tmp0 = rawNewString(6 + 17 + 1 + s2->len);
+  #    // we cannot generate s = rawNewString(...) here, because
+  #    // ``s`` may be used on the right side of the expression
+  #    appendString(tmp0, strlit_1);
+  #    appendString(tmp0, name);
+  #    appendString(tmp0, strlit_2);
+  #    appendChar(tmp0, 'z');
+  #    asgn(s, tmp0);
+  #  }
+  var args: seq[Value] = @[]
+  var precomputedLen = 0
+  for i in 1 ..< n.len:
+    let it = n[i]
+    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)
+
+  # generate length computation:
+  var tmpLen = allocTemp(c.sm, c.m.nativeIntId)
+  buildTyped c.code, info, Asgn, c.m.nativeIntId:
+    c.code.addSymUse info, tmpLen
+    c.code.addIntVal c.m.integers, info, c.m.nativeIntId, precomputedLen
+  for a in mitems(args):
+    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:
+          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)
+  buildTyped c.code, info, Asgn, t:
+    copyTree c.code, tmpStr
+    buildTyped c.code, info, Call, t:
+      let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString")
+      #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
+      let theProc = c.genx newSymNode(codegenProc, n.info)
+      copyTree c.code, theProc
+      c.code.addSymUse info, tmpLen
+  freeTemp c.sm, tmpLen
+
+  for i in 1 ..< n.len:
+    let it = n[i]
+    let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar
+    buildTyped c.code, info, Call, VoidId:
+      let codegenProc = magicsys.getCompilerProc(c.m.graph,
+        (if isChar: "appendChar" else: "appendString"))
+      #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):
+        copyTree c.code, tmpStr
+      copyTree c.code, args[i-1]
+    freeTemp c, args[i-1]
+
+  if isEmpty(d):
+    d = tmpStr
+  else:
+    # XXX Test that this does not cause memory leaks!
+    buildTyped c.code, info, Asgn, t:
+      copyTree c.code, d
+      copyTree c.code, tmpStr
+
+proc genDefault(c: var ProcCon; n: PNode; d: var Value) =
+  let m = expandDefault(n.typ, n.info)
+  gen c, m, d
+
+proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
+  case m
+  of mAnd: c.genAndOr(n, opcFJmp, d)
+  of mOr: c.genAndOr(n, opcTJmp, d)
+  of mPred, mSubI: c.genBinaryOp(n, d, CheckedSub)
+  of mSucc, mAddI: c.genBinaryOp(n, d, CheckedAdd)
+  of mInc:
+    unused(c, n, d)
+    c.genIncDec(n, CheckedAdd)
+  of mDec:
+    unused(c, n, d)
+    c.genIncDec(n, CheckedSub)
+  of mOrd, mChr, mArrToSeq, mUnown:
+    c.gen(n[1], d)
+  of generatedMagics:
+    genCall(c, n, d)
+  of mNew, mNewFinalize:
+    unused(c, n, d)
+    c.genNew(n, needsInit = true)
+  of mNewSeq:
+    unused(c, n, d)
+    c.genNewSeq(n)
+  of mNewSeqOfCap: c.genNewSeqOfCap(n, d)
+  of mNewString, mNewStringOfCap, mExit: c.genCall(n, d)
+  of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr:
+    genArrayLen(c, n, d)
+  of mMulI: genBinaryOp(c, n, d, Mul)
+  of mDivI: genBinaryOp(c, n, d, Div)
+  of mModI: genBinaryOp(c, n, d, Mod)
+  of mAddF64: genBinaryOp(c, n, d, Add)
+  of mSubF64: genBinaryOp(c, n, d, Sub)
+  of mMulF64: genBinaryOp(c, n, d, Mul)
+  of mDivF64: genBinaryOp(c, n, d, Div)
+  of mShrI: genBinaryOp(c, n, d, BitShr)
+  of mShlI: genBinaryOp(c, n, d, BitShl)
+  of mAshrI: genBinaryOp(c, n, d, BitShr)
+  of mBitandI: genBinaryOp(c, n, d, BitAnd)
+  of mBitorI: genBinaryOp(c, n, d, BitOr)
+  of mBitxorI: genBinaryOp(c, n, d, BitXor)
+  of mAddU: genBinaryOp(c, n, d, Add)
+  of mSubU: genBinaryOp(c, n, d, Sub)
+  of mMulU: genBinaryOp(c, n, d, Mul)
+  of mDivU: genBinaryOp(c, n, d, Div)
+  of mModU: genBinaryOp(c, n, d, Mod)
+  of mEqI, mEqB, mEqEnum, mEqCh:
+    genCmpOp(c, n, d, Eq)
+  of mLeI, mLeEnum, mLeCh, mLeB:
+    genCmpOp(c, n, d, Le)
+  of mLtI, mLtEnum, mLtCh, mLtB:
+    genCmpOp(c, n, d, Lt)
+  of mEqF64: genCmpOp(c, n, d, Eq)
+  of mLeF64: genCmpOp(c, n, d, Le)
+  of mLtF64: genCmpOp(c, n, d, Lt)
+  of mLePtr, mLeU: genCmpOp(c, n, d, Le)
+  of mLtPtr, mLtU: genCmpOp(c, n, d, Lt)
+  of mEqProc, mEqRef:
+    genCmpOp(c, n, d, Eq)
+  of mXor: genBinaryOp(c, n, d, BitXor)
+  of mNot: genUnaryOp(c, n, d, BoolNot)
+  of mUnaryMinusI, mUnaryMinusI64:
+    genUnaryMinus(c, n, d)
+    #genNarrow(c, n, d)
+  of mUnaryMinusF64: genUnaryMinus(c, n, d)
+  of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d)
+  of mBitnotI:
+    genUnaryOp(c, n, d, BitNot)
+    when false:
+      # XXX genNarrowU modified, do not narrow signed types
+      let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
+      let size = getSize(c.config, t)
+      if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
+        c.gABC(n, opcNarrowU, d, TRegister(size*8))
+  of mStrToStr, mEnsureMove: c.gen n[1], d
+  of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr")
+  of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr")
+  of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr")
+  of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr")
+  of mFloatToStr:
+    if n[1].typ.skipTypes(abstractInst).kind == tyFloat32:
+      genUnaryCp(c, n, d, "nimFloat32ToStr")
+    else:
+      genUnaryCp(c, n, d, "nimFloatToStr")
+  of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr")
+  of mEnumToStr: genEnumToStr(c, n, d)
+
+  of mEqStr: genBinaryCp(c, n, d, "eqStrings")
+  of mEqCString: genCall(c, n, d)
+  of mLeStr: genBinaryCp(c, n, d, "leStrings")
+  of mLtStr: genBinaryCp(c, n, d, "ltStrings")
+
+  of mSetLengthStr:
+    unused(c, n, d)
+    let nb = copyTree(n)
+    nb[1] = makeAddr(nb[1], c.m.idgen)
+    genBinaryCp(c, nb, d, "setLengthStrV2")
+
+  of mSetLengthSeq:
+    unused(c, n, d)
+    let nb = copyTree(n)
+    nb[1] = makeAddr(nb[1], c.m.idgen)
+    genCall(c, nb, d)
+
+  of mSwap:
+    unused(c, n, d)
+    c.gen(lowerSwap(c.m.graph, n, c.m.idgen,
+      if c.prc == nil: c.m.module else: c.prc), d)
+  of mParseBiggestFloat:
+    genCall c, n, d
+  of mHigh:
+    c.genHigh n, d
+
+  of mEcho:
+    unused(c, n, d)
+    genUnaryCp c, n, d, "echoBinSafe"
+
+  of mAppendStrCh:
+    unused(c, n, d)
+    let nb = copyTree(n)
+    nb[1] = makeAddr(nb[1], c.m.idgen)
+    genBinaryCp(c, nb, d, "nimAddCharV1")
+  of mMinI, mMaxI, mAbsI, mDotDot:
+    c.genCall(n, d)
+  of mSizeOf:
+    localError(c.config, n.info, sizeOfLikeMsg("sizeof"))
+  of mAlignOf:
+    localError(c.config, n.info, sizeOfLikeMsg("alignof"))
+  of mOffsetOf:
+    localError(c.config, n.info, sizeOfLikeMsg("offsetof"))
+  of mRunnableExamples:
+    discard "just ignore any call to runnableExamples"
+  of mDestroy, mTrace: discard "ignore calls to the default destructor"
+  of mOf: genOf(c, n, d)
+  of mAppendStrStr:
+    unused(c, n, d)
+    let nb = copyTree(n)
+    nb[1] = makeAddr(nb[1], c.m.idgen)
+    genBinaryCp(c, nb, d, "nimAddStrV1")
+  of mAppendSeqElem:
+    unused(c, n, d)
+    let nb = copyTree(n)
+    nb[1] = makeAddr(nb[1], c.m.idgen)
+    genCall(c, nb, d)
+  of mIsNil: genIsNil(c, n, d)
+  of mInSet: genInSet(c, n, d)
+  of mCard: genCard(c, n, d)
+  of mEqSet: genEqSet(c, n, d)
+  of mLeSet: genLeSet(c, n, d)
+  of mLtSet: genLtSet(c, n, d)
+  of mMulSet: genBinarySet(c, n, d, m)
+  of mPlusSet: genBinarySet(c, n, d, m)
+  of mMinusSet: genBinarySet(c, n, d, m)
+  of mIncl, mExcl:
+    unused(c, n, d)
+    genInclExcl(c, n, m)
+  of mConStrStr: genStrConcat(c, n, d)
+  of mDefault, mZeroDefault:
+    genDefault c, n, d
+  else:
+    # mGCref, mGCunref,
+    globalError(c.config, n.info, "cannot generate code for: " & $m)
+
+#[
+
+  of mReset:
+    unused(c, n, d)
+    var d = c.genx(n[1])
+    # XXX use ldNullOpcode() here?
+    c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
+    c.gABC(n, opcNodeToReg, d, d)
+    c.gABx(n, ldNullOpcode(n.typ), d, c.genType(n.typ))
+
+  of mConStrStr: genVarargsABC(c, n, d, opcConcatStr)
+
+  of mRepr: genUnaryABC(c, n, d, opcRepr)
+
+  of mSlice:
+    var
+      d = c.genx(n[1])
+      left = c.genIndex(n[2], n[1].typ)
+      right = c.genIndex(n[3], n[1].typ)
+    if isEmpty(d): d = c.getTemp(n)
+    c.gABC(n, opcNodeToReg, d, d)
+    c.gABC(n, opcSlice, d, left, right)
+    c.freeTemp(left)
+    c.freeTemp(right)
+    c.freeTemp(d)
+
+  of mMove:
+    let arg = n[1]
+    let a = c.genx(arg)
+    if isEmpty(d): d = c.getTemp(arg)
+    gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a)
+    c.freeTemp(a)
+  of mDup:
+    let arg = n[1]
+    let a = c.genx(arg)
+    if isEmpty(d): d = c.getTemp(arg)
+    gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a)
+    c.freeTemp(a)
+
+  of mNodeId:
+    c.genUnaryABC(n, d, opcNodeId)
+
+  of mExpandToAst:
+    if n.len != 2:
+      globalError(c.config, n.info, "expandToAst requires 1 argument")
+    let arg = n[1]
+    if arg.kind in nkCallKinds:
+      #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}:
+      #      "ExpandToAst: expanded symbol is no macro or template"
+      if isEmpty(d): d = c.getTemp(n)
+      c.genCall(arg, d)
+      # do not call clearDest(n, d) here as getAst has a meta-type as such
+      # produces a value
+    else:
+      globalError(c.config, n.info, "expandToAst requires a call expression")
+  of mParseExprToAst:
+    genBinaryABC(c, n, d, opcParseExprToAst)
+  of mParseStmtToAst:
+    genBinaryABC(c, n, d, opcParseStmtToAst)
+  of mTypeTrait:
+    let tmp = c.genx(n[1])
+    if isEmpty(d): d = c.getTemp(n)
+    c.gABx(n, opcSetType, tmp, c.genType(n[1].typ))
+    c.gABC(n, opcTypeTrait, d, tmp)
+    c.freeTemp(tmp)
+  of mSlurp: genUnaryABC(c, n, d, opcSlurp)
+  of mNLen: genUnaryABI(c, n, d, opcLenSeq, nimNodeFlag)
+  of mGetImpl: genUnaryABC(c, n, d, opcGetImpl)
+  of mGetImplTransf: genUnaryABC(c, n, d, opcGetImplTransf)
+  of mSymOwner: genUnaryABC(c, n, d, opcSymOwner)
+  of mSymIsInstantiationOf: genBinaryABC(c, n, d, opcSymIsInstantiationOf)
+  of mNChild: genBinaryABC(c, n, d, opcNChild)
+  of mNAdd: genBinaryABC(c, n, d, opcNAdd)
+  of mNAddMultiple: genBinaryABC(c, n, d, opcNAddMultiple)
+  of mNKind: genUnaryABC(c, n, d, opcNKind)
+  of mNSymKind: genUnaryABC(c, n, d, opcNSymKind)
+
+  of mNccValue: genUnaryABC(c, n, d, opcNccValue)
+  of mNccInc: genBinaryABC(c, n, d, opcNccInc)
+  of mNcsAdd: genBinaryABC(c, n, d, opcNcsAdd)
+  of mNcsIncl: genBinaryABC(c, n, d, opcNcsIncl)
+  of mNcsLen: genUnaryABC(c, n, d, opcNcsLen)
+  of mNcsAt: genBinaryABC(c, n, d, opcNcsAt)
+  of mNctLen: genUnaryABC(c, n, d, opcNctLen)
+  of mNctGet: genBinaryABC(c, n, d, opcNctGet)
+  of mNctHasNext: genBinaryABC(c, n, d, opcNctHasNext)
+  of mNctNext: genBinaryABC(c, n, d, opcNctNext)
+
+  of mNIntVal: genUnaryABC(c, n, d, opcNIntVal)
+  of mNFloatVal: genUnaryABC(c, n, d, opcNFloatVal)
+  of mNSymbol: genUnaryABC(c, n, d, opcNSymbol)
+  of mNIdent: genUnaryABC(c, n, d, opcNIdent)
+  of mNGetType:
+    let tmp = c.genx(n[1])
+    if isEmpty(d): d = c.getTemp(n)
+    let rc = case n[0].sym.name.s:
+      of "getType": 0
+      of "typeKind": 1
+      of "getTypeInst": 2
+      else: 3  # "getTypeImpl"
+    c.gABC(n, opcNGetType, d, tmp, rc)
+    c.freeTemp(tmp)
+    #genUnaryABC(c, n, d, opcNGetType)
+  of mNSizeOf:
+    let imm = case n[0].sym.name.s:
+      of "getSize": 0
+      of "getAlign": 1
+      else: 2 # "getOffset"
+    c.genUnaryABI(n, d, opcNGetSize, imm)
+  of mNStrVal: genUnaryABC(c, n, d, opcNStrVal)
+  of mNSigHash: genUnaryABC(c, n , d, opcNSigHash)
+  of mNSetIntVal:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNSetIntVal)
+  of mNSetFloatVal:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNSetFloatVal)
+  of mNSetSymbol:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNSetSymbol)
+  of mNSetIdent:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNSetIdent)
+  of mNSetStrVal:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNSetStrVal)
+  of mNNewNimNode: genBinaryABC(c, n, d, opcNNewNimNode)
+  of mNCopyNimNode: genUnaryABC(c, n, d, opcNCopyNimNode)
+  of mNCopyNimTree: genUnaryABC(c, n, d, opcNCopyNimTree)
+  of mNBindSym: genBindSym(c, n, d)
+  of mStrToIdent: genUnaryABC(c, n, d, opcStrToIdent)
+  of mEqIdent: genBinaryABC(c, n, d, opcEqIdent)
+  of mEqNimrodNode: genBinaryABC(c, n, d, opcEqNimNode)
+  of mSameNodeType: genBinaryABC(c, n, d, opcSameNodeType)
+  of mNLineInfo:
+    case n[0].sym.name.s
+    of "getFile": genUnaryABI(c, n, d, opcNGetLineInfo, 0)
+    of "getLine": genUnaryABI(c, n, d, opcNGetLineInfo, 1)
+    of "getColumn": genUnaryABI(c, n, d, opcNGetLineInfo, 2)
+    of "copyLineInfo":
+      internalAssert c.config, n.len == 3
+      unused(c, n, d)
+      genBinaryStmt(c, n, opcNCopyLineInfo)
+    of "setLine":
+      internalAssert c.config, n.len == 3
+      unused(c, n, d)
+      genBinaryStmt(c, n, opcNSetLineInfoLine)
+    of "setColumn":
+      internalAssert c.config, n.len == 3
+      unused(c, n, d)
+      genBinaryStmt(c, n, opcNSetLineInfoColumn)
+    of "setFile":
+      internalAssert c.config, n.len == 3
+      unused(c, n, d)
+      genBinaryStmt(c, n, opcNSetLineInfoFile)
+    else: internalAssert c.config, false
+  of mNHint:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNHint)
+  of mNWarning:
+    unused(c, n, d)
+    genBinaryStmt(c, n, opcNWarning)
+  of mNError:
+    if n.len <= 1:
+      # query error condition:
+      c.gABC(n, opcQueryErrorFlag, d)
+    else:
+      # setter
+      unused(c, n, d)
+      genBinaryStmt(c, n, opcNError)
+  of mNCallSite:
+    if isEmpty(d): d = c.getTemp(n)
+    c.gABC(n, opcCallSite, d)
+  of mNGenSym: genBinaryABC(c, n, d, opcGenSym)
+
+]#
+
+proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
+  result = nil
+  case n[0].kind
+  of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
+    var m = n[0][0]
+    if m.kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
+      result = copyNode(n[0])
+      result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    var m = n[0][1]
+    if m.kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
+      result = copyNode(n[0])
+      result.add n[0][0]
+      result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
+  else:
+    if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( deref ( x )) --> x
+      result = n[0][0]
+
+template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) =
+  if isEmpty(d):
+    body(Tree d)
+  else:
+    buildTyped c.code, info, Asgn, typeToIr(c.m.types, typ):
+      copyTree c.code, d
+      body(c.code)
+
+proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) =
+  if (let m = canElimAddr(n, c.m.idgen); m != nil):
+    gen(c, m, d, flags)
+    return
+
+  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):
+      copyTree target, tmp
+
+  valueIntoDest c, info, d, n.typ, body
+  freeTemp c, tmp
+
+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):
+      copyTree target, tmp
+
+  valueIntoDest c, info, d, n.typ, body
+  freeTemp c, tmp
+
+proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) =
+  let targetType = n.typ.skipTypes({tyDistinct})
+  let argType = arg.typ.skipTypes({tyDistinct})
+
+  if sameBackendType(targetType, argType) or (
+      argType.kind == tyProc and targetType.kind == argType.kind):
+    # don't do anything for lambda lifting conversions:
+    gen c, arg, d
+    return
+
+  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):
+      if opc == CheckedObjConv:
+        target.addLabel info, CheckedGoto, c.exitLabel
+      copyTree target, tmp
+
+  valueIntoDest c, info, d, n.typ, body
+  freeTemp c, tmp
+
+proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) =
+  # 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):
+      for i in ord(n.kind == nkObjConstr)..<n.len:
+        let it = n[i]
+        if it.kind == nkExprColonExpr:
+          genField(c, it[0], Value target)
+          let tmp = c.genx(it[1])
+          copyTree target, tmp
+          c.freeTemp(tmp)
+        else:
+          let tmp = c.genx(it)
+          target.addImmediateVal info, i
+          copyTree target, tmp
+          c.freeTemp(tmp)
+
+  valueIntoDest c, info, d, n.typ, body
+
+proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) =
+  let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
+  if seqType.kind == tySequence:
+    localError c.config, n.info, "sequence constructor not implemented"
+    return
+
+  let info = toLineInfo(c, n.info)
+  template body(target) =
+    buildTyped target, info, ArrayConstr, typeToIr(c.m.types, n.typ):
+      for i in 0..<n.len:
+        let tmp = c.genx(n[i])
+        copyTree target, tmp
+        c.freeTemp(tmp)
+
+  valueIntoDest c, info, d, n.typ, body
+
+proc genAsgn2(c: var ProcCon; a, b: PNode) =
+  assert a != nil
+  assert b != nil
+  var d = c.genx(a)
+  c.gen b, d
+
+proc genVarSection(c: var ProcCon; n: PNode) =
+  for a in n:
+    if a.kind == nkCommentStmt: continue
+    #assert(a[0].kind == nkSym) can happen for transformed vars
+    if a.kind == nkVarTuple:
+      c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc))
+    else:
+      var vn = a[0]
+      if vn.kind == nkPragmaExpr: vn = vn[0]
+      if vn.kind == nkSym:
+        let s = vn.sym
+        var opc: Opcode
+        if sfThread in s.flags:
+          opc = SummonThreadLocal
+        elif sfGlobal in s.flags:
+          opc = SummonGlobal
+        else:
+          opc = Summon
+        c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), opc
+        if a[2].kind != nkEmpty:
+          genAsgn2(c, vn, a[2])
+      else:
+        if a[2].kind == nkEmpty:
+          discard "XXX assign default value to location here"
+        else:
+          genAsgn2(c, vn, a[2])
+
+proc genAsgn(c: var ProcCon; n: PNode) =
+  var d = c.genx(n[0])
+  c.gen n[1], d
+
+proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) =
+  genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0)
+
+proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) =
+  genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0)
+
+proc irModule(c: var ProcCon; owner: PSym): string =
+  #if owner == c.m.module: "" else:
+  customPath(toFullPath(c.config, owner.info))
+
+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:
+    template body(target) =
+      build target, info, ModuleSymUse:
+        target.addStrVal c.m.strings, info, irModule(c, ast.originatingModule(s))
+        target.addImmediateVal info, s.itemId.item.int
+
+    valueIntoDest c, info, d, s.typ, body
+  else:
+    template body(target) =
+      target.addSymUse info, SymId(s.itemId.item)
+    valueIntoDest c, info, d, s.typ, body
+
+proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
+  let s = n.sym
+  case s.kind
+  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:
+      # anon and generic procs have no AST so we need to remember not to forget
+      # to emit these:
+      if not c.m.pendingProcs.hasKey(s.itemId):
+        c.m.pendingProcs[s.itemId] = s
+    genRdVar(c, n, d, flags)
+  of skEnumField:
+    let info = toLineInfo(c, n.info)
+    template body(target) =
+      target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), s.position
+    valueIntoDest c, info, d, n.typ, body
+  else:
+    localError(c.config, n.info, "cannot generate code for: " & s.name.s)
+
+proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) =
+  let info = toLineInfo(c, n.info)
+  template body(target) =
+    target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), bits
+  valueIntoDest c, info, d, n.typ, body
+
+proc genStringLit(c: var ProcCon; n: PNode; d: var Value) =
+  let info = toLineInfo(c, n.info)
+  template body(target) =
+    target.addStrVal c.m.strings, info, n.strVal
+  valueIntoDest c, info, d, n.typ, body
+
+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)
+  valueIntoDest c, info, d, n.typ, body
+
+proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
+  # XXX to implement properly
+  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 info = toLineInfo(c, n.info)
+  case arrayKind
+  of tyString:
+    # XXX implement range check
+    let a = genx(c, n[0], flags)
+    let b = genx(c, n[1])
+    let t = typeToIr(c.m.types, n.typ)
+    template body(target) =
+      buildTyped target, info, ArrayAt, t:
+        buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types):
+          copyTree target, a
+          target.addImmediateVal info, 1 # (len, p)-pair
+        copyTree target, b
+    intoDest d, info, t, body
+    freeTemp c, b
+    freeTemp c, a
+
+  of tyCstring, tyPtr, tyUncheckedArray:
+    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):
+        copyTree target, a
+        copyTree target, b
+    valueIntoDest c, info, d, n.typ, body
+
+    freeTemp c, b
+    freeTemp c, a
+  of tyTuple:
+    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):
+        copyTree target, a
+        target.addImmediateVal info, b
+    valueIntoDest c, info, d, n.typ, body
+
+    freeTemp c, a
+  of tyOpenArray, tyVarargs:
+    # XXX implement range check
+    let a = genx(c, n[0], flags)
+    let b = genx(c, n[1])
+    let t = typeToIr(c.m.types, n.typ)
+    template body(target) =
+      buildTyped target, info, ArrayAt, t:
+        buildTyped target, info, FieldAt, openArrayPayloadType(c.m.types, n[0].typ):
+          copyTree target, a
+          target.addImmediateVal info, 0 # (p, len)-pair
+        copyTree target, b
+    intoDest d, info, t, body
+
+    freeTemp c, b
+    freeTemp c, a
+  of tyArray:
+    # XXX implement range check
+    let a = genx(c, n[0], flags)
+    var b = default(Value)
+    genIndex(c, n[1], n[0].typ, b)
+
+    template body(target) =
+      buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ):
+        copyTree target, a
+        copyTree target, b
+    valueIntoDest c, info, d, n.typ, body
+    freeTemp c, b
+    freeTemp c, a
+  of tySequence:
+    let a = genx(c, n[0], flags)
+    let b = genx(c, n[1])
+    let t = typeToIr(c.m.types, n.typ)
+    template body(target) =
+      buildTyped target, info, ArrayAt, t:
+        buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, n[0].typ):
+          copyTree target, a
+          target.addImmediateVal info, 1 # (len, p)-pair
+        copyTree target, b
+    intoDest d, info, t, body
+    freeTemp c, b
+    freeTemp c, a
+  else:
+    localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind
+
+proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
+  let info = toLineInfo(c, n.info)
+  let a = genx(c, n[0], flags)
+
+  template body(target) =
+    buildTyped target, info, FieldAt, typeToIr(c.m.types, n.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) =
+  for i in 1..<params.len:
+    let s = params[i].sym
+    c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), SummonParam
+
+proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) =
+  template ann(s: untyped) = c.code.addPragmaId info, s
+  case callConv
+  of ccNimCall, ccFastCall, ccClosure: ann FastCall
+  of ccStdCall: ann StdCall
+  of ccCDecl: ann CDeclCall
+  of ccSafeCall: ann SafeCall
+  of ccSysCall: ann SysCall
+  of ccInline: ann InlineCall
+  of ccNoInline: ann NoinlineCall
+  of ccThisCall: ann ThisCall
+  of ccNoConvention: ann NoCall
+
+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): return
+
+  var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config)
+  genParams(c, prc.typ.n)
+
+  let body = transformBody(c.m.graph, c.m.idgen, prc, useCache)
+
+  let info = toLineInfo(c, body.info)
+  build c.code, info, ProcDecl:
+    addSymDef c.code, info, SymId(prc.itemId.item)
+    addCallConv c, info, prc.typ.callConv
+    if {sfImportc, sfExportc} * prc.flags != {}:
+      build c.code, info, PragmaPair:
+        c.code.addPragmaId info, ExternName
+        c.code.addStrVal c.m.strings, info, prc.loc.r
+      if sfImportc in prc.flags:
+        if lfHeader in prc. loc.flags:
+          assert(prc. annex != nil)
+          let str = getStr(prc. annex.path)
+          build c.code, info, PragmaPair:
+            c.code.addPragmaId info, HeaderImport
+            c.code.addStrVal c.m.strings, info, str
+        elif lfDynamicLib in prc. loc.flags:
+          assert(prc. annex != nil)
+          let str = getStr(prc. annex.path)
+          build c.code, info, PragmaPair:
+            c.code.addPragmaId info, DllImport
+            c.code.addStrVal c.m.strings, info, str
+      elif sfExportc in prc.flags:
+        if lfDynamicLib in prc. loc.flags:
+          c.code.addPragmaId info, DllExport
+        else:
+          c.code.addPragmaId info, ObjExport
+
+    gen(c, body)
+    patch c, body, c.exitLabel
+
+  copyTree cOuter.code, c.code
+
+proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
+  when defined(nimCompilerStacktraceHints):
+    setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
+  case n.kind
+  of nkSym: genSym(c, n, d, flags)
+  of nkCallKinds:
+    if n[0].kind == nkSym:
+      let s = n[0].sym
+      if s.magic != mNone:
+        genMagic(c, n, d, s.magic)
+      elif s.kind == skMethod:
+        localError(c.config, n.info, "cannot call method " & s.name.s &
+          " at compile time")
+      else:
+        genCall(c, n, d)
+        clearDest(c, n, d)
+    else:
+      genCall(c, n, d)
+      clearDest(c, n, d)
+  of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
+    genNumericLit(c, n, d, n.intVal)
+  of nkFloatLit..nkFloat128Lit:
+    genNumericLit(c, n, d, cast[int64](n.floatVal))
+  of nkStrLit..nkTripleStrLit:
+    genStringLit(c, n, d)
+  of nkNilLit:
+    if not n.typ.isEmptyType: genNilLit(c, n, d)
+    else: unused(c, n, d)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    unused(c, n, d)
+    genAsgn(c, n)
+  of nkDotExpr: genObjAccess(c, n, d, flags)
+  of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags)
+  of nkBracketExpr: genArrAccess(c, n, d, flags)
+  of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags)
+  of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags)
+  of nkIfStmt, nkIfExpr: genIf(c, n, d)
+  of nkWhenStmt:
+    # This is "when nimvm" node. Chose the first branch.
+    gen(c, n[0][1], d)
+  of nkCaseStmt: genCase(c, n, d)
+  of nkWhileStmt:
+    unused(c, n, d)
+    genWhile(c, n)
+  of nkBlockExpr, nkBlockStmt: genBlock(c, n, d)
+  of nkReturnStmt: genReturn(c, n)
+  of nkRaiseStmt: genRaise(c, n)
+  of nkBreakStmt: genBreak(c, n)
+  of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d)
+  of nkStmtList:
+    #unused(c, n, d)
+    # XXX Fix this bug properly, lexim triggers it
+    for x in n: gen(c, x)
+  of nkStmtListExpr:
+    for i in 0..<n.len-1: gen(c, n[i])
+    gen(c, n[^1], d, flags)
+  of nkPragmaBlock:
+    gen(c, n.lastSon, d, flags)
+  of nkDiscardStmt:
+    unused(c, n, d)
+    gen(c, n[0], d)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    genConv(c, n, n[1], d, flags, NumberConv) # misnomer?
+  of nkObjDownConv:
+    genConv(c, n, n[0], d, flags, ObjConv)
+  of nkObjUpConv:
+    genConv(c, n, n[0], d, flags, CheckedObjConv)
+  of nkVarSection, nkLetSection, nkConstSection:
+    unused(c, n, d)
+    genVarSection(c, n)
+  of nkLambdaKinds:
+    #let s = n[namePos].sym
+    #discard genProc(c, s)
+    gen(c, newSymNode(n[namePos].sym), d)
+  of nkChckRangeF, nkChckRange64, nkChckRange:
+    genRangeCheck(c, n, d)
+  of declarativeDefs - {nkIteratorDef}:
+    unused(c, n, d)
+    genProc(c, n)
+  of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma,
+     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
+     nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef:
+    unused(c, n, d)
+  of nkStringToCString: convStrToCStr(c, n, d)
+  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 nkCast:
+    genConv(c, n, n[1], d, flags, Cast)
+  of nkComesFrom:
+    discard "XXX to implement for better stack traces"
+  else:
+    localError(c.config, n.info, "cannot generate IR code for " & $n)
+
+proc genStmt*(c: var ProcCon; n: PNode): int =
+  result = c.code.len
+  var d = default(Value)
+  c.gen(n, d)
+  unused c, n, d
+
+proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int =
+  result = c.code.len
+  var d = default(Value)
+  c.gen(n, d)
+  if isEmpty d:
+    if requiresValue:
+      globalError(c.config, n.info, "VM problem: d register is not set")
diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim
new file mode 100644
index 000000000..90a3035dd
--- /dev/null
+++ b/compiler/nir/cir.nim
@@ -0,0 +1,78 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# We produce C code as a list of tokens.
+
+import std / assertions
+import .. / ic / bitabs
+
+type
+  Token = LitId # indexing into the tokens BiTable[string]
+
+  PredefinedToken = enum
+    IgnoreMe = "<unused>"
+    EmptyToken = ""
+    DeclPrefix = "" # the next token is the name of a definition
+    CurlyLe = "{"
+    CurlyRi = "}"
+    ParLe = "("
+    ParRi = ")"
+    BracketLe = "["
+    BracketRi = "]"
+    NewLine = "\n"
+    Semicolon = ";"
+    Comma = ", "
+    Space = " "
+    Colon = ":"
+    Dot = "."
+    Arrow = "->"
+    Star = "*"
+    Amp = "&"
+    AsgnOpr = " = "
+    ScopeOpr = "::"
+    ConstKeyword = "const "
+    StaticKeyword = "static "
+    NimString = "NimString"
+    StrLitPrefix = "(NimChar*)"
+    StrLitNamePrefix = "Qstr"
+    LoopKeyword = "while (true) "
+    WhileKeyword = "while ("
+    IfKeyword = "if ("
+    ElseKeyword = "else "
+    SwitchKeyword = "switch ("
+    CaseKeyword = "case "
+    DefaultKeyword = "default:"
+    BreakKeyword = "break"
+    NullPtr = "nullptr"
+    IfNot = "if (!("
+    ReturnKeyword = "return "
+
+const
+  ModulePrefix = Token(int(ReturnKeyword)+1)
+
+proc fillTokenTable(tab: var BiTable[string]) =
+  for e in EmptyToken..high(PredefinedToken):
+    let id = tab.getOrIncl $e
+    assert id == LitId(e)
+
+type
+  GeneratedCode* = object
+    code: seq[LitId]
+    tokens: BiTable[string]
+
+proc initGeneratedCode*(): GeneratedCode =
+  result = GeneratedCode(code: @[], tokens: initBiTable[string]())
+  fillTokenTable(result.tokens)
+
+proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
+  g.code.add Token(t)
+
+proc add*(g: var GeneratedCode; s: string) {.inline.} =
+  g.code.add g.tokens.getOrIncl(s)
+
diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim
new file mode 100644
index 000000000..1994a1be7
--- /dev/null
+++ b/compiler/nir/nir.nim
@@ -0,0 +1,71 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much
+## precious information. Can easily be translated into C. And to JavaScript, hopefully.
+
+import ".." / [ast, modulegraphs, renderer, transf]
+import nirtypes, nirinsts, ast2ir
+
+type
+  PCtx* = ref object of TPassContext
+    m: ModuleCon
+    c: ProcCon
+    oldErrorCount: int
+
+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)
+
+proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
+  c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module)
+  c.c = initProcCon(c.m, nil, c.m.graph.config)
+  c.idgen = idgen
+
+proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
+  if graph.repl.isNil:
+    graph.repl = newCtx(module, graph, idgen)
+    #registerAdditionalOps(PCtx graph.repl)
+  else:
+    refresh(PCtx graph.repl, module, idgen)
+
+proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  setupGlobalCtx(module, graph, idgen)
+  result = PCtx graph.repl
+
+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.strings, c.m.integers, res
+  #res.add "\n"
+  #toString res, c.m.types.g
+  echo res
+
+
+proc runCode*(c: PPassContext; n: PNode): PNode =
+  let c = PCtx(c)
+  # don't eval errornous code:
+  if c.oldErrorCount == c.m.graph.config.errorCounter:
+    evalStmt(c, n)
+    result = newNodeI(nkEmpty, n.info)
+  else:
+    result = n
+  c.oldErrorCount = c.m.graph.config.errorCounter
+
+when false:
+  type
+    Module* = object
+      types: TypeGraph
+      data: seq[Tree]
+      init: seq[Tree]
+      procs: seq[Tree]
+
diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim
new file mode 100644
index 000000000..f037b4f0e
--- /dev/null
+++ b/compiler/nir/nirinsts.nim
@@ -0,0 +1,418 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## NIR instructions. Somewhat inspired by LLVM's instructions.
+
+import std / [assertions, hashes]
+import .. / ic / bitabs
+import nirlineinfos, nirtypes
+
+type
+  SymId* = distinct int
+
+proc `$`*(s: SymId): string {.borrow.}
+proc hash*(s: SymId): Hash {.borrow.}
+proc `==`*(a, b: SymId): bool {.borrow.}
+
+type
+  Opcode* = enum
+    Nop,
+    ImmediateVal,
+    IntVal,
+    StrVal,
+    SymDef,
+    SymUse,
+    Typed,   # with type ID
+    PragmaId, # with Pragma ID, possible values: see PragmaKey enum
+    NilVal,
+    Label,
+    Goto,
+    CheckedGoto,
+    LoopLabel,
+    GotoLoop,  # last atom
+
+    ModuleSymUse, # `"module".x`
+
+    ArrayConstr,
+    ObjConstr,
+    Ret,
+    Yld,
+
+    Select,
+    SelectPair,  # ((values...), Label)
+    SelectList,  # (values...)
+    SelectValue, # (value)
+    SelectRange, # (valueA..valueB)
+    SummonGlobal,
+    SummonThreadLocal,
+    Summon, # x = Summon Typed <Type ID>; x begins to live
+    SummonParam,
+    SummonConst,
+    Kill, # `Kill x`: scope end for `x`
+
+    AddrOf,
+    ArrayAt, # addr(a[i])
+    FieldAt, # addr(obj.field)
+
+    Load, # a[]
+    Store, # a[] = b
+    Asgn,  # a = b
+    SetExc,
+    TestExc,
+
+    Call,
+    IndirectCall,
+    CheckedCall, # call that can raise
+    CheckedIndirectCall, # call that can raise
+    CheckedAdd, # with overflow checking etc.
+    CheckedSub,
+    CheckedMul,
+    CheckedDiv,
+    CheckedMod,
+    Add,
+    Sub,
+    Mul,
+    Div,
+    Mod,
+    BitShl,
+    BitShr,
+    BitAnd,
+    BitOr,
+    BitXor,
+    BitNot,
+    BoolNot,
+    Eq,
+    Le,
+    Lt,
+    Cast,
+    NumberConv,
+    CheckedObjConv,
+    ObjConv,
+    TestOf,
+    Emit,
+    ProcDecl,
+    PragmaPair
+
+type
+  PragmaKey* = enum
+    FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
+    ExternName,
+    HeaderImport,
+    DllImport,
+    DllExport,
+    ObjExport
+
+const
+  LastAtomicValue = GotoLoop
+
+  OpcodeBits = 8'u32
+  OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
+
+  ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
+
+  ValueProducing* = {
+    ImmediateVal,
+    IntVal,
+    StrVal,
+    SymUse,
+    NilVal,
+    ModuleSymUse,
+    ArrayConstr,
+    ObjConstr,
+    CheckedAdd,
+    CheckedSub,
+    CheckedMul,
+    CheckedDiv,
+    CheckedMod,
+    Add,
+    Sub,
+    Mul,
+    Div,
+    Mod,
+    BitShl,
+    BitShr,
+    BitAnd,
+    BitOr,
+    BitXor,
+    BitNot,
+    BoolNot,
+    Eq,
+    Le,
+    Lt,
+    Cast,
+    NumberConv,
+    CheckedObjConv,
+    ObjConv,
+    AddrOf,
+    Load,
+    ArrayAt,
+    FieldAt,
+    TestOf
+  }
+
+type
+  Instr* = object     # 8 bytes
+    x: uint32
+    info: PackedLineInfo
+
+template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
+template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
+
+template toX(k: Opcode; operand: uint32): uint32 =
+  uint32(k) or (operand shl OpcodeBits)
+
+template toX(k: Opcode; operand: LitId): uint32 =
+  uint32(k) or (operand.uint32 shl OpcodeBits)
+
+type
+  Tree* = object
+    nodes: seq[Instr]
+
+  Values* = object
+    numbers: BiTable[int64]
+    strings: BiTable[string]
+
+type
+  PatchPos* = distinct int
+  NodePos* = distinct int
+
+const
+  InvalidPatchPos* = PatchPos(-1)
+
+proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
+
+proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
+  result = PatchPos tree.nodes.len
+  tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
+
+proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
+proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
+
+proc patch*(tree: var Tree; pos: PatchPos) =
+  let pos = pos.int
+  let k = tree.nodes[pos].kind
+  assert k > LastAtomicValue
+  let distance = int32(tree.nodes.len - pos)
+  assert distance > 0
+  tree.nodes[pos].x = toX(k, cast[uint32](distance))
+
+template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
+  let pos = prepare(tree, info, kind)
+  body
+  patch(tree, pos)
+
+template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
+  let pos = prepare(tree, info, kind)
+  tree.addTyped info, typ
+  body
+  patch(tree, pos)
+
+proc len*(tree: Tree): int {.inline.} = tree.nodes.len
+
+template rawSpan(n: Instr): int = int(operand(n))
+
+proc nextChild(tree: Tree; pos: var int) {.inline.} =
+  if tree.nodes[pos].kind > LastAtomicValue:
+    assert tree.nodes[pos].operand > 0'u32
+    inc pos, tree.nodes[pos].rawSpan
+  else:
+    inc pos
+
+iterator sons*(tree: Tree; n: NodePos): NodePos =
+  var pos = n.int
+  assert tree.nodes[pos].kind > LastAtomicValue
+  let last = pos + tree.nodes[pos].rawSpan
+  inc 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.} =
+  if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
+
+proc copyTree*(dest: var Tree; src: Tree) =
+  let pos = 0
+  let L = span(src, pos)
+  let d = dest.nodes.len
+  dest.nodes.setLen(d + L)
+  assert L > 0
+  for i in 0..<L:
+    dest.nodes[d+i] = src.nodes[pos+i]
+
+type
+  LabelId* = distinct int
+
+proc newLabel*(labelGen: var int): LabelId {.inline.} =
+  result = LabelId labelGen
+  inc labelGen
+
+proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
+  assert k in {Label, LoopLabel}
+  result = LabelId labelGen
+  t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
+  inc labelGen
+
+proc boolVal*(t: var Tree; info: PackedLineInfo; b: bool) =
+  t.nodes.add Instr(x: toX(ImmediateVal, uint32(b)), info: info)
+
+proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
+  assert k in {Goto, GotoLoop, CheckedGoto}
+  t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
+
+proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
+  assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
+  t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
+
+proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
+  t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
+
+proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
+  t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
+
+proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
+  t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
+
+proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
+  assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam}
+  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)
+  patch t, x
+
+proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
+  assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
+  t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
+
+proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
+  t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
+
+proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
+  buildTyped t, info, NumberConv, typ:
+    t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
+
+proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
+  t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
+
+proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
+  buildTyped t, info, NumberConv, typ:
+    t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
+
+proc escapeToNimLit(s: string; result: var string) =
+  result.add '"'
+  for c in items s:
+    if c < ' ' or int(c) >= 128:
+      result.add '\\'
+      result.addInt int(c)
+    elif c == '\\':
+      result.add r"\\"
+    elif c == '\n':
+      result.add r"\n"
+    elif c == '\r':
+      result.add r"\r"
+    elif c == '\t':
+      result.add r"\t"
+    else:
+      result.add c
+  result.add '"'
+
+proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
+               r: var string; nesting = 0) =
+  if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
+    r.add ' '
+
+  case t[pos].kind
+  of Nop: r.add "Nop"
+  of ImmediateVal:
+    r.add $t[pos].operand
+  of IntVal:
+    r.add "IntVal "
+    r.add $integers[LitId t[pos].operand]
+  of StrVal:
+    escapeToNimLit(strings[LitId t[pos].operand], r)
+  of SymDef:
+    r.add "SymDef "
+    r.add $t[pos].operand
+  of SymUse:
+    r.add "SymUse "
+    r.add $t[pos].operand
+  of PragmaId:
+    r.add $cast[PragmaKey](t[pos].operand)
+  of Typed:
+    r.add "Typed "
+    r.add $t[pos].operand
+  of NilVal:
+    r.add "NilVal"
+  of Label:
+    r.add "L"
+    r.add $t[pos].operand
+  of Goto, CheckedGoto, LoopLabel, GotoLoop:
+    r.add $t[pos].kind
+    r.add ' '
+    r.add $t[pos].operand
+  else:
+    r.add $t[pos].kind
+    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
+    r.add "\n"
+    for i in 0..<nesting*2: r.add ' '
+    r.add "}"
+
+type
+  Value* = distinct Tree
+
+proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
+  assert k in ValueProducing - ValueProducingAtoms
+  result = prepare(Tree(dest), info, k)
+
+proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
+  patch(Tree(dest), pos)
+
+proc localToValue*(info: PackedLineInfo; s: SymId): Value =
+  result = Value(Tree())
+  Tree(result).addSymUse info, s
+
+proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
+
+proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
+
+proc extractTemp*(v: Value): SymId =
+  if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
+    result = SymId(Tree(v)[NodePos 0].operand)
+  else:
+    result = SymId(-1)
+
+proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
+
+proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
+  assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
+  Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
+
+template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
+  let pos = prepare(Tree(tree), info, kind)
+  body
+  patch(tree, pos)
+
+proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
+  addTyped(Tree(t), info, typ)
+
+template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
+  let pos = prepare(tree, info, kind)
+  tree.addTyped info, typ
+  body
+  patch(tree, pos)
+
+proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
+  addStrVal(Tree(t), strings, info, s)
+
+proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
+  addNilVal Tree(t), info, typ
diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim
new file mode 100644
index 000000000..4e86f619e
--- /dev/null
+++ b/compiler/nir/nirlineinfos.nim
@@ -0,0 +1,78 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# For the line information we use 32 bits. They are used as follows:
+# Bit 0 (AsideBit): If we have inline line information or not. If not, the
+# remaining 31 bits are used as an index into a seq[(LitId, int, int)].
+#
+# We use 10 bits for the "file ID", this means a program can consist of as much
+# as 1024 different files. (If it uses more files than that, the overflow bit
+# would be set.)
+# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
+# so 128 is the limit and 14 bits for the line number.
+# The packed representation supports files with up to 16384 lines.
+# Keep in mind that whenever any limit is reached the AsideBit is set and the real line
+# information is kept in a side channel.
+
+import std / assertions
+
+const
+  AsideBit = 1
+  FileBits = 10
+  LineBits = 14
+  ColBits = 7
+  FileMax = (1 shl FileBits) - 1
+  LineMax = (1 shl LineBits) - 1
+  ColMax = (1 shl ColBits) - 1
+
+static:
+  assert AsideBit + FileBits + LineBits + ColBits == 32
+
+import .. / ic / bitabs # for LitId
+
+type
+  PackedLineInfo* = distinct uint32
+
+  LineInfoManager* = object
+    aside*: seq[(LitId, int32, int32)]
+
+proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
+  if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
+    let col = if col < 0'i32: 0'u32 else: col.uint32
+    let line = if line < 0'i32: 0'u32 else: line.uint32
+    # use inline representation:
+    result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
+      (col shl uint32(AsideBit + FileBits + LineBits)))
+  else:
+    result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
+    m.aside.add (file, line, col)
+
+proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
+  let i = i.uint32
+  if (i and 1'u32) == 0'u32:
+    # inline representation:
+    result = (LitId((i shr 1'u32) and FileMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
+  else:
+    result = m.aside[int(i shr 1'u32)]
+
+proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
+  result = unpack(m, i)[0]
+
+when isMainModule:
+  var m = LineInfoManager(aside: @[])
+  for i in 0'i32..<16388'i32:
+    for col in 0'i32..<100'i32:
+      let packed = pack(m, LitId(1023), i, col)
+      let u = unpack(m, packed)
+      assert u[0] == LitId(1023)
+      assert u[1] == i
+      assert u[2] == col
+  echo m.aside.len
diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim
new file mode 100644
index 000000000..256c25a19
--- /dev/null
+++ b/compiler/nir/nirslots.nim
@@ -0,0 +1,105 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Management of slots. Similar to "register allocation"
+## in lower level languages.
+
+import std / [assertions, tables]
+import nirtypes, nirinsts
+
+type
+  SlotManagerFlag* = enum
+    ReuseTemps,
+    ReuseVars
+  SlotKind* = enum
+    Temp, Perm
+  SlotManager* = object # "register allocator"
+    live: Table[SymId, (SlotKind, TypeId)]
+    dead: Table[TypeId, seq[SymId]]
+    flags: set[SlotManagerFlag]
+    inScope: seq[SymId]
+    locGen: ref int
+
+proc initSlotManager*(flags: set[SlotManagerFlag]; generator: ref int): SlotManager {.inline.} =
+  SlotManager(flags: flags, locGen: generator)
+
+proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind): SymId {.inline.} =
+  if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0:
+    result = m.dead[t].pop()
+  else:
+    result = SymId(m.locGen[])
+    inc m.locGen[]
+    m.inScope.add result
+  m.live[result] = (k, t)
+
+proc allocTemp*(m: var SlotManager; t: TypeId): SymId {.inline.} =
+  result = allocRaw(m, t, ReuseTemps, Temp)
+
+proc allocVar*(m: var SlotManager; t: TypeId): SymId {.inline.} =
+  result = allocRaw(m, t, ReuseVars, Perm)
+
+proc freeLoc*(m: var SlotManager; s: SymId) =
+  let t = m.live.getOrDefault(s)
+  assert t[1].int != 0
+  m.live.del s
+  m.dead.mgetOrPut(t[1], @[]).add s
+
+proc freeTemp*(m: var SlotManager; s: SymId) =
+  let t = m.live.getOrDefault(s)
+  if t[1].int != 0 and t[0] == Temp:
+    m.live.del s
+    m.dead.mgetOrPut(t[1], @[]).add s
+
+iterator stillAlive*(m: SlotManager): (SymId, TypeId) =
+  for k, v in pairs(m.live):
+    yield (k, v[1])
+
+proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1]
+
+proc openScope*(m: var SlotManager) =
+  m.inScope.add SymId(-1) # add marker
+
+proc closeScope*(m: var SlotManager) =
+  var i = m.inScope.len - 1
+  while i >= 0:
+    if m.inScope[i] == SymId(-1):
+      m.inScope.setLen i
+      break
+    dec i
+
+when isMainModule:
+  var m = initSlotManager({ReuseTemps}, new(int))
+
+  var g = initTypeGraph()
+
+  let a = g.openType ArrayTy
+  g.addBuiltinType Int8Id
+  g.addArrayLen 5'u64
+  let finalArrayType = sealType(g, a)
+
+  let obj = g.openType ObjectDecl
+  g.addName "MyType"
+
+  g.addField "p", finalArrayType
+  let objB = sealType(g, obj)
+
+  let x = m.allocTemp(objB)
+  assert x.int == 0
+
+  let y = m.allocTemp(objB)
+  assert y.int == 1
+
+  let z = m.allocTemp(Int8Id)
+  assert z.int == 2
+
+  m.freeLoc y
+  let y2 = m.allocTemp(objB)
+  assert y2.int == 1
+
+
diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim
new file mode 100644
index 000000000..d989397a6
--- /dev/null
+++ b/compiler/nir/nirtypes.nim
@@ -0,0 +1,347 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Type system for NIR. Close to C's type system but without its quirks.
+
+import std / [assertions, hashes]
+import .. / ic / bitabs
+
+type
+  NirTypeKind* = enum
+    VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal,
+    AnnotationVal,
+    VarargsTy, # the `...` in a C prototype; also the last "atom"
+    APtrTy, # pointer to aliasable memory
+    UPtrTy, # pointer to unique/unaliasable memory
+    AArrayPtrTy, # pointer to array of aliasable memory
+    UArrayPtrTy, # pointer to array of unique/unaliasable memory
+    ArrayTy,
+    LastArrayTy, # array of unspecified size as a last field inside an object
+    ObjectTy,
+    UnionTy,
+    ProcTy,
+    ObjectDecl,
+    UnionDecl,
+    FieldDecl
+
+const
+  TypeKindBits = 8'u32
+  TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32
+
+type
+  TypeNode* = object     # 4 bytes
+    x: uint32
+
+template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask)
+template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits)
+
+template toX(k: NirTypeKind; operand: uint32): uint32 =
+  uint32(k) or (operand shl TypeKindBits)
+
+template toX(k: NirTypeKind; operand: LitId): uint32 =
+  uint32(k) or (operand.uint32 shl TypeKindBits)
+
+type
+  TypeId* = distinct int
+
+proc `==`*(a, b: TypeId): bool {.borrow.}
+proc hash*(a: TypeId): Hash {.borrow.}
+
+type
+  TypeGraph* = object
+    nodes: seq[TypeNode]
+    names: BiTable[string]
+    numbers: BiTable[uint64]
+
+const
+  VoidId* = TypeId 0
+  Bool8Id* = TypeId 1
+  Char8Id* = TypeId 2
+  Int8Id* = TypeId 3
+  Int16Id* = TypeId 4
+  Int32Id* = TypeId 5
+  Int64Id* = TypeId 6
+  UInt8Id* = TypeId 7
+  UInt16Id* = TypeId 8
+  UInt32Id* = TypeId 9
+  UInt64Id* = TypeId 10
+  Float32Id* = TypeId 11
+  Float64Id* = TypeId 12
+  VoidPtrId* = TypeId 13
+  LastBuiltinId* = 13
+
+proc initTypeGraph*(): TypeGraph =
+  result = TypeGraph(nodes: @[
+    TypeNode(x: toX(VoidTy, 0'u32)),
+    TypeNode(x: toX(BoolTy, 8'u32)),
+    TypeNode(x: toX(CharTy, 8'u32)),
+    TypeNode(x: toX(IntTy, 8'u32)),
+    TypeNode(x: toX(IntTy, 16'u32)),
+    TypeNode(x: toX(IntTy, 32'u32)),
+    TypeNode(x: toX(IntTy, 64'u32)),
+    TypeNode(x: toX(UIntTy, 8'u32)),
+    TypeNode(x: toX(UIntTy, 16'u32)),
+    TypeNode(x: toX(UIntTy, 32'u32)),
+    TypeNode(x: toX(UIntTy, 64'u32)),
+    TypeNode(x: toX(FloatTy, 32'u32)),
+    TypeNode(x: toX(FloatTy, 64'u32)),
+    TypeNode(x: toX(APtrTy, 2'u32)),
+    TypeNode(x: toX(VoidTy, 0'u32))
+  ])
+  assert result.nodes.len == LastBuiltinId+2
+
+type
+  TypePatchPos* = distinct int
+
+const
+  InvalidTypePatchPos* = TypePatchPos(-1)
+  LastAtomicValue = VarargsTy
+
+proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1
+
+proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
+  result = TypePatchPos tree.nodes.len
+  tree.nodes.add TypeNode(x: toX(kind, 1'u32))
+
+proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
+proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
+
+proc patch(tree: var TypeGraph; pos: TypePatchPos) =
+  let pos = pos.int
+  let k = tree.nodes[pos].kind
+  assert k > LastAtomicValue
+  let distance = int32(tree.nodes.len - pos)
+  assert distance > 0
+  tree.nodes[pos].x = toX(k, cast[uint32](distance))
+
+proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len
+
+template rawSpan(n: TypeNode): int = int(operand(n))
+
+proc nextChild(tree: TypeGraph; pos: var int) {.inline.} =
+  if tree.nodes[pos].kind > LastAtomicValue:
+    assert tree.nodes[pos].operand > 0'u32
+    inc pos, tree.nodes[pos].rawSpan
+  else:
+    inc pos
+
+iterator sons*(tree: TypeGraph; n: TypeId): TypeId =
+  var pos = n.int
+  assert tree.nodes[pos].kind > LastAtomicValue
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  while pos < last:
+    yield TypeId pos
+    nextChild tree, pos
+
+template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int]
+
+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 kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind
+
+proc span(tree: TypeGraph; pos: int): int {.inline.} =
+  if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
+
+proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  result = (TypeId a, TypeId b)
+
+proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  result = (TypeId a, TypeId b, TypeId c)
+
+proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt =
+  assert tree[n].kind == ArrayTy
+  result = tree.numbers[LitId tree[n].operand]
+
+proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
+  assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
+    ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
+    FieldDecl}
+  result = prepare(tree, kind)
+
+proc sealType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
+  # TODO: Search for an existing instance of this type in
+  # order to reduce memory consumption.
+  result = TypeId(p)
+  patch tree, p
+
+proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
+  assert kind in {ObjectTy, UnionTy}
+  result = TypeId tree.nodes.len
+  tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name)))
+
+proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
+  assert kind in {ObjectTy, UnionTy}
+  tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name)))
+
+proc addVarargs*(tree: var TypeGraph) =
+  tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
+
+proc getFloat128Type*(tree: var TypeGraph): TypeId =
+  result = TypeId tree.nodes.len
+  tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32))
+
+proc addBuiltinType*(g: var TypeGraph; id: TypeId) =
+  g.nodes.add g[id]
+
+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:
+  if g[t].kind in {ObjectDecl, UnionDecl}:
+    assert g[t.firstSon].kind == NameVal
+    let name = LitId g[t.firstSon].operand
+    if g[t].kind == ObjectDecl:
+      g.nodes.add TypeNode(x: toX(ObjectTy, name))
+    else:
+      g.nodes.add TypeNode(x: toX(UnionTy, name))
+  else:
+    let pos = t.int
+    let L = span(g, pos)
+    let d = g.nodes.len
+    g.nodes.setLen(d + L)
+    assert L > 0
+    for i in 0..<L:
+      g.nodes[d+i] = g.nodes[pos+i]
+
+proc addArrayLen*(g: var TypeGraph; len: uint64) =
+  g.nodes.add TypeNode(x: toX(IntVal, g.numbers.getOrIncl(len)))
+
+proc addName*(g: var TypeGraph; name: string) =
+  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
+
+proc addAnnotation*(g: var TypeGraph; name: string) =
+  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
+
+proc addField*(g: var TypeGraph; name: string; typ: TypeId) =
+  let f = g.openType FieldDecl
+  g.addType typ
+  g.addName name
+  discard sealType(g, f)
+
+proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
+  let f = g.openType APtrTy
+  g.addType t
+  result = sealType(g, f)
+
+proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
+  case g[i].kind
+  of VoidTy: dest.add "void"
+  of IntTy:
+    dest.add "i"
+    dest.addInt g[i].operand
+  of UIntTy:
+    dest.add "u"
+    dest.addInt g[i].operand
+  of FloatTy:
+    dest.add "f"
+    dest.addInt g[i].operand
+  of BoolTy:
+    dest.add "b"
+    dest.addInt g[i].operand
+  of CharTy:
+    dest.add "c"
+    dest.addInt g[i].operand
+  of NameVal, AnnotationVal:
+    dest.add g.names[LitId g[i].operand]
+  of IntVal:
+    dest.add $g.numbers[LitId g[i].operand]
+  of VarargsTy:
+    dest.add "..."
+  of APtrTy:
+    dest.add "aptr["
+    toString(dest, g, g.elementType(i))
+    dest.add "]"
+  of UPtrTy:
+    dest.add "uptr["
+    toString(dest, g, g.elementType(i))
+    dest.add "]"
+  of AArrayPtrTy:
+    dest.add "aArrayPtr["
+    toString(dest, g, g.elementType(i))
+    dest.add "]"
+  of UArrayPtrTy:
+    dest.add "uArrayPtr["
+    toString(dest, g, g.elementType(i))
+    dest.add "]"
+  of ArrayTy:
+    dest.add "Array["
+    let (elems, len) = g.sons2(i)
+    toString(dest, g, elems)
+    dest.add ", "
+    toString(dest, g, len)
+    dest.add "]"
+  of LastArrayTy:
+    # array of unspecified size as a last field inside an object
+    dest.add "LastArrayTy["
+    toString(dest, g, g.elementType(i))
+    dest.add "]"
+  of ObjectTy:
+    dest.add "object "
+    dest.add g.names[LitId g[i].operand]
+  of UnionTy:
+    dest.add "union "
+    dest.add g.names[LitId g[i].operand]
+  of ProcTy:
+    dest.add "proc["
+    for t in sons(g, i): toString(dest, g, t)
+    dest.add "]"
+  of ObjectDecl:
+    dest.add "object["
+    for t in sons(g, i):
+      toString(dest, g, t)
+      dest.add '\n'
+    dest.add "]"
+  of UnionDecl:
+    dest.add "union["
+    for t in sons(g, i):
+      toString(dest, g, t)
+      dest.add '\n'
+    dest.add "]"
+  of FieldDecl:
+    let (typ, name) = g.sons2(i)
+    toString(dest, g, typ)
+    dest.add ' '
+    toString(dest, g, name)
+
+proc toString*(dest: var string; g: TypeGraph) =
+  var i = 0
+  while i < g.len:
+    toString(dest, g, TypeId i)
+    dest.add '\n'
+    nextChild g, i
+
+proc `$`(g: TypeGraph): string =
+  result = ""
+  toString(result, g)
+
+when isMainModule:
+  var g = initTypeGraph()
+
+  let a = g.openType ArrayTy
+  g.addBuiltinType Int8Id
+  g.addArrayLen 5'u64
+  let finalArrayType = sealType(g, a)
+
+  let obj = g.openType ObjectDecl
+  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType")))
+
+  g.addField "p", finalArrayType
+  discard sealType(g, obj)
+
+  echo g
diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim
new file mode 100644
index 000000000..6d163c6c7
--- /dev/null
+++ b/compiler/nir/types2ir.nim
@@ -0,0 +1,466 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import std / [assertions, tables, sets]
+import ".." / [ast, types, options, sighashes, modulegraphs]
+import nirtypes
+
+type
+  TypesCon* = object
+    processed: Table[ItemId, TypeId]
+    recursionCheck: HashSet[ItemId]
+    g*: TypeGraph
+    conf: ConfigRef
+
+proc initTypesCon*(conf: ConfigRef): TypesCon =
+  TypesCon(g: initTypeGraph(), conf: conf)
+
+proc mangle(c: var TypesCon; t: PType): string =
+  result = $sighashes.hashType(t, c.conf)
+
+template cached(c: var TypesCon; t: PType; body: untyped) =
+  result = c.processed.getOrDefault(t.itemId)
+  if result.int == 0:
+    body
+    c.processed[t.itemId] = result
+
+proc typeToIr*(c: var TypesCon; t: PType): TypeId
+
+proc collectFieldTypes(c: var TypesCon; n: PNode; dest: var Table[ItemId, TypeId]) =
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      collectFieldTypes(c, n[i], dest)
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    collectFieldTypes(c, n[0], dest)
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        collectFieldTypes c, lastSon(n[i]), dest
+      else: discard
+  of nkSym:
+    dest[n.sym.itemId] = typeToIr(c, 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) =
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      objectToIr(c, 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
+    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
+        discard sealType(c.g, subObj)
+      else: discard
+    discard sealType(c.g, u)
+  of nkSym:
+    c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId]
+  else:
+    assert false, "unknown node kind: " & $n.kind
+
+proc objectToIr(c: var TypesCon; t: PType): TypeId =
+  if t[0] != nil:
+    # ensure we emitted the base type:
+    discard typeToIr(c, 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)
+  if t[0] != nil:
+    c.g.addNominalType(ObjectTy, mangle(c, t[0]))
+  else:
+    c.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)
+      discard sealType(c.g, voidPtr)
+      c.g.addName "m_type"
+      discard sealType(c.g, f2) # FieldDecl
+
+  objectToIr c, t.n, fieldTypes, unionId
+  result = sealType(c.g, obj)
+
+proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId =
+  result = c.g.nominalType(ObjectTy, mangle(c, t))
+
+proc tupleToIr(c: var TypesCon; 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)
+  for i in 0..<t.len:
+    c.g.addField "f_" & $i, fieldTypes[i]
+  result = sealType(c.g, obj)
+
+proc procToIr(c: var TypesCon; 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)
+
+  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 ""
+
+  for i in 0..<fieldTypes.len:
+    c.g.addType fieldTypes[i]
+
+  if addEnv:
+    let a = openType(c.g, APtrTy)
+    c.g.addBuiltinType(VoidId)
+    discard sealType(c.g, a)
+
+  if tfVarargs in t.flags:
+    c.g.addVarargs()
+  result = sealType(c.g, obj)
+
+proc nativeInt(c: TypesCon): TypeId =
+  case c.conf.target.intSize
+  of 2: result = Int16Id
+  of 4: result = Int32Id
+  else: result = Int64Id
+
+proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId =
+  let e = lastSon(t)
+  let elementType = typeToIr(c, e)
+  let arr = c.g.openType AArrayPtrTy
+  c.g.addType elementType
+  result = sealType(c.g, arr) # LastArrayTy
+
+proc openArrayToIr(c: var TypesCon; 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)
+  #assert elementType.int >= 0, typeToString(t)
+
+  let p = openType(c.g, ObjectDecl)
+  c.g.addName typeName
+
+  let f = c.g.openType FieldDecl
+  let arr = c.g.openType AArrayPtrTy
+  c.g.addType elementType
+  discard sealType(c.g, arr) # LastArrayTy
+  c.g.addName "data"
+  discard sealType(c.g, f) # FieldDecl
+
+  c.g.addField "len", c.nativeInt
+
+  result = sealType(c.g, p) # ObjectDecl
+
+proc strPayloadType(c: var TypesCon): string =
+  result = "NimStrPayload"
+  let p = openType(c.g, ObjectDecl)
+  c.g.addName result
+  c.g.addField "cap", c.nativeInt
+
+  let f = c.g.openType FieldDecl
+  let arr = c.g.openType LastArrayTy
+  c.g.addBuiltinType Char8Id
+  discard sealType(c.g, arr) # LastArrayTy
+  c.g.addName "data"
+  discard sealType(c.g, f) # FieldDecl
+
+  discard 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 = sealType(c.g, ffp) # APtrTy
+
+proc stringToIr(c: var TypesCon): TypeId =
+  #[
+
+    NimStrPayload = object
+      cap: int
+      data: UncheckedArray[char]
+
+    NimStringV2 = object
+      len: int
+      p: ptr NimStrPayload
+
+  ]#
+  let payload = strPayloadType(c)
+
+  let str = openType(c.g, ObjectDecl)
+  c.g.addName "NimStringV2"
+  c.g.addField "len", c.nativeInt
+
+  let fp = c.g.openType FieldDecl
+  let ffp = c.g.openType APtrTy
+  c.g.addNominalType ObjectTy, "NimStrPayload"
+  discard sealType(c.g, ffp) # APtrTy
+  c.g.addName "p"
+  discard sealType(c.g, fp) # FieldDecl
+
+  result = sealType(c.g, str) # ObjectDecl
+
+proc seqPayloadType(c: var TypesCon; t: PType): string =
+  #[
+    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.addField "cap", c.nativeInt
+
+  let f = c.g.openType FieldDecl
+  let arr = c.g.openType LastArrayTy
+  c.g.addType elementType
+  discard sealType(c.g, arr) # LastArrayTy
+  c.g.addName "data"
+  discard sealType(c.g, f) # FieldDecl
+  discard 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 = sealType(c.g, ffp) # APtrTy
+
+proc seqToIr(c: var TypesCon; t: PType): TypeId =
+  #[
+    NimSeqV2*[T] = object
+      len: int
+      p: ptr NimSeqPayload[T]
+  ]#
+  let mangledBase = seqPayloadType(c, t)
+
+  let sq = openType(c.g, ObjectDecl)
+  c.g.addName "NimSeqV2" & mangledBase
+  c.g.addField "len", c.nativeInt
+
+  let fp = c.g.openType FieldDecl
+  let ffp = c.g.openType APtrTy
+  c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
+  discard sealType(c.g, ffp) # APtrTy
+  c.g.addName "p"
+  discard sealType(c.g, fp) # FieldDecl
+
+  result = sealType(c.g, sq) # ObjectDecl
+
+
+proc closureToIr(c: var TypesCon; t: PType): TypeId =
+  # struct {fn(args, void* env), env}
+  # typedef struct {$n" &
+  #        "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
+  #        "void* ClE_0;$n} $1;$n"
+  let mangledBase = mangle(c, t)
+  let typeName = "NimClosure" & mangledBase
+
+  let procType = procToIr(c, t, addEnv=true)
+
+  let p = openType(c.g, ObjectDecl)
+  c.g.addName typeName
+
+  let f = c.g.openType FieldDecl
+  c.g.addType procType
+  c.g.addName "ClP_0"
+  discard sealType(c.g, f) # FieldDecl
+
+  let f2 = c.g.openType FieldDecl
+  let voidPtr = openType(c.g, APtrTy)
+  c.g.addBuiltinType(VoidId)
+  discard sealType(c.g, voidPtr)
+
+  c.g.addName "ClE_0"
+  discard sealType(c.g, f2) # FieldDecl
+
+  result = sealType(c.g, p) # ObjectDecl
+
+proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId =
+  let s = int(getSize(c.conf, t))
+  case s
+  of 1: result = UInt8Id
+  of 2: result = UInt16Id
+  of 4: result = UInt32Id
+  of 8: result = UInt64Id
+  else: result = UInt8Id
+
+proc typeToIr*(c: var TypesCon; t: PType): TypeId =
+  if t == nil: return VoidId
+  case t.kind
+  of tyInt:
+    case int(getSize(c.conf, t))
+    of 2: result = Int16Id
+    of 4: result = Int32Id
+    else: result = Int64Id
+  of tyInt8: result = Int8Id
+  of tyInt16: result = Int16Id
+  of tyInt32: result = Int32Id
+  of tyInt64: result = Int64Id
+  of tyFloat:
+    case int(getSize(c.conf, t))
+    of 4: result = Float32Id
+    else: result = Float64Id
+  of tyFloat32: result = Float32Id
+  of tyFloat64: result = Float64Id
+  of tyFloat128: result = getFloat128Type(c.g)
+  of tyUInt:
+    case int(getSize(c.conf, t))
+    of 2: result = UInt16Id
+    of 4: result = UInt32Id
+    else: result = UInt64Id
+  of tyUInt8: result = UInt8Id
+  of tyUInt16: result = UInt16Id
+  of tyUInt32: result = UInt32Id
+  of tyUInt64: result = UInt64Id
+  of tyBool: result = Bool8Id
+  of tyChar: result = Char8Id
+  of tyVoid: result = VoidId
+  of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange:
+    result = typeToIr(c, t.lastSon)
+  of tyEnum:
+    if firstOrd(c.conf, t) < 0:
+      result = Int32Id
+    else:
+      case int(getSize(c.conf, t))
+      of 1: result = UInt8Id
+      of 2: result = UInt16Id
+      of 4: result = Int32Id
+      of 8: result = Int64Id
+      else: result = Int32Id
+  of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
+    if t.len > 0:
+      result = typeToIr(c, t.lastSon)
+    else:
+      result = TypeId(-1)
+  of tyFromExpr:
+    if t.n != nil and t.n.typ != nil:
+      result = typeToIr(c, 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 uint64(n)
+      result = sealType(c.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 = sealType(c.g, a)
+      else:
+        let elemType = typeToIr(c, t.lastSon)
+        let a = openType(c.g, APtrTy)
+        c.g.addType(elemType)
+        result = sealType(c.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)
+      else:
+        let elemType = typeToIr(c, e)
+        let a = openType(c.g, APtrTy)
+        c.g.addType(elemType)
+        result = sealType(c.g, a)
+  of tySet:
+    let s = int(getSize(c.conf, t))
+    case s
+    of 1: result = UInt8Id
+    of 2: result = UInt16Id
+    of 4: result = UInt32Id
+    of 8: result = UInt64Id
+    else:
+      # array[U8, s]
+      cached(c, t):
+        let a = openType(c.g, ArrayTy)
+        c.g.addType(UInt8Id)
+        c.g.addArrayLen uint64(s)
+        result = sealType(c.g, a)
+  of tyPointer:
+    let a = openType(c.g, APtrTy)
+    c.g.addBuiltinType(VoidId)
+    result = sealType(c.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)
+      else:
+        result = objectHeaderToIr(c, t)
+  of tyTuple:
+    cached(c, t):
+      result = tupleToIr(c, t)
+  of tyProc:
+    cached(c, t):
+      if t.callConv == ccClosure:
+        result = closureToIr(c, t)
+      else:
+        result = procToIr(c, t)
+  of tyVarargs, tyOpenArray:
+    cached(c, t):
+      result = openArrayToIr(c, t)
+  of tyString:
+    cached(c, t):
+      result = stringToIr(c)
+  of tySequence:
+    cached(c, t):
+      result = seqToIr(c, t)
+  of tyCstring:
+    cached(c, t):
+      let a = openType(c.g, AArrayPtrTy)
+      c.g.addBuiltinType Char8Id
+      result = sealType(c.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 = sealType(c.g, a)
+  of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
+     tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
+     tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
+     tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
+    result = TypeId(-1)
diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim
index 8517cd942..e9ee1ee8b 100644
--- a/compiler/pipelines.nim
+++ b/compiler/pipelines.nim
@@ -11,7 +11,7 @@ when not defined(leanCompiler):
 import std/[syncio, objectdollar, assertions, tables, strutils]
 import renderer
 import ic/replayer
-
+import nir/nir
 
 proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
   graph.pipelinePass = pass
@@ -43,6 +43,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
       result = nil
   of EvalPass, InterpreterPass:
     result = interpreterCode(bModule, semNode)
+  of NirReplPass:
+    result = runCode(bModule, semNode)
   of NonePass:
     raiseAssert "use setPipeLinePass to set a proper PipelinePass"
 
@@ -112,6 +114,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
         nil
     of EvalPass, InterpreterPass:
       setupEvalGen(graph, module, idgen)
+    of NirReplPass:
+      setupNirReplGen(graph, module, idgen)
     of GenDependPass:
       setupDependPass(graph, module, idgen)
     of Docgen2Pass:
@@ -197,6 +201,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
       discard finalJSCodeGen(graph, bModule, finalNode)
   of EvalPass, InterpreterPass:
     discard interpreterCode(bModule, finalNode)
+  of NirReplPass:
+    discard runCode(bModule, finalNode)
   of SemPass, GenDependPass:
     discard
   of Docgen2Pass, Docgen2TexPass:
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 2d366d8fc..ebc8be0c3 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -68,9 +68,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
     let assignment = initExpr[i]
     if assignment.kind != nkExprColonExpr:
       invalidObjConstr(c, assignment)
-      continue
-
-    if fieldId == considerQuotedIdent(c, assignment[0]).id:
+    elif fieldId == considerQuotedIdent(c, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
@@ -125,7 +123,7 @@ proc pickCaseBranch(caseExpr, matched: PNode): PNode =
       return caseExpr[i]
 
   if endsWithElse:
-    return caseExpr[^1]
+    result = caseExpr[^1]
   else:
     result = nil
 
@@ -138,8 +136,8 @@ iterator directFieldsInRecList(recList: PNode): PNode =
   else:
     doAssert recList.kind == nkRecList
     for field in recList:
-      if field.kind != nkSym: continue
-      yield field
+      if field.kind == nkSym:
+        yield field
 
 template quoteStr(s: string): string = "'" & s & "'"
 
@@ -416,8 +414,8 @@ proc semConstructTypeAux(c: PContext,
     if status in {initPartial, initNone, initUnknown}:
       discard collectMissingFields(c, t.n, constrCtx, result.defaults)
     let base = t[0]
-    if base == nil or base.id == t.id or 
-      base.kind in { tyRef, tyPtr } and base[0].id == t.id: 
+    if base == nil or base.id == t.id or
+      base.kind in {tyRef, tyPtr} and base[0].id == t.id:
         break
     t = skipTypes(base, skipPtrs)
     if t.kind != tyObject:
@@ -463,7 +461,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
 
   if t == nil:
     return localErrorNode(c, result, "object constructor needs an object type")
-  
+
   if t.skipTypes({tyGenericInst,
       tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
       expectedType != nil and expectedType.skipTypes({tyGenericInst,
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index d14f19a8a..41ec3e480 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -25,7 +25,7 @@ import
   ast, astalgo, idents, lowerings, magicsys, guards, msgs,
   renderer, types, modulegraphs, options, spawn, lineinfos
 
-from trees import getMagic, isTrue, getRoot
+from trees import getMagic, getRoot
 from strutils import `%`
 
 discard """
diff --git a/compiler/trees.nim b/compiler/trees.nim
index cc2b0eafd..f038fbc1e 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -198,10 +198,6 @@ proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
   result = newNodeI(k, n.info, b-a+1)
   for i in 0..b-a: result[i] = n[i+a]
 
-proc isTrue*(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
 proc getRoot*(n: PNode): PSym =
   ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
   ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 579bab600..85c1305e9 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1521,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         frame = frame.next
         jumpTo = findExceptionHandler(c, frame, raised)
 
-      case jumpTo.why:
+      case jumpTo.why
       of ExceptionGotoHandler:
         # Jump to the handler, do nothing when the `finally` block ends.
         savedPC = -1
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 03265f5b5..42ce09596 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -321,10 +321,6 @@ proc isNotOpr(n: PNode): bool =
   n.kind in nkCallKinds and n[0].kind == nkSym and
     n[0].sym.magic == mNot
 
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
 proc genWhile(c: PCtx; n: PNode) =
   # lab1:
   #   cond, tmp
diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim
index 48973aa55..872549c3b 100644
--- a/lib/std/formatfloat.nim
+++ b/lib/std/formatfloat.nim
@@ -100,22 +100,23 @@ proc addFloatSprintf*(result: var string; x: float) =
     let n = writeFloatToBufferSprintf(buffer, x)
     result.addCstringN(cast[cstring](buffer[0].addr), n)
 
-proc nimFloatToString(a: float): cstring =
-  ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
-  # print `-0.0` properly
-  asm """
-    function nimOnlyDigitsOrMinus(n) {
-      return n.toString().match(/^-?\d+$/);
-    }
-    if (Number.isSafeInteger(`a`))
-      `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
-    else {
-      `result` = `a`+""
-      if(nimOnlyDigitsOrMinus(`result`)){
-        `result` = `a`+".0"
+when defined(js):
+  proc nimFloatToString(a: float): cstring =
+    ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
+    # print `-0.0` properly
+    asm """
+      function nimOnlyDigitsOrMinus(n) {
+        return n.toString().match(/^-?\d+$/);
       }
-    }
-  """
+      if (Number.isSafeInteger(`a`))
+        `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
+      else {
+        `result` = `a`+""
+        if(nimOnlyDigitsOrMinus(`result`)){
+          `result` = `a`+".0"
+        }
+      }
+    """
 
 proc addFloat*(result: var string; x: float | float32) {.inline.} =
   ## Converts float to its string representation and appends it to `result`.
diff --git a/lib/system.nim b/lib/system.nim
index 3e7d84b1d..8d1cda868 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1117,8 +1117,6 @@ template sysAssert(cond: bool, msg: string) =
 
 const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
 
-when notJSnotNims and hostOS != "standalone" and hostOS != "any":
-  include "system/cgprocs"
 when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
   proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
 
@@ -1431,7 +1429,6 @@ proc isNil*[T: proc | iterator {.closure.}](x: T): bool {.noSideEffect, magic: "
   ## Fast check whether `x` is nil. This is sometimes more efficient than
   ## `== nil`.
 
-
 when defined(nimHasTopDownInference):
   # magic used for seq type inference
   proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} =
@@ -2200,6 +2197,16 @@ when not defined(js):
 
 when notJSnotNims:
   when hostOS != "standalone" and hostOS != "any":
+    type
+      LibHandle = pointer       # private type
+      ProcAddr = pointer        # library loading and loading of procs:
+
+    proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.}
+    proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.}
+    proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.}
+
+    proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.}
+
     include "system/dyncalls"
 
   import system/countbits_impl
@@ -2673,7 +2680,7 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
   proc strcmp(a, b: cstring): cint {.noSideEffect,
     importc, header: "<string.h>".}
   if pointer(x) == pointer(y): result = true
-  elif x.isNil or y.isNil: result = false
+  elif pointer(x) == nil or pointer(y) == nil: result = false
   else: result = strcmp(x, y) == 0
 
 template closureScope*(body: untyped): untyped =
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index a8a4bd767..be931ed14 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -224,7 +224,7 @@ proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonRe
 
 proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} =
   # we cannot throw an exception here!
-  discard c_fwrite(s, 1, cast[csize_t](s.len), f)
+  discard c_fwrite(s, 1, c_strlen(s), f)
   discard c_fflush(f)
 
 {.pop.}
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index 9d0d248c3..9a7645f9b 100644
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -8,13 +8,3 @@
 #
 
 # Headers for procs that the code generator depends on ("compilerprocs")
-
-type
-  LibHandle = pointer       # private type
-  ProcAddr = pointer        # library loading and loading of procs:
-
-proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.}
-proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.}
-proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.}
-
-proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.}
diff --git a/lib/system/memory.nim b/lib/system/memory.nim
index ebda60d8d..156773c48 100644
--- a/lib/system/memory.nim
+++ b/lib/system/memory.nim
@@ -43,6 +43,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl
       inc i
 
 proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} =
+  if a.isNil: return 0
   when useLibC:
     cast[int](c_strlen(a))
   else:
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 60c63501c..89046253b 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -23,6 +23,14 @@ proc cmpStrings(a, b: string): int {.inline, compilerproc.} =
   else:
     result = alen - blen
 
+proc leStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) <= 0
+
+proc ltStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) < 0
+
 proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
   let alen = a.len
   let blen = b.len
diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim
index abdbcd7c3..e79a2b324 100644
--- a/lib/system/strs_v2.nim
+++ b/lib/system/strs_v2.nim
@@ -201,6 +201,10 @@ proc prepareMutation*(s: var string) {.inline.} =
     let s = unsafeAddr s
     nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[])
 
+proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} =
+  #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
+  prepareAdd(s, src.len)
+  appendString s, src
 
 func capacity*(self: string): int {.inline.} =
   ## Returns the current capacity of the string.