summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/extccomp.nim2
-rw-r--r--compiler/ic/rodfiles.nim7
-rw-r--r--compiler/main.nim26
-rw-r--r--compiler/modulegraphs.nim1
-rw-r--r--compiler/nim.nim2
-rw-r--r--compiler/nir/ast2ir.nim592
-rw-r--r--compiler/nir/nir.nim66
-rw-r--r--compiler/nir/nirc.nim48
-rw-r--r--compiler/nir/nirinsts.nim32
-rw-r--r--compiler/nir/nirlineinfos.nim7
-rw-r--r--compiler/nir/nirslots.nim2
-rw-r--r--compiler/nir/nirtypes.nim143
-rw-r--r--compiler/nir/nirvm.nim467
-rw-r--r--compiler/nir/stringcases.nim200
-rw-r--r--compiler/nir/types2ir.nim139
-rw-r--r--compiler/options.nim13
-rw-r--r--compiler/pipelines.nim6
-rw-r--r--compiler/sizealignoffsetimpl.nim12
-rw-r--r--lib/system/seqs_v2.nim2
-rw-r--r--lib/system/strs_v2.nim3
22 files changed, 1389 insertions, 387 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 3017aedcf..8d4511436 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -924,7 +924,7 @@ type
                               # for variables a slot index for the evaluator
     offset*: int32            # offset of record field
     disamb*: int32            # disambiguation number; the basic idea is that
-                              # `<procname>__<module>_<disamb>`
+                              # `<procname>__<module>_<disamb>` is unique
     loc*: TLoc
     annex*: PLib              # additional fields (seldom used, so we use a
                               # reference to another object to save space)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index f36d82306..0e35cc3e8 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -460,6 +460,7 @@ proc handleCmdInput*(conf: ConfigRef) =
 proc parseCommand*(command: string): Command =
   case command.normalize
   of "c", "cc", "compile", "compiletoc": cmdCompileToC
+  of "nir": cmdCompileToNir
   of "cpp", "compiletocpp": cmdCompileToCpp
   of "objc", "compiletooc": cmdCompileToOC
   of "js", "compiletojs": cmdCompileToJS
@@ -496,6 +497,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
   of cmdCompileToCpp: conf.backend = backendCpp
   of cmdCompileToOC: conf.backend = backendObjc
   of cmdCompileToJS: conf.backend = backendJs
+  of cmdCompileToNir: conf.backend = backendNir
   else: discard
 
 proc setCommandEarly*(conf: ConfigRef, command: string) =
@@ -794,7 +796,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
     else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
     #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
-  of "tlsemulation": 
+  of "tlsemulation":
     processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
     if optTlsEmulation in conf.globalOptions:
       conf.legacyFeatures.incl emitGenerics
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 75d462b06..3deab0b74 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -319,7 +319,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   var fullSuffix = suffix
   case conf.backend
   of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
-  of backendC: discard
+  of backendC, backendNir: discard
   of backendInvalid:
     # during parsing of cfg files; we don't know the backend yet, no point in
     # guessing wrong thing
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index 41e85084f..4968c5924 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -97,6 +97,7 @@ type
     typeInfoSection  # required by the backend
     backendFlagsSection
     aliveSymsSection # beware, this is stored in a `.alivesyms` file.
+    sideChannelSection
 
   RodFileError* = enum
     ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@@ -110,7 +111,7 @@ type
 
 const
   RodVersion = 1
-  cookie = [byte(0), byte('R'), byte('O'), byte('D'),
+  defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
             byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
 
 proc setError(f: var RodFile; err: RodFileError) {.inline.} =
@@ -206,13 +207,13 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
     for i in 0..<lenPrefix:
       loadPrim(f, s[i])
 
-proc storeHeader*(f: var RodFile) =
+proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
   ## stores the header which is described by `cookie`.
   if f.err != ok: return
   if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
     setError f, ioFailure
 
-proc loadHeader*(f: var RodFile) =
+proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
   ## Loads the header which is described by `cookie`.
   if f.err != ok: return
   var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
diff --git a/compiler/main.nim b/compiler/main.nim
index e5a5be56c..0627a61bb 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -49,6 +49,9 @@ proc writeDepsFile(g: ModuleGraph) =
       f.writeLine(toFullPath(g.config, k))
   f.close()
 
+proc writeNinjaFile(g: ModuleGraph) =
+  discard "to implement"
+
 proc writeCMakeDepsFile(conf: ConfigRef) =
   ## write a list of C files for build systems like CMake.
   ## only updated when the C file list changes.
@@ -159,6 +162,26 @@ proc commandCompileToC(graph: ModuleGraph) =
     if optGenCDeps in graph.config.globalOptions:
       writeCMakeDepsFile(conf)
 
+proc commandCompileToNir(graph: ModuleGraph) =
+  let conf = graph.config
+  extccomp.initVars(conf)
+  if conf.symbolFiles == disabledSf:
+    if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
+      if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
+        # nothing changed
+        graph.config.notes = graph.config.mainPackageNotes
+        return
+
+  if not extccomp.ccHasSaneOverflow(conf):
+    conf.symbols.defineSymbol("nimEmulateOverflowChecks")
+
+  if conf.symbolFiles == disabledSf:
+    setPipeLinePass(graph, NirPass)
+  else:
+    setPipeLinePass(graph, SemPass)
+  compilePipelineProject(graph)
+  writeNinjaFile(graph)
+
 proc commandJsonScript(graph: ModuleGraph) =
   extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
 
@@ -270,6 +293,8 @@ proc mainCommand*(graph: ModuleGraph) =
         # and it has added this define implictly, so we must undo that here.
         # A better solution might be to fix system.nim
         undefSymbol(conf.symbols, "useNimRtl")
+    of backendNir:
+      if conf.exc == excNone: conf.exc = excGoto
     of backendInvalid: raiseAssert "unreachable"
 
   proc compileToBackend() =
@@ -280,6 +305,7 @@ proc mainCommand*(graph: ModuleGraph) =
     of backendCpp: commandCompileToC(graph)
     of backendObjc: commandCompileToC(graph)
     of backendJs: commandCompileToJS(graph)
+    of backendNir: commandCompileToNir(graph)
     of backendInvalid: raiseAssert "unreachable"
 
   template docLikeCmd(body) =
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index f6abb0a60..c450af50f 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -65,6 +65,7 @@ type
     CgenPass
     EvalPass
     InterpreterPass
+    NirPass
     NirReplPass
     GenDependPass
     Docgen2TexPass
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 023a76ff9..7ec6f3e77 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -116,7 +116,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     conf.backend = backendC
 
   if conf.selectedGC == gcUnselected:
-    if conf.backend in {backendC, backendCpp, backendObjc} or
+    if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
         (conf.cmd == cmdInteractive and isDefined(conf, "nir")):
       initOrcDefines(conf)
 
diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim
index fcda145ea..fd3a4df09 100644
--- a/compiler/nir/ast2ir.nim
+++ b/compiler/nir/ast2ir.nim
@@ -9,7 +9,7 @@
 
 import std / [assertions, tables, sets]
 import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys,
-  modulegraphs, guards, renderer, transf, bitsets, trees, nimsets,
+  modulegraphs, renderer, transf, bitsets, trees, nimsets,
   expanddefaults]
 from ".." / lowerings import lowerSwap, lowerTupleUnpacking
 from ".." / pathutils import customPath
@@ -22,9 +22,8 @@ when defined(nimCompilerStacktraceHints):
 
 type
   ModuleCon* = ref object
-    strings*: BiTable[string]
-    integers*: BiTable[int64]
     man*: LineInfoManager
+    lit*: Literals
     types*: TypesCon
     slotGenerator: ref int
     module*: PSym
@@ -34,7 +33,8 @@ type
     pendingProcs: Table[ItemId, PSym] # procs we still need to generate code for
 
   ProcCon* = object
-    config: ConfigRef
+    config*: ConfigRef
+    lit: Literals
     lastFileKey: FileIndex
     lastFileVal: LitId
     labelGen: int
@@ -48,8 +48,11 @@ type
     options: TOptions
 
 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)
+  let lit = Literals() # must be shared
+  var g = new(int)
+  g[] = idgen.symId + 1
+  result = ModuleCon(graph: graph, types: initTypesCon(config, lit), slotGenerator: g,
+    idgen: idgen, module: module, lit: lit)
   case config.target.intSize
   of 2:
     result.nativeIntId = Int16Id
@@ -63,6 +66,7 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m
 
 proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
   ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config,
+    lit: m.lit,
     options: if prc != nil: prc.options
              else: config.options)
 
@@ -71,7 +75,7 @@ proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo =
   if c.lastFileKey == i.fileIndex:
     val = c.lastFileVal
   else:
-    val = c.m.strings.getOrIncl(toFullPath(c.config, i.fileIndex))
+    val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex))
     # remember the entry:
     c.lastFileKey = i.fileIndex
     c.lastFileVal = val
@@ -117,6 +121,11 @@ proc getTemp(c: var ProcCon; n: PNode): Value =
   c.code.addSummon info, tmp, t
   result = localToValue(info, tmp)
 
+proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
+  let tmp = allocTemp(c.sm, t)
+  c.code.addSummon info, tmp, t
+  result = localToValue(info, tmp)
+
 template withTemp(tmp, n, body: untyped) {.dirty.} =
   var tmp = getTemp(c, n)
   body
@@ -306,11 +315,58 @@ proc caseRange(c: var ProcCon; n: PNode) =
     freeTemp(c, y)
     freeTemp(c, x)
 
+proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) =
+  let cp = getCompilerProc(c.m.graph, name)
+  let theProc = c.genx newSymNode(cp)
+  copyTree c.code, theProc
+
+template buildCond(useNegation: bool; cond: typed; body: untyped) =
+  let lab = newLabel(c.labelGen)
+  #let info = toLineInfo(c, n.info)
+  buildTyped c.code, info, Select, Bool8Id:
+    c.code.copyTree cond
+    build c.code, info, SelectPair:
+      build c.code, info, SelectValue:
+        c.code.boolVal(info, useNegation)
+      c.code.gotoLabel info, Goto, lab
+
+  body
+  c.code.addLabel info, Label, lab
+
+template buildIf(cond: typed; body: untyped) =
+  buildCond false, cond, body
+
+template buildIfNot(cond: typed; body: untyped) =
+  buildCond true, cond, body
+
+template buildIfThenElse(cond: typed; then, otherwise: untyped) =
+  let lelse = newLabel(c.labelGen)
+  let lend = newLabel(c.labelGen)
+  buildTyped c.code, info, Select, Bool8Id:
+    c.code.copyTree cond
+    build c.code, info, SelectPair:
+      build c.code, info, SelectValue:
+        c.code.boolVal(info, false)
+      c.code.gotoLabel info, Goto, lelse
+
+  then()
+  c.code.gotoLabel info, Goto, lend
+  c.code.addLabel info, Label, lelse
+  otherwise()
+  c.code.addLabel info, Label, lend
+
+include stringcases
+
 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)
