summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ic/bitabs.nim4
-rw-r--r--compiler/ic/rodfiles.nim1
-rw-r--r--compiler/nir/ast2ir.nim161
-rw-r--r--compiler/nir/cir.nim881
-rw-r--r--compiler/nir/nirc.nim15
-rw-r--r--compiler/nir/nirfiles.nim10
-rw-r--r--compiler/nir/nirinsts.nim34
-rw-r--r--compiler/nir/nirtypes.nim35
-rw-r--r--compiler/nir/nirvm.nim2
-rw-r--r--compiler/nir/types2ir.nim21
10 files changed, 1072 insertions, 92 deletions
diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim
index 65b1e5a0e..c8798e0ca 100644
--- a/compiler/ic/bitabs.nim
+++ b/compiler/ic/bitabs.nim
@@ -36,9 +36,7 @@ proc mustRehash(length, counter: int): bool {.inline.} =
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
 const
-  idStart = 256 ##
-  ## Ids do not start with 0 but with this value. The IR needs it.
-  ## TODO: explain why
+  idStart = 1
 
 template idToIdx(x: LitId): int = x.int - idStart
 
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index db520527b..be5095fbb 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -98,6 +98,7 @@ type
     backendFlagsSection
     aliveSymsSection # beware, this is stored in a `.alivesyms` file.
     sideChannelSection
+    namespaceSection
     symnamesSection
 
   RodFileError* = enum
diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim
index ad04dc103..2dc28e87b 100644
--- a/compiler/nir/ast2ir.nim
+++ b/compiler/nir/ast2ir.nim
@@ -31,6 +31,8 @@ type
     idgen: IdGenerator
     processedProcs, pendingProcsAsSet: HashSet[ItemId]
     pendingProcs: seq[PSym] # procs we still need to generate code for
+    pendingVarsAsSet: HashSet[ItemId]
+    pendingVars: seq[PSym]
     noModularity*: bool
     inProc: int
     toSymId: Table[ItemId, SymId]
@@ -69,6 +71,8 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m
     result.nativeIntId = Int64Id
     result.nativeUIntId = UInt16Id
   result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types)
+  nirm.namespace = nirm.lit.strings.getOrIncl(customPath(toFullPath(config, module.info)))
+  nirm.intbits = uint32(config.target.intSize * 8)
 
 proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
   result = ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config,
@@ -162,11 +166,6 @@ proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
   c.code.addSummon info, tmp, t
   result = localToValue(info, tmp)
 
-template withTemp(tmp, n, body: untyped) {.dirty.} =
-  var tmp = getTemp(c, n)
-  body
-  c.freeTemp(tmp)
-
 proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
   var tmp = default(Value)
   gen(c, n, tmp, flags)
@@ -281,14 +280,15 @@ proc genIf(c: var ProcCon; n: PNode; d: var Value) =
     var it = n[i]
     if it.len == 2:
       let info = toLineInfo(c, it[0].info)
