summary refs log tree commit diff stats
path: root/compiler/nir
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 /compiler/nir
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.
Diffstat (limited to 'compiler/nir')
-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
8 files changed, 3752 insertions, 0 deletions
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)