+
+  if n[0].typ.skipTypes(abstractInst).kind == tyString:
+    genStringCase(c, n, d)
+    return
+
   var sections = newSeqOfCap[LabelId](n.len-1)
   let ending = newLabel(c.labelGen)
   let info = toLineInfo(c, n.info)
@@ -481,10 +537,11 @@ proc genField(c: var ProcCon; n: PNode; d: var Value) =
 proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
   let info = toLineInfo(c, n.info)
   if arr.skipTypes(abstractInst).kind == tyArray and
-      (let x = firstOrd(c.config, arr); x != Zero):
+      (let offset = firstOrd(c.config, arr); offset != Zero):
+    let x = c.genx(n)
     buildTyped d, info, Sub, c.m.nativeIntId:
-      c.gen(n, d)
-      d.addImmediateVal toLineInfo(c, n.info), toInt(x)
+      copyTree d.Tree, x
+      d.addImmediateVal toLineInfo(c, n.info), toInt(offset)
   else:
     c.gen(n, d)
   if optBoundsCheck in c.options:
@@ -492,7 +549,7 @@ proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
     build d, info, CheckedIndex:
       copyTree d.Tree, idx
       let x = toInt64 lengthOrd(c.config, arr)
-      d.Tree.addIntVal c.m.integers, info, c.m.nativeIntId, x
+      d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
       d.Tree.addLabel info, CheckedGoto, c.exitLabel
 
 proc genNew(c: var ProcCon; n: PNode; needsInit: bool) =
@@ -545,12 +602,10 @@ proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
         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)
+proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) =
   let baseType = seqtype.lastSon
-  var d = c.genx(n[1])
-  var b = c.genx(n[2])
+  # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
+  let payloadPtr = seqPayloadPtrType(c.m.types, seqtype)
 
   # $1.len = $2
   buildTyped c.code, info, Asgn, c.m.nativeIntId:
@@ -558,10 +613,7 @@ proc genNewSeq(c: var ProcCon; n: PNode) =
       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:
@@ -571,11 +623,20 @@ proc genNewSeq(c: var ProcCon; n: PNode) =
     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)
+        let theProc = c.genx newSymNode(codegenProc)
         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))
+
+proc genNewSeq(c: var ProcCon; n: PNode) =
+  let info = toLineInfo(c, n.info)
+  let seqtype = skipTypes(n[1].typ, abstractVarRange)
+  var d = c.genx(n[1])
+  var b = c.genx(n[2])
+
+  genNewSeqPayload(c, info, d, b, seqtype)
+
   freeTemp c, b
   freeTemp c, d
 
@@ -589,6 +650,14 @@ template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untype
       copyTree c.code, d
       body(c.code)
 
+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 genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
   let info = toLineInfo(c, n.info)
   let tmp = c.genx(n[1])
@@ -683,7 +752,7 @@ proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
 
   of tyArray:
     template body(target) =
-      target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
+      target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
     intoDest d, info, c.m.nativeIntId, body
   else: internalError(c.config, n.info, "genArrayLen()")
 
@@ -694,7 +763,7 @@ proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) =
   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)
+      target.addIntVal(c.lit.numbers, info, t, 0)
       copyTree target, tmp
   intoDest d, info, t, body
   c.freeTemp(tmp)
@@ -707,7 +776,7 @@ proc genHigh(c: var ProcCon; n: PNode; d: var Value) =
   template body(target) =
     buildTyped target, info, Sub, t:
       copyTree target, x
-      target.addIntVal(c.m.integers, info, t, 1)
+      target.addIntVal(c.lit.numbers, info, t, 1)
   intoDest d, info, t, body
   c.freeTemp x
 
@@ -813,15 +882,15 @@ proc genInBitset(c: var ProcCon; n: PNode; d: var Value) =
               buildTyped target, info, BitShr, t:
                 buildTyped target, info, Cast, expansion:
                   copyTree target, b
-                addIntVal target, c.m.integers, info, expansion, 3
+                addIntVal target, c.lit.numbers, info, expansion, 3
 
           buildTyped target, info, BitShl, t:
-            addIntVal target, c.m.integers, info, t, 1
+            addIntVal target, c.lit.numbers, 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
+              addIntVal target, c.lit.numbers, info, expansion, mask
+        addIntVal target, c.lit.numbers, info, t, 0
   intoDest d, info, t, body
 
   c.freeTemp(b)
@@ -916,7 +985,7 @@ proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
           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
+        c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
 
   else:
     template body(target) =
@@ -933,14 +1002,14 @@ proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (Sy
   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
+    c.code.addIntVal c.lit.numbers, 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
+      c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last
     build c.code, info, SelectPair:
       build c.code, info, SelectValue:
         c.code.boolVal(info, false)
@@ -969,7 +1038,7 @@ proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId
     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.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
   c.code.addLabel info, GotoLoop, back
   c.code.addLabel info, Label, exit
   freeTemp(c.sm, s)
@@ -1000,7 +1069,7 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
             buildTyped c.code, info, ArrayAt, elemType:
               copyTree c.code, b
               c.code.addSymUse info, idx
-        c.code.addIntVal c.m.integers, info, elemType, 0
+        c.code.addIntVal c.lit.numbers, info, elemType, 0
 
     # if !$3: break
     buildTyped c.code, info, Select, Bool8Id:
@@ -1019,7 +1088,7 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
           copyTree target, a
           buildTyped target, info, BitNot, setType:
             copyTree target, b
-        target.addIntVal c.m.integers, info, setType, 0
+        target.addIntVal c.lit.numbers, info, setType, 0
 
     intoDest d, info, Bool8Id, body
 
@@ -1103,19 +1172,19 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
           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
+            addIntVal c.code, c.lit.numbers, 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
+              addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
           buildTyped c.code, info, BitShl, t:
-            c.code.addIntVal c.m.integers, info, t, 1
+            c.code.addIntVal c.lit.numbers, info, t, 1
             buildTyped c.code, info, BitAnd, t:
               copyTree c.code, b
-              c.code.addIntVal c.m.integers, info, t, 7
+              c.code.addIntVal c.lit.numbers, info, t, 7
       else:
         # $1[(NU)($2)>>3] &= ~(1U<<($2&7U))
         buildTyped c.code, info, ArrayAt, t:
@@ -1123,20 +1192,20 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
           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
+            addIntVal c.code, c.lit.numbers, 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
+              addIntVal c.code, c.lit.numbers, 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
+              c.code.addIntVal c.lit.numbers, info, t, 1
               buildTyped c.code, info, BitAnd, t:
                 copyTree c.code, b
-                c.code.addIntVal c.m.integers, info, t, 7
+                c.code.addIntVal c.lit.numbers, info, t, 7
 
     else:
       copyTree c.code, a
@@ -1145,20 +1214,20 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
         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
+            c.code.addIntVal c.lit.numbers, info, t, 1
             buildTyped c.code, info, BitAnd, t:
               copyTree c.code, b
-              c.code.addIntVal c.m.integers, info, t, mask
+              c.code.addIntVal c.lit.numbers, 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
+              c.code.addIntVal c.lit.numbers, info, t, 1
               buildTyped c.code, info, BitAnd, t:
                 copyTree c.code, b
-                c.code.addIntVal c.m.integers, info, t, mask
+                c.code.addIntVal c.lit.numbers, info, t, mask
   freeTemp c, b
   freeTemp c, a
 
@@ -1182,7 +1251,7 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
   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
+      c.code.addIntVal c.lit.numbers, info, t, 0
 
     for it in n:
       if it.kind == nkRange:
@@ -1195,10 +1264,10 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
             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
+                c.code.addIntVal c.lit.numbers, info, t, 1
                 buildTyped c.code, info, BitAnd, t:
                   c.code.addSymUse info, idx
-                  c.code.addIntVal c.m.integers, info, t, mask
+                  c.code.addIntVal c.lit.numbers, info, t, mask
 
         endLoop(c, info, idx, backLabel, endLabel)
         freeTemp c, b
@@ -1212,10 +1281,10 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
             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
+                c.code.addIntVal c.lit.numbers, info, t, 1
                 buildTyped c.code, info, BitAnd, t:
                   copyTree c.code, a
-                  c.code.addIntVal c.m.integers, info, t, mask
+                  c.code.addIntVal c.lit.numbers, info, t, mask
         freeTemp c, a
 
   else:
@@ -1223,7 +1292,7 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
     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
+      c.code.addIntVal c.lit.numbers, info, t, 0
     endLoop(c, info, idx, backLabel, endLabel)
 
     # incl elements:
@@ -1239,19 +1308,19 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
             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
+              addIntVal c.code, c.lit.numbers, 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
+                addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
             buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.m.integers, info, t, 1
+              c.code.addIntVal c.lit.numbers, info, t, 1
               buildTyped c.code, info, BitAnd, t:
                 c.code.addSymUse info, idx
-                c.code.addIntVal c.m.integers, info, t, 7
+                c.code.addIntVal c.lit.numbers, info, t, 7
 
         endLoop(c, info, idx, backLabel, endLabel)
         freeTemp c, b
@@ -1266,19 +1335,19 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
             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
+              addIntVal c.code, c.lit.numbers, 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
+                addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
             buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.m.integers, info, t, 1
+              c.code.addIntVal c.lit.numbers, info, t, 1
               buildTyped c.code, info, BitAnd, t:
                 copyTree c.code, a
-                c.code.addIntVal c.m.integers, info, t, 7
+                c.code.addIntVal c.lit.numbers, info, t, 7
         freeTemp c, a
 
 proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) =
@@ -1290,14 +1359,14 @@ proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) =
 
     if c.m.types.g[setType].kind != ArrayTy:
       template body(target) =
-        target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
+        target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
       intoDest d, info, setType, body
     else:
       let t = bitsetBasetype(c.m.types, n.typ)
       template body(target) =
         buildTyped target, info, ArrayConstr, setType:
           for i in 0..high(cs):
-            target.addIntVal c.m.integers, info, t, int64 cs[i]
+            target.addIntVal c.lit.numbers, info, t, int64 cs[i]
       intoDest d, info, setType, body
   else:
     genSetConstrDyn c, n, d