-      withTemp(tmp, it[0]):
-        var elsePos: LabelId
-        if isNotOpr(it[0]):
-          c.gen(it[0][1], tmp)
-          elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
-        else:
-          c.gen(it[0], tmp)
-          elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
+      var elsePos: LabelId
+      if isNotOpr(it[0]):
+        let tmp = c.genx(it[0][1])
+        elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
+        c.freeTemp tmp
+      else:
+        let tmp = c.genx(it[0])
+        elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
+        c.freeTemp tmp
       c.clearDest(n, d)
       if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d`
         c.genScope(it[1])
@@ -404,22 +404,23 @@ proc genCase(c: var ProcCon; n: PNode; d: var Value) =
   var sections = newSeqOfCap[LabelId](n.len-1)
   let ending = newLabel(c.labelGen)
   let info = toLineInfo(c, n.info)
-  withTemp(tmp, n[0]):
-    buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ):
-      c.gen(n[0], tmp)
-      for i in 1..<n.len:
-        let section = newLabel(c.labelGen)
-        sections.add section
-        let it = n[i]
-        let itinfo = toLineInfo(c, it.info)
-        build c.code, itinfo, SelectPair:
-          build c.code, itinfo, SelectList:
-            for j in 0..<it.len-1:
-              if it[j].kind == nkRange:
-                caseRange c, it[j]
-              else:
-                caseValue c, it[j]
-          c.code.addLabel itinfo, Goto, section
+  let tmp = c.genx(n[0])
+  buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ):
+    c.code.copyTree tmp
+    for i in 1..<n.len:
+      let section = newLabel(c.labelGen)
+      sections.add section
+      let it = n[i]
+      let itinfo = toLineInfo(c, it.info)
+      build c.code, itinfo, SelectPair:
+        build c.code, itinfo, SelectList:
+          for j in 0..<it.len-1:
+            if it[j].kind == nkRange:
+              caseRange c, it[j]
+            else:
+              caseValue c, it[j]
+        c.code.addLabel itinfo, Goto, section
+  c.freeTemp tmp
   for i in 1..<n.len:
     let it = n[i]
     let itinfo = toLineInfo(c, it.info)
@@ -2131,6 +2132,30 @@ proc genAsgn2(c: var ProcCon; a, b: PNode) =
   var d = c.genx(a)
   c.gen b, d
 
+proc irModule(c: var ProcCon; owner: PSym): string =
+  #if owner == c.m.module: "" else:
+  customPath(toFullPath(c.config, owner.info))
+
+proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
+  result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
+
+proc genForeignVar(c: var ProcCon; s: PSym) =
+  var opc: Opcode
+  if s.kind == skConst:
+    opc = SummonConst
+  elif sfThread in s.flags:
+    opc = SummonThreadLocal
+  else:
+    assert sfGlobal in s.flags
+    opc = SummonGlobal
+  let t = typeToIr(c.m, s.typ)
+  let info = toLineInfo(c, s.info)
+  build c.code, info, ForeignDecl:
+    buildTyped c.code, info, opc, t:
+      build c.code, info, ModuleSymUse:
+        c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
+        c.code.addImmediateVal info, s.itemId.item.int
+
 proc genVarSection(c: var ProcCon; n: PNode) =
   for a in n:
     if a.kind == nkCommentStmt: continue
@@ -2143,7 +2168,10 @@ proc genVarSection(c: var ProcCon; n: PNode) =
       if vn.kind == nkSym:
         let s = vn.sym
         var opc: Opcode
-        if sfThread in s.flags:
+        if s.kind == skConst:
+          opc = SummonConst
+          if dontInlineConstant(n, s.astdef): continue
+        elif sfThread in s.flags:
           opc = SummonThreadLocal
         elif sfGlobal in s.flags:
           opc = SummonGlobal
@@ -2172,17 +2200,13 @@ proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) =
 proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) =
   genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0)
 
-proc irModule(c: var ProcCon; owner: PSym): string =
-  #if owner == c.m.module: "" else:
-  customPath(toFullPath(c.config, owner.info))
-
-proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
-  result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
-
 proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   let info = toLineInfo(c, n.info)
   let s = n.sym
   if fromForeignModule(c, s):
+    if s.kind in {skVar, skConst, skLet} and not c.m.pendingVarsAsSet.containsOrIncl(s.itemId):
+      c.m.pendingVars.add s
+
     template body(target) =
       build target, info, ModuleSymUse:
         target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
@@ -2197,10 +2221,15 @@ proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
 proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
   let s = n.sym
   case s.kind
-  of skVar, skForVar, skTemp, skLet, skResult, skParam, skConst:
+  of skConst:
+    if dontInlineConstant(n, s.astdef):
+      genRdVar(c, n, d, flags)
+    else:
+      gen(c, s.astdef, d, flags)
+  of skVar, skForVar, skTemp, skLet, skResult, skParam:
     genRdVar(c, n, d, flags)
   of skProc, skFunc, skConverter, skMethod, skIterator:
-    if not fromForeignModule(c, s):
+    if not c.m.noModularity:
       # anon and generic procs have no AST so we need to remember not to forget
       # to emit these:
       if not c.m.processedProcs.contains(s.itemId):
@@ -2354,12 +2383,13 @@ proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
   valueIntoDest c, info, d, n.typ, body
   freeTemp c, a
 
-proc genParams(c: var ProcCon; params: PNode; prc: PSym) =
+proc genParams(c: var ProcCon; params: PNode; prc: PSym): PSym =
+  result = nil
   if params.len > 0 and resultPos < prc.ast.len:
     let resNode = prc.ast[resultPos]
-    let res = resNode.sym # get result symbol
-    c.code.addSummon toLineInfo(c, res.info), toSymId(c, res),
-      typeToIr(c.m, res.typ), SummonResult
+    result = resNode.sym # get result symbol
+    c.code.addSummon toLineInfo(c, result.info), toSymId(c, result),
+      typeToIr(c.m, result.typ), SummonResult
   elif prc.typ.len > 0 and not isEmptyType(prc.typ[0]) and not isCompileTimeOnly(prc.typ[0]):
     # happens for procs without bodies:
     let t = typeToIr(c.m, prc.typ[0])
@@ -2389,6 +2419,7 @@ proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvent
   of ccNoConvention: ann NoCall
 
 proc genProc(cOuter: var ProcCon; prc: PSym) =
+  if prc.magic notin generatedMagics: return
   if cOuter.m.processedProcs.containsOrIncl(prc.itemId):
     return
   #assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s
@@ -2399,14 +2430,22 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
   inc cOuter.m.inProc
 
   var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config)
-
-  let body = transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions})
-
-  let info = toLineInfo(c, body.info)
-  build c.code, info, ProcDecl:
-    let symId = toSymId(c, prc)
-    addSymDef c.code, info, symId
-    c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
+  let body =
+    if not fromForeignModule(c, prc):
+      transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions})
+    else:
+      nil
+
+  let info = toLineInfo(c, prc.info)
+  build c.code, info, (if body != nil: ProcDecl else: ForeignProcDecl):
+    if body != nil:
+      let symId = toSymId(c, prc)
+      addSymDef c.code, info, symId
+      c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
+    else:
+      build c.code, info, ModuleSymUse:
+        c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc))
+        c.code.addImmediateVal info, prc.itemId.item.int
     addCallConv c, info, prc.typ.callConv
     if sfCompilerProc in prc.flags:
       build c.code, info, PragmaPair:
@@ -2435,11 +2474,16 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
         else:
           c.code.addPragmaId info, ObjExport
 
-    genParams(c, prc.typ.n, prc)
-    gen(c, body)
-    patch c, body, c.exitLabel
-    build c.code, info, Ret:
-      discard
+    let resultSym = genParams(c, prc.typ.n, prc)
+    if body != nil:
+      gen(c, body)
+      patch c, body, c.exitLabel
+      if resultSym != nil:
+        build c.code, info, Ret:
+          c.code.addSymUse info, toSymId(c, resultSym)
+      else:
+        build c.code, info, Ret:
+          c.code.addNop info
 
   #copyTree cOuter.code, c.code
   dec cOuter.m.inProc
@@ -2569,10 +2613,13 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
     localError(c.config, n.info, "cannot generate IR code for " & $n)
 
 proc genPendingProcs(c: var ProcCon) =
-  while c.m.pendingProcs.len > 0:
+  while c.m.pendingProcs.len > 0 or c.m.pendingVars.len > 0:
     let procs = move(c.m.pendingProcs)
     for v in procs:
       genProc(c, v)
+    let vars = move(c.m.pendingVars)
+    for v in vars:
+      genForeignVar(c, v)
 
 proc genStmt*(c: var ProcCon; n: PNode): NodePos =
   result = NodePos c.code.len
diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim
index 90a3035dd..0b9746856 100644
--- a/compiler/nir/cir.nim
+++ b/compiler/nir/cir.nim
@@ -9,8 +9,11 @@
 
 # We produce C code as a list of tokens.
 
-import std / assertions
-import .. / ic / bitabs
+import std / [assertions, syncio, tables, intsets]
+from std / strutils import toOctal
+import .. / ic / [bitabs, rodfiles]
+import nirtypes, nirinsts, nirfiles
+import ../../dist/checksums/src/checksums/md5
 
 type
   Token = LitId # indexing into the tokens BiTable[string]
@@ -18,7 +21,6 @@ type
   PredefinedToken = enum
     IgnoreMe = "<unused>"
     EmptyToken = ""
-    DeclPrefix = "" # the next token is the name of a definition
     CurlyLe = "{"
     CurlyRi = "}"
     ParLe = "("
@@ -29,7 +31,7 @@ type
     Semicolon = ";"
     Comma = ", "
     Space = " "
-    Colon = ":"
+    Colon = ": "
     Dot = "."
     Arrow = "->"
     Star = "*"
@@ -38,36 +40,41 @@ type
     ScopeOpr = "::"
     ConstKeyword = "const "
     StaticKeyword = "static "
-    NimString = "NimString"
-    StrLitPrefix = "(NimChar*)"
-    StrLitNamePrefix = "Qstr"
-    LoopKeyword = "while (true) "
-    WhileKeyword = "while ("
+    ExternKeyword = "extern "
+    WhileKeyword = "while "
     IfKeyword = "if ("
     ElseKeyword = "else "
-    SwitchKeyword = "switch ("
+    SwitchKeyword = "switch "
     CaseKeyword = "case "
     DefaultKeyword = "default:"
     BreakKeyword = "break"
     NullPtr = "nullptr"
     IfNot = "if (!("
     ReturnKeyword = "return "
-
-const
-  ModulePrefix = Token(int(ReturnKeyword)+1)
+    TypedefStruct = "typedef struct "
+    TypedefUnion = "typedef union "
+    IncludeKeyword = "#include "
 
 proc fillTokenTable(tab: var BiTable[string]) =
   for e in EmptyToken..high(PredefinedToken):
     let id = tab.getOrIncl $e
-    assert id == LitId(e)
+    assert id == LitId(e), $(id, " ", ord(e))
 
 type
   GeneratedCode* = object
+    m: NirModule
+    includes: seq[LitId]
+    includedHeaders: IntSet
+    data: seq[LitId]
+    protos: seq[LitId]
     code: seq[LitId]
     tokens: BiTable[string]
+    emittedStrings: IntSet
+    needsPrefix: IntSet
+    mangledModules: Table[LitId, LitId]
 
-proc initGeneratedCode*(): GeneratedCode =
-  result = GeneratedCode(code: @[], tokens: initBiTable[string]())
+proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
+  result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
   fillTokenTable(result.tokens)
 
 proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
@@ -76,3 +83,845 @@ proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
 proc add*(g: var GeneratedCode; s: string) {.inline.} =
   g.code.add g.tokens.getOrIncl(s)
 
+proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
+  result = c.mangledModules.getOrDefault(key, LitId(0))
+  if result == LitId(0):
+    let u {.cursor.} = c.m.lit.strings[key]
+    var last = u.len - len(".nim") - 1
+    var start = last
+    while start >= 0 and u[start] != '/': dec start
+    var sum = getMD5(u)
+    sum.setLen(8)
+    let dest = u.substr(start+1, last) & sum
+    result = c.tokens.getOrIncl(dest)
+    c.mangledModules[key] = result
+
+type
+  CppFile = object
+    f: File
+
+proc write(f: var CppFile; s: string) = write(f.f, s)
+proc write(f: var CppFile; c: char) = write(f.f, c)
+
+proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
+  var indent = 0
+  for i in 0..<s.len:
+    let x = s[i]
+    case x
+    of Token(CurlyLe):
+      inc indent
+      write f, c.tokens[x]
+      write f, "\n"
+      for i in 1..indent*2: write f, ' '
+    of Token(CurlyRi):
+      dec indent
+      write f, c.tokens[x]
+      if i+1 < s.len and s[i+1] == Token(CurlyRi):
+        discard
+      else:
+        write f, "\n"
+        for i in 1..indent*2: write f, ' '
+    of Token(Semicolon):
+      write f, c.tokens[x]
+      if i+1 < s.len and s[i+1] == Token(CurlyRi):
+        discard "no newline before }"
+      else:
+        write f, "\n"
+        for i in 1..indent*2: write f, ' '
+    of Token(NewLine):
+      write f, c.tokens[x]
+      for i in 1..indent*2: write f, ' '
+    else:
+      write f, c.tokens[x]
+
+
+# Type graph
+
+type
+  TypeList = object
+    processed: IntSet
+    s: seq[(TypeId, PredefinedToken)]
+
+proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
+  if not containsOrIncl(dest.processed, int(elem)):
+    dest.s.add (elem, decl)
+
+type
+  TypeOrder = object
+    forwardedDecls, ordered: TypeList
+    typeImpls: Table[string, TypeId]
+    lookedAt: IntSet
+
+proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
+
+proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
+  var ch = child
+  var viaPointer = false
+  while true:
+    case types[ch].kind
+    of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
+      viaPointer = true
+      ch = elementType(types, ch)
+    of LastArrayTy:
+      ch = elementType(types, ch)
+    else:
+      break
+
+  case types[ch].kind
+  of ObjectTy, UnionTy:
+    let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
+    let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
+    if viaPointer:
+      c.forwardedDecls.add obj, decl
+    else:
+      if not containsOrIncl(c.lookedAt, obj.int):
+        traverseObject(types, lit, c, obj)
+      c.ordered.add obj, decl
+  of ArrayTy:
+    if viaPointer:
+      c.forwardedDecls.add ch, TypedefStruct
+    else:
+      if not containsOrIncl(c.lookedAt, ch.int):
+        traverseObject(types, lit, c, ch)
+      c.ordered.add ch, TypedefStruct
+  else:
+    discard "uninteresting type as we only focus on the required struct declarations"
+
+proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
+  for x in sons(types, t):
+    case types[x].kind
+    of FieldDecl:
+      recordDependency types, lit, c, t, x.firstSon
+    of ObjectTy:
+      # inheritance
+      recordDependency types, lit, c, t, x
+    else: discard
+
+proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
+  for t in allTypes(types):
+    if types[t].kind in {ObjectDecl, UnionDecl}:
+      assert types[t.firstSon].kind == NameVal
+      c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
+
+  for t in allTypesIncludingInner(types):
+    case types[t].kind
+    of ObjectDecl, UnionDecl:
+      traverseObject types, lit, c, t
+      let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
+      c.ordered.add t, decl
+    of ArrayTy:
+      traverseObject types, lit, c, t
+      c.ordered.add t, TypedefStruct
+    else: discard
+
+when false:
+  template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
+  template emitType(t: Token) = c.types.add t
+  template emitType(t: PredefinedToken) = c.types.add Token(t)
+
+proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
+  template maybeAddName =
+    if name != "":
+      g.add Space
+      g.add name
+
+  template atom(s: string) =
+    g.add s
+    maybeAddName()
+  case types[t].kind
+  of VoidTy: atom "void"
+  of IntTy: atom "NI" & $types[t].integralBits
+  of UIntTy: atom "NU" & $types[t].integralBits
+  of FloatTy: atom "NF" & $types[t].integralBits
+  of BoolTy: atom "NB" & $types[t].integralBits
+  of CharTy: atom "NC" & $types[t].integralBits
+  of ObjectTy, UnionTy, NameVal, AnnotationVal:
+    atom lit.strings[types[t].litId]
+  of VarargsTy:
+    g.add "..."
+  of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
+    genType g, types, lit, elementType(types, t)
+    g.add Star
+    maybeAddName()
+  of ArrayTy:
+    genType g, types, lit, arrayName(types, t)
+    maybeAddName()
+  of LastArrayTy:
+    genType g, types, lit, elementType(types, t)
+    maybeAddName()
+    g.add BracketLe
+    g.add BracketRi
+  of ProcTy:
+    let (retType, callConv) = returnType(types, t)
+    genType g, types, lit, retType
+    genType g, types, lit, callConv
+    g.add ParLe
+    g.add Star # "(*fn)"
+    maybeAddName()
+    g.add ParRi
+    g.add ParLe
+    var i = 0
+    for ch in params(types, t):
+      if i > 0: g.add Comma
+      genType g, types, lit, ch
+      inc i
+    g.add ParRi
+  of ObjectDecl, UnionDecl:
+    atom lit.strings[types[t.firstSon].litId]
+  of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
+    #raiseAssert "did not expect: " & $types[t].kind
+    g.add "BUG "
+    atom $types[t].kind
+
+proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
+  for (t, declKeyword) in c.forwardedDecls.s:
+    let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
+    let s {.cursor.} = lit.strings[types[name].litId]
+    g.add declKeyword
+    g.add s
+    g.add Space
+    g.add s
+    g.add Semicolon
+
+  for (t, declKeyword) in c.ordered.s:
+    let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
+    let s {.cursor.} = lit.strings[types[name].litId]
+    g.add declKeyword
+    g.add CurlyLe
+    if types[t].kind == ArrayTy:
+      #let name = arrayName(types, t)
+
+      genType g, types, lit, elementType(types, t), "a"
+      g.add BracketLe
+      g.add $arrayLen(types, t)
+      g.add BracketRi
+      g.add Semicolon
+    else:
+      var i = 0
+      for x in sons(types, t):
+        case types[x].kind
+        of FieldDecl:
+          genType g, types, lit, x.firstSon, "F" & $i
+          g.add Semicolon
+          inc i
+        of ObjectTy:
+          genType g, types, lit, x, "P"
+          g.add Semicolon
+        else: discard
+
+    g.add CurlyRi
+    g.add s
+    g.add Semicolon
+
+# Procs
+
+proc toCChar*(c: char; result: var string) {.inline.} =
+  case c
+  of '\0'..'\x1F', '\x7F'..'\xFF':
+    result.add '\\'
+    result.add toOctal(c)
+  of '\'', '\"', '\\', '?':
+    result.add '\\'
+    result.add c
+  else:
+    result.add c
+
+proc makeCString(s: string): string =
+  result = newStringOfCap(s.len + 10)
+  result.add('"')
+  for c in s: toCChar(c, result)
+  result.add('"')
+
+template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
+template emitData(t: Token) = c.data.add t
+template emitData(t: PredefinedToken) = c.data.add Token(t)
+
+proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
+  result = Token(c.tokens.getOrIncl "QStr" & $litId)
+  if not containsOrIncl(c.emittedStrings, int(litId)):
+    let s {.cursor.} = lit.strings[litId]
+    emitData "static const struct "
+    emitData CurlyLe
+    emitData "  NI cap"
+    emitData Semicolon
+    emitData "NC8 data"
+    emitData BracketLe
+    emitData $s.len
+    emitData "+1"
+    emitData BracketRi
+    emitData Semicolon
+    emitData CurlyRi
+    emitData result
+    emitData AsgnOpr
+    emitData CurlyLe
+    emitData $s.len
+    emitData " | NIM_STRLIT_FLAG"
+    emitData Comma
+    emitData makeCString(s)
+    emitData CurlyRi
+    emitData Semicolon
+
+proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
+  let i = lit.numbers[litId]
+  if i > low(int32) and i <= high(int32):
+    c.add $i
+  elif i == low(int32):
+    # Nim has the same bug for the same reasons :-)
+    c.add "(-2147483647 -1)"
+  elif i > low(int64):
+    c.add "IL64("
+    c.add $i
+    c.add ")"
+  else:
+    c.add "(IL64(-9223372036854775807) - IL64(1))"
+
+proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
+
+proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
+  if t[n].kind == SymDef:
+    c.needsPrefix.incl t[n].symId.int
+  gen c, t, n
+
+proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
+  c.add annotation
+  let m: string
+  if t[name].kind == SymDef:
+    m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $t[name].symId
+  else:
+    assert t[name].kind == ModuleSymUse
+    let (x, y) = sons2(t, name)
+    m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
+  genType c, c.m.types, c.m.lit, t[typ].typeId, m
+
+proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
+  assert t[name].kind == SymDef
+  c.add annotation
+  genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $t[name].symId
+  # XXX Use proper names here
+
+proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
+  let signatureBegin = c.code.len
+  let name = n.firstSon
+
+  var prc = n.firstSon
+  next t, prc
+
+  while true:
+    case t[prc].kind
+    of PragmaPair:
+      let (x, y) = sons2(t, prc)
+      let key = cast[PragmaKey](t[x].rawOperand)
+      case key
+      of HeaderImport:
+        let lit = t[y].litId
+        let headerAsStr {.cursor.} = c.m.lit.strings[lit]
+        let header = c.tokens.getOrIncl(headerAsStr)
+        # headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
+        if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
+          if headerAsStr[0] == '#':
+            discard "skip the #include"
+          else:
+            c.includes.add Token(IncludeKeyword)
+          c.includes.add header
+          c.includes.add Token NewLine
+        # do not generate code for importc'ed procs:
+        return
+      of DllImport:
+        let lit = t[y].litId
+        raiseAssert "cannot eval: " & c.m.lit.strings[lit]
+      else: discard
+    of PragmaId: discard
+    else: break
+    next t, prc
+
+  var resultDeclPos = NodePos(-1)
+  if t[prc].kind == SummonResult:
+    resultDeclPos = prc
+    gen c, t, prc.firstSon
+    next t, prc
+  else:
+    c.add "void"
+  c.add Space
+  genSymDef c, t, name
+  c.add ParLe
+  var params = 0
+  while t[prc].kind == SummonParam:
+    if params > 0: c.add Comma
+    let (typ, sym) = sons2(t, prc)
+    genLocal c, t, sym, typ, ""
+    next t, prc
+    inc params
+  if params == 0:
+    c.add "void"
+  c.add ParRi
+
+  for i in signatureBegin ..< c.code.len:
+    c.protos.add c.code[i]
+  c.protos.add Token Semicolon
+
+  if isExtern:
+    c.code.setLen signatureBegin
+  else:
+    c.add CurlyLe
+    if resultDeclPos.int >= 0:
+      gen c, t, resultDeclPos
+    for ch in sonsRest(t, n, prc):
+      gen c, t, ch
+    c.add CurlyRi
+
+template triop(opr) =
+  let (typ, a, b) = sons3(t, n)
+  c.add ParLe
+  c.add ParLe
+  gen c, t, typ
+  c.add ParRi
+  gen c, t, a
+  c.add opr
+  gen c, t, b
+  c.add ParRi
+
+template cmpop(opr) =
+  let (_, a, b) = sons3(t, n)
+  c.add ParLe
+  gen c, t, a
+  c.add opr
+  gen c, t, b
+  c.add ParRi
+
+template binaryop(opr) =
+  let (typ, a) = sons2(t, n)
+  c.add ParLe
+  c.add ParLe
+  gen c, t, typ
+  c.add ParRi
+  c.add opr
+  gen c, t, a
+  c.add ParRi
+
+template checkedBinaryop(opr) =
+  let (typ, labIdx, a, b) = sons4(t, n)
+  let bits = integralBits(c.m.types[t[typ].typeId])
+  let lab = t[labIdx].label
+
+  c.add (opr & $bits)
+  c.add ParLe
+  c.gen t, a
+  c.add Comma
+  c.gen t, b
+  c.add Comma
+  c.add "L" & $lab.int
+  c.add ParRi
+
+template moveToDataSection(body: untyped) =
+  let oldLen = c.code.len
+  body
+  for i in oldLen ..< c.code.len:
+    c.data.add c.code[i]
+  setLen c.code, oldLen
+
+proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
+  case t[n].kind
+  of Nop:
+    discard "nothing to emit"
+  of ImmediateVal:
+    c.add "BUG: " & $t[n].kind
+  of IntVal:
+    genIntLit c, c.m.lit, t[n].litId
+  of StrVal:
+    c.code.add genStrLit(c, c.m.lit, t[n].litId)
+  of Typed:
+    genType c, c.m.types, c.m.lit, t[n].typeId
+  of SymDef, SymUse:
+    let s = t[n].symId
+    if c.needsPrefix.contains(s.int):
+      c.code.add mangleModuleName(c, c.m.namespace)
+      c.add "__"
+      c.add $s
+    else:
+      # XXX Use proper names here
+      c.add "q"
+      c.add $s
+
+  of ModuleSymUse:
+    let (x, y) = sons2(t, n)
+    let u = mangleModuleName(c, t[x].litId)
+    let s = t[y].immediateVal
+    c.code.add u
+    c.add "__"
+    c.add $s
+
+  of NilVal:
+    c.add NullPtr
+  of LoopLabel:
+    c.add WhileKeyword
+    c.add ParLe
+    c.add "1"
+    c.add ParRi
+    c.add CurlyLe
+  of GotoLoop:
+    c.add CurlyRi
+  of Label:
+    let lab = t[n].label
+    c.add "L"
+    c.add $lab.int
+    c.add Colon
+    c.add Semicolon
+  of Goto:
+    let lab = t[n].label
+    c.add "goto L"
+    c.add $lab.int
+    c.add Semicolon
+  of CheckedGoto:
+    discard "XXX todo"
+  of ArrayConstr:
+    c.add CurlyLe
+    var i = 0
+    for ch in sonsFrom1(t, n):
+      if i > 0: c.add Comma
+      c.gen t, ch
+      inc i
+    c.add CurlyRi
+  of ObjConstr:
+    c.add CurlyLe
+    var i = 0
+    for ch in sonsFrom1(t, n):
+      if i mod 2 == 0:
+        if i > 0: c.add Comma
+        c.add ".F" & $t[ch].immediateVal
+        c.add AsgnOpr
+      else:
+        c.gen t, ch
+      inc i
+    c.add CurlyRi
+  of Ret:
+    c.add ReturnKeyword
+    c.gen t, n.firstSon
+    c.add Semicolon
+  of Select:
+    c.add SwitchKeyword
+    c.add ParLe
+    let (_, selector) = sons2(t, n)
+    c.gen t, selector
+    c.add ParRi
+    c.add CurlyLe
+    for ch in sonsFromN(t, n, 2):
+      c.gen t, ch
+    c.add CurlyRi
+  of SelectPair:
+    let (le, ri) = sons2(t, n)
+    c.gen t, le
+    c.gen t, ri
+  of SelectList:
+    for ch in sons(t, n):
+      c.gen t, ch
+  of SelectValue:
+    c.add CaseKeyword
+    c.gen t, n.firstSon
+    c.add Colon
+  of SelectRange:
+    let (le, ri) = sons2(t, n)
+    c.add CaseKeyword
+    c.gen t, le
+    c.add " ... "
+    c.gen t, ri
+    c.add Colon
+  of ForeignDecl:
+    c.data.add LitId(ExternKeyword)
+    c.gen t, n.firstSon
+  of SummonGlobal:
+    moveToDataSection:
+      let (typ, sym) = sons2(t, n)
+      c.genGlobal t, sym, typ, ""
+      c.add Semicolon
+  of SummonThreadLocal:
+    moveToDataSection:
+      let (typ, sym) = sons2(t, n)
+      c.genGlobal t, sym, typ, "__thread "
+      c.add Semicolon
+  of SummonConst:
+    moveToDataSection:
+      let (typ, sym) = sons2(t, n)
+      c.genGlobal t, sym, typ, "const "
+      c.add Semicolon
+  of Summon, SummonResult:
+    let (typ, sym) = sons2(t, n)
+    c.genLocal t, sym, typ, ""
+    c.add Semicolon
+
+  of SummonParam:
+    raiseAssert "SummonParam should have been handled in genProc"
+  of Kill:
+    discard "we don't care about Kill instructions"
+  of AddrOf:
+    let (_, arg) = sons2(t, n)
+    c.add "&"
+    gen c, t, arg
+  of DerefArrayAt:
+    let (_, a, i) = sons3(t, n)
+    gen c, t, a
+    c.add BracketLe
+    gen c, t, i
+    c.add BracketRi
+  of ArrayAt:
+    let (_, a, i) = sons3(t, n)
+    gen c, t, a
+    c.add Dot
+    c.add "a"
+    c.add BracketLe
+    gen c, t, i
+    c.add BracketRi
+  of FieldAt:
+    let (_, a, b) = sons3(t, n)
+    gen c, t, a
+    let field = t[b].immediateVal
+    c.add Dot
+    c.add "F" & $field
+  of DerefFieldAt:
+    let (_, a, b) = sons3(t, n)
+    gen c, t, a
+    let field = t[b].immediateVal
+    c.add Arrow
+    c.add "F" & $field
+  of Load:
+    let (_, arg) = sons2(t, n)
+    c.add ParLe
+    c.add "*"
+    gen c, t, arg
+    c.add ParRi
+  of Store:
+    raiseAssert "Assumption was that Store is unused!"
+  of Asgn:
+    let (_, dest, src) = sons3(t, n)
+    gen c, t, dest
+    c.add AsgnOpr
+    gen c, t, src
+    c.add Semicolon
+  of CheckedRange:
+    c.add "nimCheckRange"
+    c.add ParLe
+    let (_, gotoInstr, x, a, b) = sons5(t, n)
+    gen c, t, x
+    c.add Comma
+    gen c, t, a
+    c.add Comma
+    gen c, t, b
+    c.add Comma
+    c.add "L" & $t[gotoInstr].label.int
+    c.add ParRi
+  of CheckedIndex:
+    c.add "nimCheckIndex"
+    c.add ParLe
+    let (gotoInstr, x, a) = sons3(t, n)
+    gen c, t, x
+    c.add Comma
+    gen c, t, a
+    c.add Comma
+    c.add "L" & $t[gotoInstr].label.int
+    c.add ParRi
+  of Call, IndirectCall:
+    let (typ, fn) = sons2(t, n)
+    gen c, t, fn
+    c.add ParLe
+    var i = 0
+    for ch in sonsFromN(t, n, 2):
+      if i > 0: c.add Comma
+      gen c, t, ch
+      inc i
+    c.add ParRi
+    if c.m.types[t[typ].typeId].kind == VoidTy:
+      c.add Semicolon
+  of CheckedCall, CheckedIndirectCall:
+    let (typ, gotoInstr, fn) = sons3(t, n)
+    gen c, t, fn
+    c.add ParLe
+    var i = 0
+    for ch in sonsFromN(t, n, 3):
+      if i > 0: c.add Comma
+      gen c, t, ch
+      inc i
+    c.add ParRi
+    if c.m.types[t[typ].typeId].kind == VoidTy:
+      c.add Semicolon
+
+  of CheckedAdd: checkedBinaryop "nimAddInt"
+  of CheckedSub: checkedBinaryop "nimSubInt"
+  of CheckedMul: checkedBinaryop "nimMulInt"
+  of CheckedDiv: checkedBinaryop "nimDivInt"
+  of CheckedMod: checkedBinaryop "nimModInt"
+  of Add: triop " + "
+  of Sub: triop " - "
+  of Mul: triop " * "
+  of Div: triop " / "
+  of Mod: triop " % "
+  of BitShl: triop " << "
+  of BitShr: triop " >> "
+  of BitAnd: triop " & "
+  of BitOr: triop " | "
+  of BitXor: triop " ^ "
+  of BitNot: binaryop " ~ "
+  of BoolNot: binaryop " !"
+  of Eq: cmpop " == "
+  of Le: cmpop " <= "
+  of Lt: cmpop " < "
+  of Cast: binaryop ""
+  of NumberConv: binaryop ""
+  of CheckedObjConv: binaryop ""
+  of ObjConv: binaryop ""
+  of Emit: raiseAssert "cannot interpret: Emit"
+  of ProcDecl: genProcDecl c, t, n, false
+  of ForeignProcDecl: genProcDecl c, t, n, true
+  of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
+    c.add "cannot interpret: " & $t[n].kind
+
+const
+  Prelude = """
+/* GENERATED CODE. DO NOT EDIT. */
+
+#ifdef __cplusplus
+#define NB8 bool
+#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
+// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
+#define NB8 _Bool
+#else
+typedef unsigned char NB8; // best effort
+#endif
+
+typedef unsigned char NC8;
+
+typedef float NF32;
+typedef double NF64;
+#if defined(__BORLANDC__) || defined(_MSC_VER)
+typedef signed char NI8;
+typedef signed short int NI16;
+typedef signed int NI32;
+typedef __int64 NI64;
+/* XXX: Float128? */
+typedef unsigned char NU8;
+typedef unsigned short int NU16;
+typedef unsigned int NU32;
+typedef unsigned __int64 NU64;
+#elif defined(HAVE_STDINT_H)
+#ifndef USE_NIM_NAMESPACE
+#  include <stdint.h>
+#endif
+typedef int8_t NI8;
+typedef int16_t NI16;
+typedef int32_t NI32;
+typedef int64_t NI64;
+typedef uint8_t NU8;
+typedef uint16_t NU16;
+typedef uint32_t NU32;
+typedef uint64_t NU64;
+#elif defined(HAVE_CSTDINT)
+#ifndef USE_NIM_NAMESPACE
+#  include <cstdint>
+#endif
+typedef std::int8_t NI8;
+typedef std::int16_t NI16;
+typedef std::int32_t NI32;
+typedef std::int64_t NI64;
+typedef std::uint8_t NU8;
+typedef std::uint16_t NU16;
+typedef std::uint32_t NU32;
+typedef std::uint64_t NU64;
+#else
+/* Unknown compiler/version, do our best */
+#ifdef __INT8_TYPE__
+typedef __INT8_TYPE__ NI8;
+#else
+typedef signed char NI8;
+#endif
+#ifdef __INT16_TYPE__
+typedef __INT16_TYPE__ NI16;
+#else
+typedef signed short int NI16;
+#endif
+#ifdef __INT32_TYPE__
+typedef __INT32_TYPE__ NI32;
+#else
+typedef signed int NI32;
+#endif
+#ifdef __INT64_TYPE__
+typedef __INT64_TYPE__ NI64;
+#else
+typedef long long int NI64;
+#endif
+/* XXX: Float128? */
+#ifdef __UINT8_TYPE__
+typedef __UINT8_TYPE__ NU8;
+#else
+typedef unsigned char NU8;
+#endif
+#ifdef __UINT16_TYPE__
+typedef __UINT16_TYPE__ NU16;
+#else
+typedef unsigned short int NU16;
+#endif
+#ifdef __UINT32_TYPE__
+typedef __UINT32_TYPE__ NU32;
+#else
+typedef unsigned int NU32;
+#endif
+#ifdef __UINT64_TYPE__
+typedef __UINT64_TYPE__ NU64;
+#else
+typedef unsigned long long int NU64;
+#endif
+#endif
+
+#ifdef NIM_INTBITS
+#  if NIM_INTBITS == 64
+typedef NI64 NI;
+typedef NU64 NU;
+#  elif NIM_INTBITS == 32
+typedef NI32 NI;
+typedef NU32 NU;
+#  elif NIM_INTBITS == 16
+typedef NI16 NI;
+typedef NU16 NU;
+#  elif NIM_INTBITS == 8
+typedef NI8 NI;
+typedef NU8 NU;
+#  else
+#    error "invalid bit width for int"
+#  endif
+#endif
+
+#define NIM_STRLIT_FLAG ((NU64)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
+
+#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
+#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
+#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
+
+#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
+#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
+#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
+
+#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
+#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
+
+"""
+
+proc generateCode*(inp, outp: string) =
+  var c = initGeneratedCode(load(inp))
+
+  var co = TypeOrder()
+  traverseTypes(c.m.types, c.m.lit, co)
+
+  generateTypes(c, c.m.types, c.m.lit, co)
+  let typeDecls = move c.code
+
+  var i = NodePos(0)
+  while i.int < c.m.code.len:
+    gen c, c.m.code, NodePos(i)
+    next c.m.code, i
+
+  var f = CppFile(f: open(outp, fmWrite))
+  f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
+  f.write Prelude
+  writeTokenSeq f, c.includes, c
+  writeTokenSeq f, typeDecls, c
+  writeTokenSeq f, c.data, c
+  writeTokenSeq f, c.protos, c
+  writeTokenSeq f, c.code, c
+  f.f.close
diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim
index 363507ca6..a2cf69988 100644
--- a/compiler/nir/nirc.nim
+++ b/compiler/nir/nirc.nim
@@ -7,10 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
-## Nir Compiler. Currently only supports a "view" command.
+## Nir Compiler.
 
 import ".." / ic / [bitabs, rodfiles]
-import nirinsts, nirtypes, nirlineinfos, nirfiles #, nir2gcc
+import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
 
 proc view(filename: string) =
   let m = load(filename)
@@ -20,14 +20,10 @@ proc view(filename: string) =
   nirtypes.toString res, m.types
   echo res
 
-proc libgcc(filename: string) =
-  let m = load(filename)
-  #gcc m, filename
-
 import std / [syncio, parseopt]
 
 proc writeHelp =
-  echo """Usage: nirc view|gcc <file.nir>"""
+  echo """Usage: nirc view|c <file.nir>"""
   quit 0
 
 proc main =
@@ -49,7 +45,8 @@ proc main =
   case cmd
   of "", "view":
     view inp
-  of "gcc":
-   libgcc inp
+  of "c":
+    let outp = inp & ".c"
+    cir.generateCode inp, outp
 
 main()
diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim
index f6c73178b..cd5a79f06 100644
--- a/compiler/nir/nirfiles.nim
+++ b/compiler/nir/nirfiles.nim
@@ -16,6 +16,8 @@ type
     man*: LineInfoManager
     types*: TypeGraph
     lit*: Literals
+    namespace*: LitId
+    intbits*: uint32
     symnames*: SymNames
 
 proc load*(filename: string): NirModule =
@@ -39,6 +41,10 @@ proc load*(filename: string): NirModule =
     r.loadSection sideChannelSection
     r.load result.man
 
+    r.loadSection namespaceSection
+    r.loadPrim result.namespace
+    r.loadPrim result.intbits
+
     r.loadSection symnamesSection
     r.load result.symnames
 
@@ -64,6 +70,10 @@ proc store*(m: NirModule; outp: string) =
     r.storeSection sideChannelSection
     r.store m.man
 
+    r.storeSection namespaceSection
+    r.storePrim m.namespace
+    r.storePrim m.intbits
+
     r.storeSection symnamesSection
     r.store m.symnames
 
diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim
index 5c2901540..6cffc1a89 100644
--- a/compiler/nir/nirinsts.nim
+++ b/compiler/nir/nirinsts.nim
@@ -54,6 +54,7 @@ type
     SelectList,  # (values...)
     SelectValue, # (value)
     SelectRange, # (valueA..valueB)
+    ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
     SummonGlobal,
     SummonThreadLocal,
     Summon, # x = Summon Typed <Type ID>; x begins to live
@@ -108,6 +109,7 @@ type
     TestOf,
     Emit,
     ProcDecl,
+    ForeignProcDecl,
     PragmaPair
 
 type
@@ -278,6 +280,14 @@ iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
 
 template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
 
+iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
+  var pos = n.int
+  assert tree[parent].kind > LastAtomicValue
+  let last = parent.int + tree[parent].rawSpan
+  while pos < last:
+    yield NodePos pos
+    nextChild tree, pos
+
 proc span(tree: Tree; pos: int): int {.inline.} =
   if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
 
@@ -290,19 +300,36 @@ proc copyTree*(dest: var Tree; src: Tree) =
   for i in 0..<L:
     dest.nodes[d+i] = src.nodes[pos+i]
 
-proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) =
+proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
   assert(not isAtom(tree, n.int))
   let a = n.int+1
   let b = a + span(tree, a)
   result = (NodePos a, NodePos b)
 
-proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) =
+proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
   assert(not isAtom(tree, n.int))
   let a = n.int+1
   let b = a + span(tree, a)
   let c = b + span(tree, b)
   result = (NodePos a, NodePos b, NodePos c)
 
+proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  let d = c + span(tree, c)
+  result = (NodePos a, NodePos b, NodePos c, NodePos d)
+
+proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  let d = c + span(tree, c)
+  let e = d + span(tree, d)
+  result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
+
 proc typeId*(ins: Instr): TypeId {.inline.} =
   assert ins.kind == Typed
   result = TypeId(ins.operand)
@@ -358,6 +385,9 @@ proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
 proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
   t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
 
+proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
+  t.nodes.add Instr(x: toX(Nop, 0'u32), 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)
diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim
index d1c7c6084..a79bf6d01 100644
--- a/compiler/nir/nirtypes.nim
+++ b/compiler/nir/nirtypes.nim
@@ -177,11 +177,35 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
   let c = b + span(tree, b)
   result = (TypeId a, TypeId b, TypeId c)
 
+proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
+  assert tree[n].kind == ArrayTy
+  let (_, _, c) = sons3(tree, n)
+  result = c
+
 proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
   assert tree[n].kind == ArrayTy
   let (_, b) = sons2(tree, n)
   result = tree.lit.numbers[LitId tree[b].operand]
 
+proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
+  # Returns the positions of the return type + calling convention.
+  var pos = n.int
+  assert tree.nodes[pos].kind == ProcTy
+  let a = n.int+1
+  let b = a + span(tree, a)
+  result = (TypeId b, TypeId a) # not a typo, order is reversed
+
+iterator params*(tree: TypeGraph; n: TypeId): TypeId =
+  var pos = n.int
+  assert tree.nodes[pos].kind == ProcTy
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  nextChild tree, pos
+  nextChild tree, pos
+  while pos < last:
+    yield TypeId pos
+    nextChild tree, pos
+
 proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
   assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
     ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
@@ -356,10 +380,12 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
     dest.add "]"
   of ArrayTy:
     dest.add "Array["
-    let (elems, len) = g.sons2(i)
+    let (elems, len, name) = g.sons3(i)
     toString(dest, g, elems)
     dest.add ", "
     toString(dest, g, len)
+    dest.add ", "
+    toString(dest, g, name)
     dest.add "]"
   of LastArrayTy:
     # array of unspecified size as a last field inside an object
@@ -421,6 +447,12 @@ iterator allTypes*(g: TypeGraph; start = 0): TypeId =
     yield TypeId i
     nextChild g, i
 
+iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId =
+  var i = start
+  while i < g.len:
+    yield TypeId i
+    inc i
+
 proc `$`(g: TypeGraph): string =
   result = ""
   toString(result, g)
@@ -431,6 +463,7 @@ when isMainModule:
   let a = g.openType ArrayTy
   g.addBuiltinType Int8Id
   g.addArrayLen 5
+  g.addName "SomeArray"
   let finalArrayType = finishType(g, a)
 
   let obj = g.openType ObjectDecl
diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim
index a3d69a296..faa7a1fb7 100644
--- a/compiler/nir/nirvm.nim
+++ b/compiler/nir/nirvm.nim
@@ -421,7 +421,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla
       for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr})
 
   case t[n].kind
-  of Nop:
+  of Nop, ForeignDecl, ForeignProcDecl:
     discard "don't use Nop"
   of ImmediateVal:
     bc.add info, ImmediateValM, t[n].rawOperand
diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim
index 4c3ce7001..9c9513284 100644
--- a/compiler/nir/types2ir.nim
+++ b/compiler/nir/types2ir.nim
@@ -14,6 +14,7 @@ import nirtypes
 type
   TypesCon* = object
     processed: Table[ItemId, TypeId]
+    processedByName: Table[string, TypeId]
     recursionCheck: HashSet[ItemId]
     conf: ConfigRef
     stringType: TypeId
@@ -30,6 +31,13 @@ template cached(c: var TypesCon; t: PType; body: untyped) =
     body
     c.processed[t.itemId] = result
 
+template cachedByName(c: var TypesCon; t: PType; body: untyped) =
+  let key = mangle(c, t)
+  result = c.processedByName.getOrDefault(key)
+  if result.int == 0:
+    body
+    c.processedByName[key] = result
+
 proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
 
 proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
@@ -273,10 +281,15 @@ proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeI
   let f = g.openType FieldDecl
   let arr = g.openType LastArrayTy
   g.addType elementType
-  result[1] = finishType(g, arr) # LastArrayTy
+  # DO NOT USE `finishType` here as it is an inner type. This is subtle and we
+  # probably need an even better API here.
+  sealType(g, arr)
+  result[1] = TypeId(arr)
+
   g.addOffset c.conf.target.ptrSize
   g.addName "data"
   sealType(g, f) # FieldDecl
+
   sealType(g, p)
 
 proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
@@ -413,6 +426,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
       let a = openType(g, ArrayTy)
       g.addType(elemType)
       g.addArrayLen n
+      g.addName mangle(c, t)
       result = finishType(g, a)
   of tyPtr, tyRef:
     cached(c, t):
@@ -451,6 +465,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
         let a = openType(g, ArrayTy)
         g.addType(UInt8Id)
         g.addArrayLen s
+        g.addName mangle(c, t)
         result = finishType(g, a)
   of tyPointer, tyNil:
     # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
@@ -467,7 +482,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
       else:
         result = objectHeaderToIr(c, g, t)
   of tyTuple:
-    cached(c, t):
+    cachedByName(c, t):
       result = tupleToIr(c, g, t)
   of tyProc:
     cached(c, t):
@@ -485,7 +500,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
     else:
       result = c.stringType
   of tySequence:
-    cached(c, t):
+    cachedByName(c, t):
       result = seqToIr(c, g, t)
   of tyCstring:
     cached(c, t):