@@ -1334,7 +1403,7 @@ proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
   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
+    c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen
   for a in mitems(args):
     buildTyped c.code, info, Asgn, c.m.nativeIntId:
       c.code.addSymUse info, tmpLen
@@ -1441,6 +1510,57 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) =
         buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, typeToIr(c.m.types, n1.typ)):
           copyTree c.code, a
 
+template fieldAt(x: Value; i: int; t: TypeId): Tree =
+  var result = default(Tree)
+  buildTyped result, info, FieldAt, t:
+    copyTree result, x
+    result.addImmediateVal info, i
+  result
+
+template eqNil(x: Tree; t: TypeId): Tree =
+  var result = default(Tree)
+  buildTyped result, info, Eq, t:
+    copyTree result, x
+    result.addNilVal info, t
+  result
+
+template eqZero(x: Tree): Tree =
+  var result = default(Tree)
+  buildTyped result, info, Eq, c.m.nativeIntId:
+    copyTree result, x
+    result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
+  result
+
+template bitOp(x: Tree; opc: Opcode; y: int): Tree =
+  var result = default(Tree)
+  buildTyped result, info, opc, c.m.nativeIntId:
+    copyTree result, x
+    result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y
+  result
+
+proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) =
+  let info = toLineInfo(c, n.info)
+  let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG
+
+  let x = c.genx(n[1])
+  let baseType = t.lastSon
+
+  let seqType = seqPayloadPtrType(c.m.types, t)
+  let p = fieldAt(x, 0, seqType)
+
+  # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0:
+  #   alignedDealloc($1.p, NIM_ALIGNOF($2))
+  buildIfNot p.eqNil(seqType):
+    buildIf fieldAt(Value(p), 0, c.m.nativeIntId).bitOp(BitAnd, 0).eqZero():
+      let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc")
+      buildTyped c.code, info, Call, VoidId:
+        let theProc = c.genx newSymNode(codegenProc, n.info)
+        copyTree c.code, theProc
+        copyTree c.code, p
+        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
+
+  freeTemp c, x
+
 proc genDestroy(c: var ProcCon; n: PNode) =
   let t = n[1].typ.skipTypes(abstractInst)
   case t.kind
@@ -1448,16 +1568,110 @@ proc genDestroy(c: var ProcCon; n: PNode) =
     var unused = default(Value)
     genUnaryCp(c, n, unused, "nimDestroyStrV1")
   of tySequence:
-    #[
-    var a = initLocExpr(c, arg)
-    linefmt(c, cpsStmts, "if ($1.p && ($1.p->cap & NIM_STRLIT_FLAG) == 0) {$n" &
-      " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" &
-      "}$n",
-      [rdLoc(a), getTypeDesc(c.module, t.lastSon)])
-    ]#
-    globalError(c.config, n.info, "not implemented: =destroy for seqs")
+    genDestroySeq(c, n, t)
   else: discard "nothing to do"
 
+type
+  IndexFor = enum
+    ForSeq, ForStr, ForOpenArray, ForArray
+
+proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType = nil): Value =
+  if optBoundsCheck in c.options:
+    let info = toLineInfo(c, n.info)
+    result = default(Value)
+    let idx = genx(c, n)
+    build result, info, CheckedIndex:
+      copyTree result.Tree, idx
+      case kind
+      of ForSeq, ForStr:
+        buildTyped result, info, FieldAt, c.m.nativeIntId:
+          copyTree result.Tree, a
+          result.addImmediateVal info, 0 # (len, p)-pair
+      of ForOpenArray:
+        buildTyped result, info, FieldAt, c.m.nativeIntId:
+          copyTree result.Tree, a
+          result.addImmediateVal info, 1 # (p, len)-pair
+      of ForArray:
+        let x = toInt64 lengthOrd(c.config, arr)
+        result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
+      result.Tree.addLabel info, CheckedGoto, c.exitLabel
+    freeTemp c, idx
+  else:
+    result = genx(c, n)
+
+proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
+                    x: Value; n: PNode; arrType: PType) =
+  let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon))
+  case arrType.kind
+  of tyString, tySequence:
+    let t = typeToIr(c.m.types, arrType.lastSon)
+    let checkKind = if arrType.kind == tyString: ForStr else: ForSeq
+    let pay = if checkKind == ForStr: strPayloadPtrType(c.m.types)
+              else: seqPayloadPtrType(c.m.types, arrType)
+
+    let y = genIndexCheck(c, n[2], x, checkKind)
+    let z = genIndexCheck(c, n[3], x, checkKind)
+
+    buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ):
+      target.addImmediateVal info, 0
+      buildTyped target, info, AddrOf, elemType:
+        buildTyped target, info, ArrayAt, t:
+          buildTyped target, info, FieldAt, pay:
+            copyTree target, x
+            target.addImmediateVal info, 1 # (len, p)-pair
+          copyTree target, y
+
+      # len:
+      target.addImmediateVal info, 1
+      buildTyped target, info, Add, c.m.nativeIntId:
+        buildTyped target, info, Sub, c.m.nativeIntId:
+          copyTree target, z
+          copyTree target, y
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
+
+    freeTemp c, z
+    freeTemp c, y
+  of tyArray, tyOpenArray:
+    let t = typeToIr(c.m.types, arrType.lastSon)
+    # XXX This evaluates the index check for `y` twice.
+    # This check is also still insufficient for non-zero based arrays.
+    let checkKind = if arrType.kind == tyArray: ForArray else: ForOpenArray
+
+    let y = genIndexCheck(c, n[2], x, checkKind, arrType)
+    let z = genIndexCheck(c, n[3], x, checkKind, arrType)
+
+    buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ):
+      target.addImmediateVal info, 0
+      buildTyped target, info, AddrOf, elemType:
+        buildTyped target, info, ArrayAt, t:
+          copyTree target, x
+          copyTree target, y
+
+      target.addImmediateVal info, 1
+      buildTyped target, info, Add, c.m.nativeIntId:
+        buildTyped target, info, Sub, c.m.nativeIntId:
+          copyTree target, z
+          copyTree target, y
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
+
+    freeTemp c, z
+    freeTemp c, y
+  else:
+    raiseAssert "addSliceFields: " & typeToString(arrType)
+
+proc genSlice(c: var ProcCon; n: PNode; d: var Value) =
+  let info = toLineInfo(c, n.info)
+
+  let x = c.genx(n[1])
+
+  let arrType = n[1].typ.skipTypes(abstractVar)
+
+  template body(target) =
+    c.addSliceFields target, info, x, n, arrType
+
+  valueIntoDest c, info, d, arrType, body
+  freeTemp c, x
+
 proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, d)
@@ -1470,7 +1684,7 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
   of mDec:
     unused(c, n, d)
     c.genIncDec(n, CheckedSub)
-  of mOrd, mChr, mArrToSeq, mUnown:
+  of mOrd, mChr, mUnown:
     c.gen(n[1], d)
   of generatedMagics:
     genCall(c, n, d)
@@ -1621,154 +1835,12 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
   of mDestroy: genDestroy(c, n)
   #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0")
   #of mAccessTypeField: genAccessTypeField(c, n, d)
-  #of mSlice: genSlice(c, n, d)
+  of mSlice: genSlice(c, n, d)
   of mTrace: discard "no code to generate"
   else:
     # mGCref, mGCunref: unused by ORC
     globalError(c.config, n.info, "cannot generate code for: " & $m)
 
-#[
-
-  of mRepr: genUnaryABC(c, n, d, opcRepr)
-
-  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
@@ -1798,14 +1870,6 @@ proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
       # 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)
@@ -1842,7 +1906,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo;
         buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types):
           copyTree target, tmp
           target.addImmediateVal info, 1 # (len, p)-pair
-        target.addIntVal c.m.integers, info, c.m.nativeIntId, 0
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     # len:
     target.addImmediateVal info, 1
     buildTyped target, info, FieldAt, c.m.nativeIntId:
@@ -1857,7 +1921,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo;
         buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, typ):
           copyTree target, tmp
           target.addImmediateVal info, 1 # (len, p)-pair
-        target.addIntVal c.m.integers, info, c.m.nativeIntId, 0
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     # len:
     target.addImmediateVal info, 1
     buildTyped target, info, FieldAt, c.m.nativeIntId:
@@ -1870,9 +1934,9 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo;
     buildTyped target, info, AddrOf, elemType:
       buildTyped target, info, ArrayAt, t:
         copyTree target, tmp
-        target.addIntVal c.m.integers, info, c.m.nativeIntId, 0
+        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
     target.addImmediateVal info, 1
-    target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
+    target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
   else:
     raiseAssert "addAddrOfFirstElem: " & typeToString(typ)
 
@@ -1933,10 +1997,32 @@ proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) =
 
   valueIntoDest c, info, d, n.typ, body
 
+proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) =
+  if isEmpty(d): d = getTemp(c, n)
+
+  let info = toLineInfo(c, n.info)
+  let seqtype = skipTypes(n.typ, abstractVarRange)
+  let baseType = seqtype.lastSon
+
+  var b = default(Value)
+  b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len
+
+  genNewSeqPayload(c, info, d, b, seqtype)
+
+  for i in 0..<n.len:
+    var dd = default(Value)
+    buildTyped dd, info, ArrayAt, typeToIr(c.m.types, seqtype):
+      buildTyped dd, info, FieldAt, seqPayloadPtrType(c.m.types, seqtype):
+        copyTree Tree(dd), d
+        dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i
+    gen(c, n[i], dd)
+
+  freeTemp c, d
+
 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"
+    genSeqConstr(c, n, d)
     return
 
   let info = toLineInfo(c, n.info)
@@ -1973,7 +2059,9 @@ proc genVarSection(c: var ProcCon; n: PNode) =
           opc = SummonGlobal
         else:
           opc = Summon
-        c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), opc
+        let t = typeToIr(c.m.types, s.typ)
+        #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info)
+        c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), t, opc
         if a[2].kind != nkEmpty:
           genAsgn2(c, vn, a[2])
       else:
@@ -2002,7 +2090,7 @@ proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   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.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
         target.addImmediateVal info, s.itemId.item.int
 
     valueIntoDest c, info, d, s.typ, body
@@ -2026,7 +2114,7 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
   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
+      target.addIntVal c.lit.numbers, 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)
@@ -2034,13 +2122,13 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
 proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) =
   let info = toLineInfo(c, n.info)
   template body(target) =
-    target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), bits
+    target.addIntVal c.lit.numbers, 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
+    target.addStrVal c.lit.strings, info, n.strVal
   valueIntoDest c, info, d, n.typ, body
 
 proc genNilLit(c: var ProcCon; n: PNode; d: var Value) =
@@ -2068,31 +2156,6 @@ proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
   else:
     gen c, n[0], d
 
-type
-  IndexFor = enum
-    ForSeq, ForStr, ForOpenArray
-
-proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor): Value =
-  if optBoundsCheck in c.options:
-    let info = toLineInfo(c, n.info)
-    result = default(Value)
-    let idx = genx(c, n)
-    build result, info, CheckedIndex:
-      copyTree result.Tree, idx
-      case kind
-      of ForSeq, ForStr:
-        buildTyped result, info, FieldAt, c.m.nativeIntId:
-          copyTree result.Tree, a
-          result.addImmediateVal info, 0 # (len, p)-pair
-      of ForOpenArray:
-        buildTyped result, info, FieldAt, c.m.nativeIntId:
-          copyTree result.Tree, a
-          result.addImmediateVal info, 1 # (p, len)-pair
-      result.Tree.addLabel info, CheckedGoto, c.exitLabel
-    freeTemp c, idx
-  else:
-    result = genx(c, n)
-
 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)
@@ -2189,7 +2252,10 @@ proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
 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
+    if not isCompileTimeOnly(s.typ):
+      let t = typeToIr(c.m.types, s.typ)
+      assert t.int != -1, typeToString(s.typ)
+      c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), t, SummonParam
 
 proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) =
   template ann(s: untyped) = c.code.addPragmaId info, s
@@ -2207,10 +2273,9 @@ proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvent
 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
+  if isGenericRoutineStrict(prc) or isCompileTimeProc(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, keepOpenArrayConversions})
 
@@ -2221,26 +2286,27 @@ proc genProc(cOuter: var ProcCon; n: PNode) =
     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
+        c.code.addStrVal c.lit.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
+            c.code.addStrVal c.lit.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
+            c.code.addStrVal c.lit.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
 
+    genParams(c, prc.typ.n)
     gen(c, body)
     patch c, body, c.exitLabel
 
diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim
index 1994a1be7..0669bc222 100644
--- a/compiler/nir/nir.nim
+++ b/compiler/nir/nir.nim
@@ -10,8 +10,12 @@
 ## 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
+from os import addFileExt, `/`, createDir
+
+import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
+import nirtypes, nirinsts, ast2ir, nirlineinfos
+
+import ".." / ic / [rodfiles, bitabs]
 
 type
   PCtx* = ref object of TPassContext
@@ -45,8 +49,8 @@ proc evalStmt(c: PCtx; n: PNode) =
 
   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 c.c.code, NodePos(pc), c.m.lit.strings, c.m.lit.numbers, res
+  #res.add "\n--------------------------\n"
   #toString res, c.m.types.g
   echo res
 
@@ -61,11 +65,51 @@ proc runCode*(c: PPassContext; n: PNode): PNode =
     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]
+type
+  NirPassContext* = ref object of TPassContext
+    m: ModuleCon
+    c: ProcCon
+
+proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  let m = initModuleCon(g, g.config, idgen, module)
+  NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
+
+proc gen(c: NirPassContext; n: PNode) =
+  let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
+  let pc = genStmt(c.c, n)
+
+proc nirBackend*(c: PPassContext; n: PNode): PNode =
+  gen(NirPassContext(c), n)
+  result = n
+
+proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
+  discard nirBackend(c, finalNode)
+
+  let c = NirPassContext(c)
+  let nimcache = getNimcacheDir(c.c.config).string
+  createDir nimcache
+  let outp = nimcache / c.m.module.name.s.addFileExt("nir")
+  var r = rodfiles.create(outp)
+  try:
+    r.storeHeader(nirCookie)
+    r.storeSection stringsSection
+    r.store c.m.lit.strings
+
+    r.storeSection numbersSection
+    r.store c.m.lit.numbers
 
+    r.storeSection bodiesSection
+    r.store c.c.code
+
+    r.storeSection typesSection
+    r.store c.m.types.g
+
+    r.storeSection sideChannelSection
+    r.store c.m.man
+
+  finally:
+    r.close()
+  if r.err != ok:
+    rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
+  else:
+    echo "created: ", outp
diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim
new file mode 100644
index 000000000..da8d7d778
--- /dev/null
+++ b/compiler/nir/nirc.nim
@@ -0,0 +1,48 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Nir Compiler. Currently only supports a "view" command.
+
+import ".." / ic / [bitabs, rodfiles]
+import nirinsts, nirtypes, nirlineinfos
+
+proc view(filename: string) =
+  var lit = Literals()
+
+  var r = rodfiles.open(filename)
+  var code = default Tree
+  var man = default LineInfoManager
+  var types = initTypeGraph(lit)
+  try:
+    r.loadHeader(nirCookie)
+    r.loadSection stringsSection
+    r.load lit.strings
+
+    r.loadSection numbersSection
+    r.load lit.numbers
+
+    r.loadSection bodiesSection
+    r.load code
+
+    r.loadSection typesSection
+    r.load types
+
+    r.loadSection sideChannelSection
+    r.load man
+
+  finally:
+    r.close()
+
+  var res = ""
+  allTreesToString code, lit.strings, lit.numbers, res
+  echo res
+
+import std / os
+
+view paramStr(1)
diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim
index 2c0dc3d11..741733d48 100644
--- a/compiler/nir/nirinsts.nim
+++ b/compiler/nir/nirinsts.nim
@@ -10,9 +10,14 @@
 ## NIR instructions. Somewhat inspired by LLVM's instructions.
 
 import std / [assertions, hashes]
-import .. / ic / bitabs
+import .. / ic / [bitabs, rodfiles]
 import nirlineinfos, nirtypes
 
+const
+  NirVersion = 1
+  nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
+            byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
+
 type
   SymId* = distinct int
 
@@ -167,6 +172,8 @@ type
 template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
 template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
 
+template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
+
 template toX(k: Opcode; operand: uint32): uint32 =
   uint32(k) or (operand shl OpcodeBits)
 
@@ -257,6 +264,10 @@ proc newLabel*(labelGen: var int): LabelId {.inline.} =
   result = LabelId labelGen
   inc labelGen
 
+proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
+  result = LabelId labelGen
+  inc labelGen, n
+
 proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
   assert k in {Label, LoopLabel}
   result = LabelId labelGen
@@ -281,9 +292,11 @@ 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.} =
+  assert typ.int >= 0
   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 typ.int >= 0
   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)
@@ -304,10 +317,16 @@ proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo;
 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 addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
+  t.nodes.add Instr(x: toX(StrVal, uint32(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 store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
+proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
+
 proc escapeToNimLit(s: string; result: var string) =
   result.add '"'
   for c in items s:
@@ -370,6 +389,14 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl
     for i in 0..<nesting*2: r.add ' '
     r.add "}"
 
+proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
+                       r: var string) =
+
+  var i = 0
+  while i < t.len:
+    toString t, NodePos(i), strings, integers, r
+    nextChild t, i
+
 type
   Value* = distinct Tree
 
@@ -419,3 +446,6 @@ proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo
 
 proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
   addNilVal Tree(t), info, typ
+
+proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
+  addIntVal Tree(t), integers, info, typ, x
diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim
index 4e86f619e..5f5d55086 100644
--- a/compiler/nir/nirlineinfos.nim
+++ b/compiler/nir/nirlineinfos.nim
@@ -34,13 +34,13 @@ const
 static:
   assert AsideBit + FileBits + LineBits + ColBits == 32
 
-import .. / ic / bitabs # for LitId
+import .. / ic / [bitabs, rodfiles] # for LitId
 
 type
   PackedLineInfo* = distinct uint32
 
   LineInfoManager* = object
-    aside*: seq[(LitId, int32, int32)]
+    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:
@@ -66,6 +66,9 @@ proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
 proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
   result = unpack(m, i)[0]
 
+proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
+proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
+
 when isMainModule:
   var m = LineInfoManager(aside: @[])
   for i in 0'i32..<16388'i32:
diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim
index 256c25a19..d983fdea2 100644
--- a/compiler/nir/nirslots.nim
+++ b/compiler/nir/nirslots.nim
@@ -76,7 +76,7 @@ proc closeScope*(m: var SlotManager) =
 when isMainModule:
   var m = initSlotManager({ReuseTemps}, new(int))
 
-  var g = initTypeGraph()
+  var g = initTypeGraph(Literals())
 
   let a = g.openType ArrayTy
   g.addBuiltinType Int8Id
diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim
index a42feab00..288f3063d 100644
--- a/compiler/nir/nirtypes.nim
+++ b/compiler/nir/nirtypes.nim
@@ -10,12 +10,15 @@
 ## Type system for NIR. Close to C's type system but without its quirks.
 
 import std / [assertions, hashes]
-import .. / ic / bitabs
+import .. / ic / [bitabs, rodfiles]
 
 type
   NirTypeKind* = enum
-    VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal,
+    VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal,
+    IntVal, SizeVal, AlignVal, OffsetVal,
     AnnotationVal,
+    ObjectTy,
+    UnionTy,
     VarargsTy, # the `...` in a C prototype; also the last "atom"
     APtrTy, # pointer to aliasable memory
     UPtrTy, # pointer to unique/unaliasable memory
@@ -23,8 +26,6 @@ type
     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,
@@ -54,10 +55,13 @@ proc `==`*(a, b: TypeId): bool {.borrow.}
 proc hash*(a: TypeId): Hash {.borrow.}
 
 type
+  Literals* = ref object
+    strings*: BiTable[string]
+    numbers*: BiTable[int64]
+
   TypeGraph* = object
     nodes: seq[TypeNode]
-    names: BiTable[string]
-    numbers: BiTable[uint64]
+    lit: Literals
 
 const
   VoidId* = TypeId 0
@@ -76,7 +80,7 @@ const
   VoidPtrId* = TypeId 13
   LastBuiltinId* = 13
 
-proc initTypeGraph*(): TypeGraph =
+proc initTypeGraph*(lit: Literals): TypeGraph =
   result = TypeGraph(nodes: @[
     TypeNode(x: toX(VoidTy, 0'u32)),
     TypeNode(x: toX(BoolTy, 8'u32)),
@@ -93,7 +97,7 @@ proc initTypeGraph*(): TypeGraph =
     TypeNode(x: toX(FloatTy, 64'u32)),
     TypeNode(x: toX(APtrTy, 2'u32)),
     TypeNode(x: toX(VoidTy, 0'u32))
-  ])
+  ], lit: lit)
   assert result.nodes.len == LastBuiltinId+2
 
 type
@@ -164,9 +168,9 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
   let c = b + span(tree, b)
   result = (TypeId a, TypeId b, TypeId c)
 
-proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt =
+proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
   assert tree[n].kind == ArrayTy
-  result = tree.numbers[LitId tree[n].operand]
+  result = tree.lit.numbers[LitId tree[n].operand]
 
 proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
   assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
@@ -174,20 +178,54 @@ proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
     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)
+template typeInvariant(p: TypePatchPos) =
+  when false:
+    if tree[TypeId(p)].kind == FieldDecl:
+      var k = 0
+      for ch in sons(tree, TypeId(p)):
+        inc k
+      assert k > 2, "damn! " & $k
+
+proc sealType*(tree: var TypeGraph; p: TypePatchPos) =
+  patch tree, p
+  typeInvariant(p)
+
+proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
+  # Search for an existing instance of this type in
+  # order to reduce memory consumption:
   patch tree, p
+  typeInvariant(p)
+
+  let s = span(tree, p.int)
+  var i = 0
+  while i < p.int:
+    if tree.nodes[i].x == tree.nodes[p.int].x:
+      var isMatch = true
+      for j in 1..<s:
+        if tree.nodes[j+i].x == tree.nodes[j+p.int].x:
+          discard "still a match"
+        else:
+          isMatch = false
+          break
+      if isMatch:
+        if p.int+s == tree.len:
+          setLen tree.nodes, p.int
+        return TypeId(i)
+    nextChild tree, i
+  result = TypeId(p)
 
 proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
   assert kind in {ObjectTy, UnionTy}
+  let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
+  for i in 0..<tree.len:
+    if tree.nodes[i].x == content.x:
+      return TypeId(i)
   result = TypeId tree.nodes.len
-  tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name)))
+  tree.nodes.add content
 
 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)))
+  tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
 
 proc addVarargs*(tree: var TypeGraph) =
   tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
@@ -219,30 +257,46 @@ proc addType*(g: var TypeGraph; t: TypeId) =
     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 addArrayLen*(g: var TypeGraph; len: int64) =
+  g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len)))
+
+proc addSize*(g: var TypeGraph; s: int64) =
+  g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s)))
+
+proc addOffset*(g: var TypeGraph; offset: int64) =
+  g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset)))
+
+proc addAlign*(g: var TypeGraph; a: int64) =
+  g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a)))
 
 proc addName*(g: var TypeGraph; name: string) =
-  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
+  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
 
 proc addAnnotation*(g: var TypeGraph; name: string) =
-  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
+  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
 
-proc addField*(g: var TypeGraph; name: string; typ: TypeId) =
+proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) =
   let f = g.openType FieldDecl
   g.addType typ
+  g.addOffset offset
   g.addName name
-  discard sealType(g, f)
+  sealType(g, f)
 
 proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
   let f = g.openType APtrTy
   g.addType t
-  result = sealType(g, f)
+  result = finishType(g, f)
 
 proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
   let f = g.openType AArrayPtrTy
   g.addType t
-  result = sealType(g, f)
+  result = finishType(g, f)
+
+proc store*(r: var RodFile; g: TypeGraph) =
+  storeSeq r, g.nodes
+
+proc load*(r: var RodFile; g: var TypeGraph) =
+  loadSeq r, g.nodes
 
 proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
   case g[i].kind
@@ -263,9 +317,11 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
     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]
+    dest.add g.lit.strings[LitId g[i].operand]
+  of IntVal, SizeVal, AlignVal, OffsetVal:
+    dest.add $g[i].kind
+    dest.add ' '
+    dest.add $g.lit.numbers[LitId g[i].operand]
   of VarargsTy:
     dest.add "..."
   of APtrTy:
@@ -298,10 +354,10 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
     dest.add "]"
   of ObjectTy:
     dest.add "object "
-    dest.add g.names[LitId g[i].operand]
+    dest.add g.lit.strings[LitId g[i].operand]
   of UnionTy:
     dest.add "union "
-    dest.add g.names[LitId g[i].operand]
+    dest.add g.lit.strings[LitId g[i].operand]
   of ProcTy:
     dest.add "proc["
     for t in sons(g, i): toString(dest, g, t)
@@ -319,10 +375,19 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
       dest.add '\n'
     dest.add "]"
   of FieldDecl:
-    let (typ, name) = g.sons2(i)
-    toString(dest, g, typ)
-    dest.add ' '
-    toString(dest, g, name)
+    dest.add "field["
+    for t in sons(g, i):
+      toString(dest, g, t)
+      dest.add ' '
+    dest.add "]"
+
+    when false:
+      let (typ, offset, name) = g.sons3(i)
+      toString(dest, g, typ)
+      dest.add ' '
+      toString(dest, g, offset)
+      dest.add ' '
+      toString(dest, g, name)
 
 proc toString*(dest: var string; g: TypeGraph) =
   var i = 0
@@ -336,17 +401,17 @@ proc `$`(g: TypeGraph): string =
   toString(result, g)
 
 when isMainModule:
-  var g = initTypeGraph()
+  var g = initTypeGraph(Literals())
 
   let a = g.openType ArrayTy
   g.addBuiltinType Int8Id
-  g.addArrayLen 5'u64
-  let finalArrayType = sealType(g, a)
+  g.addArrayLen 5
+  let finalArrayType = finishType(g, a)
 
   let obj = g.openType ObjectDecl
-  g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType")))
+  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType")))
 
-  g.addField "p", finalArrayType
-  discard sealType(g, obj)
+  g.addField "p", finalArrayType, 0
+  sealType(g, obj)
 
   echo g
diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim
new file mode 100644
index 000000000..dcb5ded6f
--- /dev/null
+++ b/compiler/nir/nirvm.nim
@@ -0,0 +1,467 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+##[ NIR is a little too high level to interpret it efficiently. Thus
+we compute `addresses` for SymIds, labels and offsets for object fields
+in a preprocessing step.
+
+We also split the instruction stream into separate (code, debug) seqs while
+we're at it.
+]##
+
+import std / [tables, intsets]
+import ".." / ic / bitabs
+import nirinsts, nirtypes
+
+type
+  OpcodeM = enum
+    ImmediateValM,
+    IntValM,
+    StrValM,
+    LoadLocalM, # with local ID
+    TypedM,   # with type ID
+    PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum
+    NilValM,
+    GotoM,
+    CheckedGotoM, # last atom
+
+    LoadProcM,
+    LoadGlobalM, # `"module".x`
+
+    ArrayConstrM,
+    ObjConstrM,
+    RetM,
+    YldM,
+
+    SelectM,
+    SelectPairM,  # ((values...), Label)
+    SelectListM,  # (values...)
+    SelectValueM, # (value)
+    SelectRangeM, # (valueA..valueB)
+    SummonGlobalM,
+    SummonThreadLocalM,
+    SummonM, # x = Summon Typed <Type ID>; x begins to live
+    SummonParamM,
+
+    AddrOfM,
+    ArrayAtM, # addr(a[i])
+    FieldAtM, # addr(obj.field)
+
+    LoadM, # a[]
+    StoreM, # a[] = b
+    AsgnM,  # a = b
+    SetExcM,
+    TestExcM,
+
+    CheckedRangeM,
+    CheckedIndexM,
+
+    CallM,
+    IndirectCallM,
+    CheckedCallM, # call that can raise
+    CheckedIndirectCallM, # call that can raise
+    CheckedAddM, # with overflow checking etc.
+    CheckedSubM,
+    CheckedMulM,
+    CheckedDivM,
+    CheckedModM,
+    AddM,
+    SubM,
+    MulM,
+    DivM,
+    ModM,
+    BitShlM,
+    BitShrM,
+    BitAndM,
+    BitOrM,
+    BitXorM,
+    BitNotM,
+    BoolNotM,
+    EqM,
+    LeM,
+    LtM,
+    CastM,
+    NumberConvM,
+    CheckedObjConvM,
+    ObjConvM,
+    TestOfM,
+    ProcDeclM,
+    PragmaPairM
+
+const
+  LastAtomicValue = CheckedGotoM
+
+  OpcodeBits = 8'u32
+  OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
+
+type
+  Instr = distinct uint32
+
+template kind(n: Instr): OpcodeM = OpcodeM(n and OpcodeMask)
+template operand(n: Instr): uint32 = (n shr OpcodeBits)
+
+template toIns(k: OpcodeM; operand: uint32): Instr =
+  Instr(uint32(k) or (operand shl OpcodeBits))
+
+template toIns(k: OpcodeM; operand: LitId): Instr =
+  Instr(uint32(k) or (operand.uint32 shl OpcodeBits))
+
+type
+  PatchPos = distinct int
+  CodePos = distinct int
+
+  Unit = ref object ## a NIR module
+    procs: Table[SymId, CodePos]
+    globals: Table[SymId, uint32]
+    integers: BiTable[int64]
+    strings: BiTable[string]
+    globalsGen: uint32
+
+  Universe* = object ## all units: For interpretation we need that
+    units: Table[string, Unit]
+
+  Bytecode = object
+    code: seq[Instr]
+    debug: seq[PackedLineInfo]
+    u: Unit
+
+const
+  InvalidPatchPos* = PatchPos(-1)
+
+proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
+
+proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos =
+  result = PatchPos bc.code.len
+  bc.code.add toIns(kind, 1'u32)
+  bc.debug.add info
+
+proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) =
+  bc.code.add toIns(kind, raw)
+  bc.debug.add info
+
+proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) =
+  add bc, info, kind, uint(lit)
+
+proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue
+proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue
+
+proc patch(bc: var Bytecode; pos: PatchPos) =
+  let pos = pos.int
+  let k = bc.code[pos].kind
+  assert k > LastAtomicValue
+  let distance = int32(bc.code.len - pos)
+  assert distance > 0
+  bc.code[pos] = toIns(k, cast[uint32](distance))
+
+template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) =
+  let pos = prepare(bc, info, kind)
+  body
+  patch(bc, pos)
+
+proc len*(bc: Bytecode): int {.inline.} = bc.code.len
+
+template rawSpan(n: Instr): int = int(operand(n))
+
+proc nextChild(bc: Bytecode; pos: var int) {.inline.} =
+  if bc.code[pos].kind > LastAtomicValue:
+    assert bc.code[pos].operand > 0'u32
+    inc pos, bc.code[pos].rawSpan
+  else:
+    inc pos
+
+iterator sons(bc: Bytecode; n: CodePos): CodePos =
+  var pos = n.int
+  assert bc.code[pos].kind > LastAtomicValue
+  let last = pos + bc.code[pos].rawSpan
+  inc pos
+  while pos < last:
+    yield CodePos pos
+    nextChild bc, pos
+
+template `[]`*(t: Bytecode; n: CodePos): Instr = t.code[n.int]
+
+proc span(bc: Bytecode; pos: int): int {.inline.} =
+  if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand)
+
+type
+  Preprocessing = object
+    known: Table[LabelId, CodePos]
+    toPatch: Table[LabelId, seq[CodePos]]
+    locals: Table[SymId, uint32]
+    c: Bytecode # to be moved out
+    thisModule: LitId
+    markedWithLabel: IntSet
+
+proc genGoto(c: var Preprocessing; lab: LabelId; opc: OpcodeM) =
+  let dest = c.known.getOrDefault(lab, CodePos(-1))
+  if dest.int >= 0:
+    c.bc.add info, opc, uint32 dest
+  else:
+    let here = CodePos(c.bc.code.len)
+    c.toPatch.mgetOrPut(lab, @[]).add here
+    c.bc.add info, opc, 1u32 # will be patched once we traversed the label
+
+proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
+  let info = t[n].info
+
+  template recurse(opc) =
+    build c.bc, info, opc:
+      for c in sons(t, n): preprocess(c, u, t, c)
+
+  case t[n].kind
+  of Nop:
+    discard "don't use Nop"
+  of ImmediateVal:
+    c.bc.add info, ImmediateValM, t[n].rawOperand
+  of IntVal:
+    c.bc.add info, IntValM, t[n].rawOperand
+  of StrVal:
+    c.bc.add info, StrValM, t[n].rawOperand
+  of SymDef:
+    assert false, "SymDef outside of declaration context"
+  of SymUse:
+    let s = t[n].symId
+    if c.locals.hasKey(s):
+      c.bc.add info, LoadLocalM, c.locals[s]
+    elif c.bc.u.procs.hasKey(s):
+      build c.bc, info, LoadProcM:
+        c.bc.add info, StrValM, thisModule
+        c.bc.add info, LoadLocalM, uint32 c.bc.u.procs[s]
+    elif c.bc.u.globals.hasKey(s):
+      build c.bc, info, LoadGlobalM:
+        c.bc.add info, StrValM, thisModule
+        c.bc.add info, LoadLocalM, uint32 s
+    else:
+      assert false, "don't understand SymUse ID"
+
+  of ModuleSymUse:
+    let moduleName {.cursor.} = c.bc.u.strings[t[n.firstSon].litId]
+    let unit = u.units.getOrDefault(moduleName)
+
+  of Typed:
+    c.bc.add info, TypedM, t[n].rawOperand
+  of PragmaId:
+    c.bc.add info, TypedM, t[n].rawOperand
+  of NilVal:
+    c.bc.add info, NilValM, t[n].rawOperand
+  of LoopLabel, Label:
+    let lab = t[n].label
+    let here = CodePos(c.bc.code.len-1)
+    c.known[lab] = here
+    var p: seq[CodePos]
+    if c.toPatch.take(lab, p):
+      for x in p: c.bc.code[x] = toIns(c.bc.code[x].kind, here)
+    c.markedWithLabel.incl here.int # for toString()
+  of Goto, GotoLoop:
+    c.genGoto(t[n].label, GotoM)
+  of CheckedGoto:
+    c.genGoto(t[n].label, CheckedGotoM)
+  of ArrayConstr:
+    recurse ArrayConstrM
+  of ObjConstr:
+    recurse ObjConstrM
+  of Ret:
+    recurse RetM
+  of Yld:
+    recurse YldM
+  of Select:
+    recurse SelectM
+  of SelectPair:
+    recurse SelectPairM
+  of SelectList:
+    recurse SelectListM
+  of SelectValue:
+    recurse SelectValueM
+  of SelectRange:
+    recurse SelectRangeM
+  of SummonGlobal, SummonThreadLocal, SummonConst:
+    #let s =
+    discard "xxx"
+  of Summon, SummonParam:
+    # x = Summon Typed <Type ID>; x begins to live
+    discard "xxx"
+  of Kill:
+    discard "we don't care about Kill instructions"
+  of AddrOf:
+    recurse AddrOfM
+  of ArrayAt:
+    recurse ArrayAtM
+  of FieldAt:
+    recurse FieldAtM
+  of Load:
+    recurse LoadM
+  of Store:
+    recurse StoreM
+  of Asgn:
+    recurse AsgnM
+  of SetExc:
+    recurse SetExcM
+  of TestExc:
+    recurse TestExcM
+  of CheckedRange:
+    recurse CheckedRangeM
+  of CheckedIndex:
+    recurse CheckedIndexM
+  of Call:
+    recurse CallM
+  of IndirectCall:
+    recurse IndirectCallM
+  of CheckedCall:
+    recurse CheckedCallM
+  of CheckedIndirectCall:
+    recurse CheckedIndirectCallM
+  of CheckedAdd:
+    recurse CheckedAddM
+  of CheckedSub:
+    recurse CheckedSubM
+  of CheckedMul:
+    recurse CheckedMulM
+  of CheckedDiv:
+    recurse CheckedDivM
+  of CheckedMod:
+    recurse CheckedModM
+  of Add:
+    recurse AddM
+  of Sub:
+    recurse SubM
+  of Mul:
+    recurse MulM
+  of Div:
+    recurse DivM
+  of Mod:
+    recurse ModM
+  of BitShl:
+    recurse BitShlM
+  of BitShr:
+    recurse BitShrM
+  of BitAnd:
+    recurse BitAndM
+  of BitOr:
+    recurse BitOrM
+  of BitXor:
+    recurse BitXorM
+  of BitNot:
+    recurse BitNotM
+  of BoolNot:
+    recurse BoolNotM
+  of Eq:
+    recurse EqM
+  of Le:
+    recurse LeM
+  of Lt:
+    recurse LtM
+  of Cast:
+    recurse CastM
+  of NumberConv:
+    recurse NumberConvM
+  of CheckedObjConv:
+    recurse CheckedObjConvM
+  of ObjConv:
+    recurse ObjConvM
+  of TestOf:
+    recurse TestOfM
+  of Emit:
+    assert false, "cannot interpret: Emit"
+  of ProcDecl:
+    recurse ProcDeclM
+  of PragmaPair:
+    recurse PragmaPairM
+
+const PayloadSize = 128
+
+type
+  StackFrame = ref object
+    locals: pointer   # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed.
+    payload: array[PayloadSize, byte]
+    caller: StackFrame
+    returnAddr: CodePos
+
+proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame =
+  result = StackFrame(caller: caller, returnAddr: returnAddr)
+  if size <= PayloadSize:
+    result.locals = addr(result.payload)
+  else:
+    result.locals = alloc0(size)
+
+proc popStackFrame(s: StackFrame): StackFrame =
+  if result.locals != addr(result.payload):
+    dealloc result.locals
+  result = s.caller
+
+template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff)
+
+proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer)
+
+proc evalAddr(c: seq[Instr]; pc: CodePos; s: StackFrame): pointer =
+  case c[pc].kind
+  of LoadLocalM:
+    result = s.locals +! c[pc].operand
+  of FieldAtM:
+    result = eval(c, pc+1, s)
+    result = result +! c[pc+2].operand
+  of ArrayAtM:
+    let elemSize = c[pc+1].operand
+    result = eval(c, pc+2, s)
+    var idx: int
+    eval(c, pc+3, addr idx)
+    result = result +! (idx * elemSize)
+
+proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer) =
+  case c[pc].kind
+  of AddM:
+    # assume `int` here for now:
+    var x, y: int
+    eval c, pc+1, s, addr x
+    eval c, pc+2, s, addr y
+    cast[ptr int](res)[] = x + y
+  of StrValM:
+    cast[ptr StringDesc](res)[] = addr(c.strings[c[pc].litId])
+  of ObjConstrM:
+    for ch in sons(c, pc):
+      let offset = c[ch]
+      eval c, ch+2, s, result+!offset
+  of ArrayConstrM:
+    let elemSize = c[pc+1].operand
+    var r = result
+    for ch in sons(c, pc):
+      eval c, ch, s, r
+      r = r+!elemSize # can even do strength reduction here!
+  else:
+    assert false, "cannot happen"
+
+proc exec(c: seq[Instr]; pc: CodePos) =
+  var pc = pc
+  var currentFrame: StackFrame = nil
+  while true:
+    case c[pc].kind
+    of GotoM:
+      pc = CodePos(c[pc].operand)
+    of Asgn:
+      let (size, a, b) = sons3(c, pc)
+      let dest = evalAddr(c, a, s)
+      eval(c, b, s, dest)
+    of CallM:
+      # No support for return values, these are mapped to `var T` parameters!
+      let prc = evalProc(c, pc+1)
+      # setup storage for the proc already:
+      let s2 = newStackFrame(prc.frameSize, currentFrame, pc)
+      var i = 0
+      for a in sons(c, pc):
+        eval(c, a, s2, paramAddr(s2, i))
+        inc i
+      currentFrame = s2
+      pc = pcOf(prc)
+    of RetM:
+      pc = currentFrame.returnAddr
+      currentFrame = popStackFrame(currentFrame)
+    of SelectM:
+      var x: bool
+      eval(c, b, addr x)
+      # follow the selection instructions...
+      pc = activeBranch(c, b, x)
diff --git a/compiler/nir/stringcases.nim b/compiler/nir/stringcases.nim
new file mode 100644
index 000000000..9417d613d
--- /dev/null
+++ b/compiler/nir/stringcases.nim
@@ -0,0 +1,200 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## included from ast2ir.nim
+
+#[
+
+case s
+of "abc", "abbd":
+  echo 1
+of "hah":
+  echo 2
+of "gah":
+  echo 3
+
+# we produce code like this:
+
+if s[0] <= 'a':
+  if s == "abc: goto L1
+  elif s == "abbd": goto L1
+else:
+  if s[2] <= 'h':
+    if s == "hah": goto L2
+    elif s == "gah": goto L3
+goto afterCase
+
+L1:
+  echo 1
+  goto afterCase
+L2:
+  echo 2
+  goto afterCase
+L3:
+  echo 3
+  goto afterCase
+
+afterCase: ...
+
+]#
+
+# We split the set of strings into 2 sets of roughly the same size.
+# The condition used for splitting is a (position, char) tuple.
+# Every string of length > position for which s[position] <= char is in one
+# set else it is in the other set.
+
+from sequtils import addUnique
+
+type
+  Key = (LitId, LabelId)
+
+proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) =
+  var cand: seq[char] = @[]
+  for t in items a:
+    let s = strings[t[0]]
+    if s.len > position: cand.addUnique s[position]
+
+  result = ('\0', -1.0)
+  for disc in items cand:
+    var hits = 0
+    for t in items a:
+      let s = strings[t[0]]
+      if s.len > position and s[position] <= disc:
+        inc hits
+    # the split is the better, the more `hits` is close to `a.len / 2`:
+    let grade = 100000.0 - abs(hits.float - a.len.float / 2.0)
+    if grade > result[1]:
+      result = (disc, grade)
+
+proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) =
+  var m = 0
+  for t in items a:
+    m = max(m, strings[t[0]].len)
+
+  result = ('\0', -1)
+  var best = -1.0
+  for i in 0 ..< m:
+    let current = splitValue(strings, a, i)
+    if current[1] > best:
+      best = current[1]
+      result = (current[0], i)
+
+type
+  SearchKind = enum
+    LinearSearch, SplitSearch
+  SearchResult* = object
+    case kind: SearchKind
+    of LinearSearch:
+      a: seq[Key]
+    of SplitSearch:
+      span: int
+      best: (char, int)
+
+proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
+  var d = SearchResult(kind: LinearSearch, a: @[])
+  for x in a: d.a.add x
+  dest.add d
+
+proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
+  if a.len <= 4:
+    emitLinearSearch strings, a, dest
+  else:
+    let best = tryAllPositions(strings, a)
+    var groupA: seq[Key] = @[]
+    var groupB: seq[Key] = @[]
+    for t in items a:
+      let s = strings[t[0]]
+      if s.len > best[1] and s[best[1]] <= best[0]:
+        groupA.add t
+      else:
+        groupB.add t
+    if groupA.len == 0 or groupB.len == 0:
+      emitLinearSearch strings, a, dest
+    else:
+      let toPatch = dest.len
+      dest.add SearchResult(kind: SplitSearch, span: 1, best: best)
+      split strings, groupA, dest
+      split strings, groupB, dest
+      let dist = dest.len - toPatch
+      assert dist > 0
+      dest[toPatch].span = dist
+
+proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) =
+  result = (@[], newLabels(c.labelGen, n.len))
+  assert n.kind == nkCaseStmt
+  for i in 1..<n.len:
+    let it = n[i]
+    let thisBranch = LabelId(result[1].int + i - 1)
+    if it.kind == nkOfBranch:
+      for j in 0..<it.len-1:
+        assert it[j].kind in {nkStrLit..nkTripleStrLit}
+        result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch)
+
+proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int;
+                    selector: Value; info: PackedLineInfo) =
+  case s[i].kind
+  of SplitSearch:
+    let thenA = i+1
+    let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span)
+    let best = s[i].best
+
+    let tmp = getTemp(c, Bool8Id, info)
+    buildTyped dest, info, Asgn, Bool8Id:
+      dest.copyTree tmp
+      buildTyped dest, info, Call, Bool8Id:
+        c.addUseCodegenProc dest, "nimStrAtLe", info
+        dest.copyTree selector
+        dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1]
+        dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int
+
+    template then() =
+      c.decodeSolution dest, s, thenA, selector, info
+    template otherwise() =
+      c.decodeSolution dest, s, elseA, selector, info
+    buildIfThenElse tmp, then, otherwise
+    freeTemp c, tmp
+
+  of LinearSearch:
+    let tmp = getTemp(c, Bool8Id, info)
+    for x in s[i].a:
+      buildTyped dest, info, Asgn, Bool8Id:
+        dest.copyTree tmp
+        buildTyped dest, info, Call, Bool8Id:
+          c.addUseCodegenProc dest, "eqStrings", info
+          dest.copyTree selector
+          dest.addStrLit info, x[0]
+      buildIf tmp:
+        c.code.gotoLabel info, Goto, x[1]
+    freeTemp c, tmp
+
+proc genStringCase(c: var ProcCon; n: PNode; d: var Value) =
+  let (problem, firstBranch) = toProblemDescription(c, n)
+  var solution: seq[SearchResult] = @[]
+  split c.lit.strings, problem, solution
+
+  # XXX Todo move complex case selector into a temporary.
+  let selector = c.genx(n[0])
+
+  let info = toLineInfo(c, n.info)
+  decodeSolution c, c.code, solution, 0, selector, info
+
+  let lend = newLabel(c.labelGen)
+  c.code.addLabel info, Goto, lend
+  for i in 1..<n.len:
+    let it = n[i]
+    let thisBranch = LabelId(firstBranch.int + i - 1)
+    c.code.addLabel info, Label, thisBranch
+    if it.kind == nkOfBranch:
+      gen(c, it.lastSon, d)
+      c.code.addLabel info, Goto, lend
+    else:
+      gen(c, it.lastSon, d)
+
+  c.code.addLabel info, Label, lend
+  freeTemp c, selector
diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim
index 835bef03c..76907d587 100644
--- a/compiler/nir/types2ir.nim
+++ b/compiler/nir/types2ir.nim
@@ -18,8 +18,8 @@ type
     g*: TypeGraph
     conf: ConfigRef
 
-proc initTypesCon*(conf: ConfigRef): TypesCon =
-  TypesCon(g: initTypeGraph(), conf: conf)
+proc initTypesCon*(conf: ConfigRef; lit: Literals): TypesCon =
+  TypesCon(g: initTypeGraph(lit), conf: conf)
 
 proc mangle(c: var TypesCon; t: PType): string =
   result = $sighashes.hashType(t, c.conf)
@@ -67,11 +67,11 @@ proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; un
         let subObj = openType(c.g, ObjectDecl)
         c.g.addName "uo_" & $unionId & "_" & $i
         objectToIr c, lastSon(n[i]), fieldTypes, unionId
-        discard sealType(c.g, subObj)
+        sealType(c.g, subObj)
       else: discard
-    discard sealType(c.g, u)
+    sealType(c.g, u)
   of nkSym:
-    c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId]
+    c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
   else:
     assert false, "unknown node kind: " & $n.kind
 
@@ -85,6 +85,9 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId =
   collectFieldTypes c, t.n, fieldTypes
   let obj = openType(c.g, ObjectDecl)
   c.g.addName mangle(c, t)
+  c.g.addSize c.conf.getSize(t)
+  c.g.addAlign c.conf.getAlign(t)
+
   if t[0] != nil:
     c.g.addNominalType(ObjectTy, mangle(c, t[0]))
   else:
@@ -93,12 +96,13 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId =
       let f2 = c.g.openType FieldDecl
       let voidPtr = openType(c.g, APtrTy)
       c.g.addBuiltinType(VoidId)
-      discard sealType(c.g, voidPtr)
+      sealType(c.g, voidPtr)
+      c.g.addOffset 0 # type field is always at offset 0
       c.g.addName "m_type"
-      discard sealType(c.g, f2) # FieldDecl
+      sealType(c.g, f2) # FieldDecl
 
   objectToIr c, t.n, fieldTypes, unionId
-  result = sealType(c.g, obj)
+  result = finishType(c.g, obj)
 
 proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId =
   result = c.g.nominalType(ObjectTy, mangle(c, t))
@@ -109,9 +113,18 @@ proc tupleToIr(c: var TypesCon; t: PType): TypeId =
     fieldTypes[i] = typeToIr(c, t[i])
   let obj = openType(c.g, ObjectDecl)
   c.g.addName mangle(c, t)
+  c.g.addSize c.conf.getSize(t)
+  c.g.addAlign c.conf.getAlign(t)
+
+  var accum = OffsetAccum(maxAlign: 1)
   for i in 0..<t.len:
-    c.g.addField "f_" & $i, fieldTypes[i]
-  result = sealType(c.g, obj)
+    let child = t[i]
+    c.g.addField "f_" & $i, fieldTypes[i], accum.offset
+
+    computeSizeAlign(c.conf, child)
+    accum.align(child.align)
+    accum.inc(int32(child.size))
+  result = finishType(c.g, obj)
 
 proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
   var fieldTypes = newSeq[TypeId](0)
@@ -137,11 +150,11 @@ proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
   if addEnv:
     let a = openType(c.g, APtrTy)
     c.g.addBuiltinType(VoidId)
-    discard sealType(c.g, a)
+    sealType(c.g, a)
 
   if tfVarargs in t.flags:
     c.g.addVarargs()
-  result = sealType(c.g, obj)
+  result = finishType(c.g, obj)
 
 proc nativeInt(c: TypesCon): TypeId =
   case c.conf.target.intSize
@@ -154,7 +167,7 @@ proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId =
   let elementType = typeToIr(c, e)
   let arr = c.g.openType AArrayPtrTy
   c.g.addType elementType
-  result = sealType(c.g, arr) # LastArrayTy
+  result = finishType(c.g, arr) # LastArrayTy
 
 proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
   # object (a: ArrayPtr[T], len: int)
@@ -167,38 +180,45 @@ proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
 
   let p = openType(c.g, ObjectDecl)
   c.g.addName typeName
+  c.g.addSize c.conf.target.ptrSize*2
+  c.g.addAlign c.conf.target.ptrSize
 
   let f = c.g.openType FieldDecl
   let arr = c.g.openType AArrayPtrTy
   c.g.addType elementType
-  discard sealType(c.g, arr) # LastArrayTy
+  sealType(c.g, arr) # LastArrayTy
+  c.g.addOffset 0
   c.g.addName "data"
-  discard sealType(c.g, f) # FieldDecl
+  sealType(c.g, f) # FieldDecl
 
-  c.g.addField "len", c.nativeInt
+  c.g.addField "len", c.nativeInt, c.conf.target.ptrSize
 
-  result = sealType(c.g, p) # ObjectDecl
+  result = finishType(c.g, p) # ObjectDecl
 
 proc strPayloadType(c: var TypesCon): string =
   result = "NimStrPayload"
   let p = openType(c.g, ObjectDecl)
   c.g.addName result
-  c.g.addField "cap", c.nativeInt
+  c.g.addSize c.conf.target.ptrSize*2
+  c.g.addAlign c.conf.target.ptrSize
+
+  c.g.addField "cap", c.nativeInt, 0
 
   let f = c.g.openType FieldDecl
   let arr = c.g.openType LastArrayTy
   c.g.addBuiltinType Char8Id
-  discard sealType(c.g, arr) # LastArrayTy
+  sealType(c.g, arr) # LastArrayTy
+  c.g.addOffset c.conf.target.ptrSize # comes after the len field
   c.g.addName "data"
-  discard sealType(c.g, f) # FieldDecl
+  sealType(c.g, f) # FieldDecl
 
-  discard sealType(c.g, p)
+  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
+  result = finishType(c.g, ffp) # APtrTy
 
 proc stringToIr(c: var TypesCon): TypeId =
   #[
@@ -216,16 +236,20 @@ proc stringToIr(c: var TypesCon): TypeId =
 
   let str = openType(c.g, ObjectDecl)
   c.g.addName "NimStringV2"
-  c.g.addField "len", c.nativeInt
+  c.g.addSize c.conf.target.ptrSize*2
+  c.g.addAlign c.conf.target.ptrSize
+
+  c.g.addField "len", c.nativeInt, 0
 
   let fp = c.g.openType FieldDecl
   let ffp = c.g.openType APtrTy
   c.g.addNominalType ObjectTy, "NimStrPayload"
-  discard sealType(c.g, ffp) # APtrTy
+  sealType(c.g, ffp) # APtrTy
+  c.g.addOffset c.conf.target.ptrSize # comes after 'len' field
   c.g.addName "p"
-  discard sealType(c.g, fp) # FieldDecl
+  sealType(c.g, fp) # FieldDecl
 
-  result = sealType(c.g, str) # ObjectDecl
+  result = finishType(c.g, str) # ObjectDecl
 
 proc seqPayloadType(c: var TypesCon; t: PType): string =
   #[
@@ -241,21 +265,25 @@ proc seqPayloadType(c: var TypesCon; t: PType): string =
 
   let p = openType(c.g, ObjectDecl)
   c.g.addName payloadName
-  c.g.addField "cap", c.nativeInt
+  c.g.addSize c.conf.target.intSize
+  c.g.addAlign c.conf.target.intSize
+
+  c.g.addField "cap", c.nativeInt, 0
 
   let f = c.g.openType FieldDecl
   let arr = c.g.openType LastArrayTy
   c.g.addType elementType
-  discard sealType(c.g, arr) # LastArrayTy
+  sealType(c.g, arr) # LastArrayTy
+  c.g.addOffset c.conf.target.ptrSize
   c.g.addName "data"
-  discard sealType(c.g, f) # FieldDecl
-  discard sealType(c.g, p)
+  sealType(c.g, f) # FieldDecl
+  sealType(c.g, p)
 
 proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId =
   let mangledBase = seqPayloadType(c, t)
   let ffp = c.g.openType APtrTy
   c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  result = sealType(c.g, ffp) # APtrTy
+  result = finishType(c.g, ffp) # APtrTy
 
 proc seqToIr(c: var TypesCon; t: PType): TypeId =
   #[
@@ -267,16 +295,20 @@ proc seqToIr(c: var TypesCon; t: PType): TypeId =
 
   let sq = openType(c.g, ObjectDecl)
   c.g.addName "NimSeqV2" & mangledBase
-  c.g.addField "len", c.nativeInt
+  c.g.addSize c.conf.getSize(t)
+  c.g.addAlign c.conf.getAlign(t)
+
+  c.g.addField "len", c.nativeInt, 0
 
   let fp = c.g.openType FieldDecl
   let ffp = c.g.openType APtrTy
   c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  discard sealType(c.g, ffp) # APtrTy
+  sealType(c.g, ffp) # APtrTy
+  c.g.addOffset c.conf.target.ptrSize
   c.g.addName "p"
-  discard sealType(c.g, fp) # FieldDecl
+  sealType(c.g, fp) # FieldDecl
 
-  result = sealType(c.g, sq) # ObjectDecl
+  result = finishType(c.g, sq) # ObjectDecl
 
 
 proc closureToIr(c: var TypesCon; t: PType): TypeId =
@@ -291,21 +323,25 @@ proc closureToIr(c: var TypesCon; t: PType): TypeId =
 
   let p = openType(c.g, ObjectDecl)
   c.g.addName typeName
+  c.g.addSize c.conf.getSize(t)
+  c.g.addAlign c.conf.getAlign(t)
 
   let f = c.g.openType FieldDecl
   c.g.addType procType
+  c.g.addOffset 0
   c.g.addName "ClP_0"
-  discard sealType(c.g, f) # FieldDecl
+  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)
+  sealType(c.g, voidPtr)
 
+  c.g.addOffset c.conf.target.ptrSize
   c.g.addName "ClE_0"
-  discard sealType(c.g, f2) # FieldDecl
+  sealType(c.g, f2) # FieldDecl
 
-  result = sealType(c.g, p) # ObjectDecl
+  result = finishType(c.g, p) # ObjectDecl
 
 proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId =
   let s = int(getSize(c.conf, t))
@@ -376,8 +412,8 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
       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)
+      c.g.addArrayLen n
+      result = finishType(c.g, a)
   of tyPtr, tyRef:
     cached(c, t):
       let e = t.lastSon
@@ -385,12 +421,12 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
         let elemType = typeToIr(c, e.lastSon)
         let a = openType(c.g, AArrayPtrTy)
         c.g.addType(elemType)
-        result = sealType(c.g, a)
+        result = finishType(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)
+        result = finishType(c.g, a)
   of tyVar, tyLent:
     cached(c, t):
       let e = t.lastSon
@@ -401,7 +437,7 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
         let elemType = typeToIr(c, e)
         let a = openType(c.g, APtrTy)
         c.g.addType(elemType)
-        result = sealType(c.g, a)
+        result = finishType(c.g, a)
   of tySet:
     let s = int(getSize(c.conf, t))
     case s
@@ -414,12 +450,13 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
       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:
+        c.g.addArrayLen s
+        result = finishType(c.g, a)
+  of tyPointer, tyNil:
+    # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
     let a = openType(c.g, APtrTy)
     c.g.addBuiltinType(VoidId)
-    result = sealType(c.g, a)
+    result = finishType(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`
@@ -451,20 +488,20 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
     cached(c, t):
       let a = openType(c.g, AArrayPtrTy)
       c.g.addBuiltinType Char8Id
-      result = sealType(c.g, a)
+      result = finishType(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)
+      result = finishType(c.g, a)
   of tyUntyped, tyTyped:
     # this avoids a special case for system.echo which is not a generic but
     # uses `varargs[typed]`:
     result = VoidId
   of tyNone, tyEmpty, tyTypeDesc,
-     tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
+     tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
      tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
      tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
     result = TypeId(-1)
diff --git a/compiler/options.nim b/compiler/options.nim
index a2f50b12b..b36f72693 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -137,13 +137,15 @@ type
     backendCpp = "cpp"
     backendJs = "js"
     backendObjc = "objc"
+    backendNir = "nir"
     # backendNimscript = "nimscript" # this could actually work
     # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
 
   Command* = enum  ## Nim's commands
     cmdNone # not yet processed command
     cmdUnknown # command unmapped
-    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
+    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
+    cmdCompileToNir,
     cmdCrun # compile and run in nimache
     cmdTcc # run the project via TCC backend
     cmdCheck # semantic checking for whole project
@@ -170,7 +172,8 @@ type
     # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
 
 const
-  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
+  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
+                  cmdCompileToJS, cmdCrun, cmdCompileToNir}
   cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
                  cmdCtags, cmdBuildindex}
 
@@ -235,9 +238,9 @@ type
     laxEffects
       ## Lax effects system prior to Nim 2.0.
     verboseTypeMismatch
-    emitGenerics 
-      ## generics are emitted in the module that contains them. 
-      ## Useful for libraries that rely on local passC 
+    emitGenerics
+      ## generics are emitted in the module that contains them.
+      ## Useful for libraries that rely on local passC
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim
index e9ee1ee8b..e4c484e1f 100644
--- a/compiler/pipelines.nim
+++ b/compiler/pipelines.nim
@@ -45,6 +45,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
     result = interpreterCode(bModule, semNode)
   of NirReplPass:
     result = runCode(bModule, semNode)
+  of NirPass:
+    result = nirBackend(bModule, semNode)
   of NonePass:
     raiseAssert "use setPipeLinePass to set a proper PipelinePass"
 
@@ -107,6 +109,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
     case graph.pipelinePass
     of CgenPass:
       setupCgen(graph, module, idgen)
+    of NirPass:
+      openNirBackend(graph, module, idgen)
     of JSgenPass:
       when not defined(leanCompiler):
         setupJSgen(graph, module, idgen)
@@ -203,6 +207,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
     discard interpreterCode(bModule, finalNode)
   of NirReplPass:
     discard runCode(bModule, finalNode)
+  of NirPass:
+    closeNirBackend(bModule, finalNode)
   of SemPass, GenDependPass:
     discard
   of Docgen2Pass, Docgen2TexPass:
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index 424d7450f..9e5a9ab90 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -29,11 +29,11 @@ proc raiseIllegalTypeRecursion() =
   raise newException(IllegalTypeRecursionError, "illegal type recursion")
 
 type
-  OffsetAccum = object
-    maxAlign: int32
-    offset: int32
+  OffsetAccum* = object
+    maxAlign*: int32
+    offset*: int32
 
-proc inc(arg: var OffsetAccum; value: int32) =
+proc inc*(arg: var OffsetAccum; value: int32) =
   if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
   if value == szUnknownSize or arg.offset == szUnknownSize:
     arg.offset = szUnknownSize
@@ -47,7 +47,7 @@ proc alignmentMax(a, b: int32): int32 =
   else:
     max(a, b)
 
-proc align(arg: var OffsetAccum; value: int32) =
+proc align*(arg: var OffsetAccum; value: int32) =
   if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
   if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
     arg.maxAlign = szUnknownSize
@@ -73,7 +73,7 @@ proc finish(arg: var OffsetAccum): int32 =
     result = align(arg.offset, arg.maxAlign) - arg.offset
     arg.offset += result
 
-proc computeSizeAlign(conf: ConfigRef; typ: PType)
+proc computeSizeAlign*(conf: ConfigRef; typ: PType)
 
 proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
   ## returns object alignment
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
index ee8f2d67e..d6b26493c 100644
--- a/lib/system/seqs_v2.nim
+++ b/lib/system/seqs_v2.nim
@@ -178,7 +178,7 @@ proc newSeq[T](s: var seq[T], len: Natural) =
   shrink(s, 0)
   setLen(s, len)
 
-proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} =
+proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
   result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
 
 
diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim
index 5e4cda186..dbee10777 100644
--- a/lib/system/strs_v2.nim
+++ b/lib/system/strs_v2.nim
@@ -209,6 +209,9 @@ proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} =
 proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} =
   frees(s)
 
+proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} =
+  result = idx < s.len and s[idx] <= ch
+
 func capacity*(self: string): int {.inline.} =
   ## Returns the current capacity of the string.
   # See https://github.com/nim-lang/RFCs/issues/460