summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-11-07 11:25:57 +0100
committerGitHub <noreply@github.com>2023-11-07 11:25:57 +0100
commite081f565cb658a173c0a3fbe562beda937ed2cc4 (patch)
treeb262afca004f156f32ac377818b8f245eb01e56f /compiler
parentf5bbdaf9069f59fd4baeb977f49bb6caa19343af (diff)
downloadNim-e081f565cb658a173c0a3fbe562beda937ed2cc4.tar.gz
IC: use better packed line information format (#22917)
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ic/dce.nim4
-rw-r--r--compiler/ic/ic.nim116
-rw-r--r--compiler/ic/integrity.nim12
-rw-r--r--compiler/ic/navigator.nim63
-rw-r--r--compiler/ic/packed_ast.nim82
-rw-r--r--compiler/ic/rodfiles.nim2
-rw-r--r--compiler/nir/nirlineinfos.nim3
7 files changed, 135 insertions, 147 deletions
diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim
index ce6422101..49669e4e2 100644
--- a/compiler/ic/dce.nim
+++ b/compiler/ic/dce.nim
@@ -112,8 +112,8 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
     followLater(c, g, c.thisModule, n.operand)
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
     let m = n1.litId
     let item = n2.operand
     let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim
index 4d02822b2..1b52c82b0 100644
--- a/compiler/ic/ic.nim
+++ b/compiler/ic/ic.nim
@@ -16,6 +16,8 @@ from std/os import removeFile, isAbsolute
 
 import ../../dist/checksums/src/checksums/sha1
 
+import ".." / nir / nirlineinfos
+
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions, formatfloat]
 
@@ -60,6 +62,7 @@ type
     strings*: BiTable[string] # we could share these between modules.
     numbers*: BiTable[BiggestInt] # we also store floats in here so
                                   # that we can assure that every bit is kept
+    man*: LineInfoManager
 
     cfg: PackedConfig
 
@@ -75,37 +78,36 @@ type
     symMarker*: IntSet #Table[ItemId, SymId]    # ItemId.item -> SymId
     config*: ConfigRef
 
-proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int;
+proc toString*(tree: PackedTree; pos: NodePos; m: PackedModule; nesting: int;
                result: var string) =
-  let pos = n.int
   if result.len > 0 and result[^1] notin {' ', '\n'}:
     result.add ' '
 
   result.add $tree[pos].kind
-  case tree.nodes[pos].kind
-  of nkNone, nkEmpty, nkNilLit, nkType: discard
+  case tree[pos].kind
+  of nkEmpty, nkNilLit, nkType: discard
   of nkIdent, nkStrLit..nkTripleStrLit:
     result.add " "
-    result.add m.strings[LitId tree.nodes[pos].operand]
+    result.add m.strings[LitId tree[pos].operand]
   of nkSym:
     result.add " "
-    result.add m.strings[m.syms[tree.nodes[pos].operand].name]
+    result.add m.strings[m.syms[tree[pos].operand].name]
   of directIntLit:
     result.add " "
-    result.addInt tree.nodes[pos].operand
+    result.addInt tree[pos].operand
   of externSIntLit:
     result.add " "
-    result.addInt m.numbers[LitId tree.nodes[pos].operand]
+    result.addInt m.numbers[LitId tree[pos].operand]
   of externUIntLit:
     result.add " "
-    result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand])
+    result.addInt cast[uint64](m.numbers[LitId tree[pos].operand])
   of nkFloatLit..nkFloat128Lit:
     result.add " "
-    result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand])
+    result.addFloat cast[BiggestFloat](m.numbers[LitId tree[pos].operand])
   else:
     result.add "(\n"
     for i in 1..(nesting+1)*2: result.add ' '
-    for child in sonsReadonly(tree, n):
+    for child in sonsReadonly(tree, pos):
       toString(tree, child, m, nesting + 1, result)
     result.add "\n"
     for i in 1..nesting*2: result.add ' '
@@ -284,7 +286,8 @@ proc toLitId(x: BiggestInt; m: var PackedModule): LitId =
   result = getOrIncl(m.numbers, x)
 
 proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo =
-  PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
+  pack(m.man, toLitId(x.fileIndex, c, m), x.line.int32, x.col.int32)
+  #PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
 
 proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} =
   ## given a symbol, produce an ItemId with the correct properties
@@ -421,53 +424,49 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
 proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
   ## add a remote symbol reference to the tree
   let info = n.info.toPackedInfo(c, m)
-  ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
-                          typeId: storeTypeLater(n.typ, c, m), info: info)
-  ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
-                          operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
-  ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
-                          operand: n.sym.itemId.item)
+  ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
+             typeId = storeTypeLater(n.typ, c, m), info = info)
+  ir.addNode(kind = nkNone, info = info,
+             operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
+  ir.addNode(kind = nkNone, info = info,
+             operand = n.sym.itemId.item)
 
 proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
   ## serialize a node into the tree
   if n == nil:
-    ir.nodes.add PackedNode(kind: nkNilRodNode, flags: {}, operand: 1)
+    ir.addNode(kind = nkNilRodNode, operand = 1, info = NoLineInfo)
     return
   let info = toPackedInfo(n.info, c, m)
   case n.kind
   of nkNone, nkEmpty, nkNilLit, nkType:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0,
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags, operand = 0,
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkIdent:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.strings, n.ident.s),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+                operand = int32 getOrIncl(m.strings, n.ident.s),
+                typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkSym:
     if n.sym.itemId.module == c.thisModule:
       # it is a symbol that belongs to the module we're currently
       # packing:
       let id = n.sym.storeSymLater(c, m).item
-      ir.nodes.add PackedNode(kind: nkSym, flags: n.flags, operand: id,
-                              typeId: storeTypeLater(n.typ, c, m), info: info)
+      ir.addNode(kind = nkSym, flags = n.flags, operand = id,
+                 typeId = storeTypeLater(n.typ, c, m), info = info)
     else:
       # store it as an external module reference:
       addModuleRef(n, ir, c, m)
-  of directIntLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32(n.intVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
   of externIntLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.numbers, n.intVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, n.intVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkStrLit..nkTripleStrLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.strings, n.strVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.strings, n.strVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkFloatLit..nkFloat128Lit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   else:
     let patchPos = ir.prepare(n.kind, n.flags,
                               storeTypeLater(n.typ, c, m), info)
@@ -492,8 +491,8 @@ proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var
       # do not serialize the body of the proc, it's unnecessary since
       # n[0].sym.ast has the sem'checked variant of it which is what
       # everybody should use instead.
-      ir.nodes.add PackedNode(kind: nkEmpty, flags: {}, operand: 0,
-                              typeId: nilItemId, info: info)
+      ir.addNode(kind = nkEmpty, flags = {}, operand = 0,
+                 typeId = nilItemId, info = info)
   ir.patch patchPos
 
 proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
@@ -609,9 +608,9 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
   loadSeqSection methodsSection, m.methods
   loadSeqSection pureEnumsSection, m.pureEnums
 
-  loadSeqSection toReplaySection, m.toReplay.nodes
-  loadSeqSection topLevelSection, m.topLevel.nodes
-  loadSeqSection bodiesSection, m.bodies.nodes
+  loadTabSection toReplaySection, m.toReplay
+  loadTabSection topLevelSection, m.topLevel
+  loadTabSection bodiesSection, m.bodies
   loadSeqSection symsSection, m.syms
   loadSeqSection typesSection, m.types
 
@@ -625,6 +624,9 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
   f.loadSection backendFlagsSection
   f.loadPrim m.backendFlags
 
+  f.loadSection sideChannelSection
+  f.load m.man
+
   close(f)
   result = f.err
 
@@ -672,10 +674,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
   storeSeqSection methodsSection, m.methods
   storeSeqSection pureEnumsSection, m.pureEnums
 
-  storeSeqSection toReplaySection, m.toReplay.nodes
-  storeSeqSection topLevelSection, m.topLevel.nodes
+  storeTabSection toReplaySection, m.toReplay
+  storeTabSection topLevelSection, m.topLevel
 
-  storeSeqSection bodiesSection, m.bodies.nodes
+  storeTabSection bodiesSection, m.bodies
   storeSeqSection symsSection, m.syms
 
   storeSeqSection typesSection, m.types
@@ -690,6 +692,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
   f.storeSection backendFlagsSection
   f.storePrim m.backendFlags
 
+  f.storeSection sideChannelSection
+  f.store m.man
+
   close(f)
   encoder.disable()
   if f.err != ok:
@@ -748,8 +753,9 @@ proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule:
 proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
                        x: PackedLineInfo): TLineInfo =
   assert g[thisModule].status in {loaded, storing, stored}
-  result = TLineInfo(line: x.line, col: x.col,
-            fileIndex: toFileIndexCached(c, g, thisModule, x.file))
+  let (fileId, line, col) = unpack(g[thisModule].fromDisk.man, x)
+  result = TLineInfo(line: line.uint16, col: col.int16,
+            fileIndex: toFileIndexCached(c, g, thisModule, fileId))
 
 proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
                 tree: PackedTree; n: NodePos): PNode =
@@ -768,9 +774,9 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
   of nkIdent:
     result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
   of nkSym:
-    result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].operand))
   of directIntLit:
-    result.intVal = tree.nodes[n.int].operand
+    result.intVal = tree[n].operand
   of externIntLit:
     result.intVal = g[thisModule].fromDisk.numbers[n.litId]
   of nkStrLit..nkTripleStrLit:
@@ -779,10 +785,10 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
     result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.numbers[n.litId])
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
     transitionNoneToSym(result)
-    result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].operand))
   else:
     for n0 in sonsReadonly(tree, n):
       result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
@@ -1239,5 +1245,5 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
       echo "  <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
 
   echo "symbols: ", m.syms.len, " types: ", m.types.len,
-    " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len,
+    " top level nodes: ", m.topLevel.len, " other nodes: ", m.bodies.len,
     " strings: ", m.strings.len, " numbers: ", m.numbers.len
diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim
index ed87ae59d..00014f15a 100644
--- a/compiler/ic/integrity.nim
+++ b/compiler/ic/integrity.nim
@@ -72,15 +72,15 @@ proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
     c.thisModule = oldThisModule
 
 proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
-  if tree[n.int].typeId != nilItemId:
-    checkType(c, tree[n.int].typeId)
+  if tree[n].typeId != nilItemId:
+    checkType(c, tree[n].typeId)
   case n.kind
   of nkEmpty, nkNilLit, nkType, nkNilRodNode:
     discard
   of nkIdent:
     assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
   of nkSym:
-    checkLocalSym(c, tree.nodes[n.int].operand)
+    checkLocalSym(c, tree[n].operand)
   of directIntLit:
     discard
   of externIntLit, nkFloatLit..nkFloat128Lit:
@@ -89,9 +89,9 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
     assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
-    checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
+    checkForeignSym(c, PackedItemId(module: n1.litId, item: tree[n2].operand))
   else:
     for n0 in sonsReadonly(tree, n):
       checkNode(c, tree, n0)
diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim
index aea5e12e7..c2d7ee4ad 100644
--- a/compiler/ic/navigator.nim
+++ b/compiler/ic/navigator.nim
@@ -20,20 +20,25 @@ when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import ".." / [ast, modulegraphs, msgs, options]
+import ".." / nir / nirlineinfos
 import packed_ast, bitabs, ic
 
 type
+  UnpackedLineInfo = object
+    file: LitId
+    line, col: int
   NavContext = object
     g: ModuleGraph
     thisModule: int32
-    trackPos: PackedLineInfo
+    trackPos: UnpackedLineInfo
     alreadyEmitted: HashSet[string]
     outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
 
-proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool =
-  if current.file == trackPos.file and current.line == trackPos.line:
+proc isTracked(man: LineInfoManager; current: PackedLineInfo, trackPos: UnpackedLineInfo, tokenLen: int): bool =
+  let (currentFile, currentLine, currentCol) = man.unpack(current)
+  if currentFile == trackPos.file and currentLine == trackPos.line:
     let col = trackPos.col
-    if col >= current.col and col < current.col+tokenLen:
+    if col >= currentCol and col < currentCol+tokenLen:
       result = true
     else:
       result = false
@@ -42,32 +47,34 @@ proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool =
 
 proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
   result = s.name != LitId(0) and
-    isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
 
 proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
   let name = c.g.packed[s.module].fromDisk.syms[s.item].name
   result = name != LitId(0) and
-    isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
 
 const
   EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
 
 proc search(c: var NavContext; tree: PackedTree): ItemId =
   # We use the linear representation here directly:
-  for i in 0..high(tree.nodes):
-    case tree.nodes[i].kind
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
     of nkSym:
-      let item = tree.nodes[i].operand
-      if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info):
+      let item = tree[i].operand
+      if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree[i].info):
         return ItemId(module: c.thisModule, item: item)
     of nkModuleRef:
-      if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file:
-        let (n1, n2) = sons2(tree, NodePos i)
+      let (currentFile, currentLine, currentCol) = c.g.packed[c.thisModule].fromDisk.man.unpack(tree[i].info)
+      if currentLine == c.trackPos.line and currentFile == c.trackPos.file:
+        let (n1, n2) = sons2(tree, i)
         assert n1.kind == nkInt32Lit
         assert n2.kind == nkInt32Lit
-        let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
+        let pId = PackedItemId(module: n1.litId, item: tree[n2].operand)
         let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
-        if searchForeignSym(c, itemId, tree.nodes[i].info):
+        if searchForeignSym(c, itemId, tree[i].info):
           return itemId
     else: discard
   return EmptyItemId
@@ -77,32 +84,34 @@ proc isDecl(tree: PackedTree; n: NodePos): bool =
   const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
     nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
     nkIdentDefs, nkEnumTy, nkVarTuple}
-  result = n.int >= 0 and tree[n.int].kind in declarativeNodes
+  result = n.int >= 0 and tree[n].kind in declarativeNodes
 
 proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
+  let (fileId, line, col) = unpack(c.g.packed[c.thisModule].fromDisk.man, info)
   var m = ""
-  var file = c.g.packed[c.thisModule].fromDisk.strings[info.file]
+  var file = c.g.packed[c.thisModule].fromDisk.strings[fileId]
   if c.outputSep == ' ':
     file = os.extractFilename file
-  toLocation(m, file, info.line.int, info.col.int + ColOffset)
+  toLocation(m, file, line, col + ColOffset)
   if not c.alreadyEmitted.containsOrIncl(m):
     msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
 
 proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
-  for i in 0..high(tree.nodes):
-    case tree.nodes[i].kind
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
     of nkSym:
-      let item = tree.nodes[i].operand
+      let item = tree[i].operand
       if sym.item == item and sym.module == c.thisModule:
-        usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
     of nkModuleRef:
-      let (n1, n2) = sons2(tree, NodePos i)
-      assert n1.kind == nkInt32Lit
-      assert n2.kind == nkInt32Lit
-      let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
+      let (n1, n2) = sons2(tree, i)
+      assert n1.kind == nkNone
+      assert n2.kind == nkNone
+      let pId = PackedItemId(module: n1.litId, item: tree[n2].operand)
       let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
       if itemId.item == sym.item and sym.module == itemId.module:
-        usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
     else: discard
 
 proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
@@ -138,7 +147,7 @@ proc nav(g: ModuleGraph) =
   var c = NavContext(
     g: g,
     thisModule: int32 mid,
-    trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
+    trackPos: UnpackedLineInfo(line: unpacked.line.int, col: unpacked.col.int, file: fileId),
     outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
   )
   var symId = search(c, g.packed[mid].fromDisk.topLevel)
diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim
index e7443c3c7..ea7c7f967 100644
--- a/compiler/ic/packed_ast.nim
+++ b/compiler/ic/packed_ast.nim
@@ -13,9 +13,11 @@
 ## it is superior.
 
 import std/[hashes, tables, strtabs]
-import bitabs
+import bitabs, rodfiles
 import ".." / [ast, options]
 
+import ".." / nir / nirlineinfos
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -37,11 +39,6 @@ const
   emptyNodeId* = NodeId(-1)
 
 type
-  PackedLineInfo* = object
-    line*: uint16
-    col*: int16
-    file*: LitId
-
   PackedLib* = object
     kind*: TLibKind
     generated*: bool
@@ -95,13 +92,15 @@ type
     flags*: TNodeFlags
     operand*: int32  # for kind in {nkSym, nkSymDef}: SymId
                      # for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
-                     # for kind in nkInt32Lit: direct value
+                     # for kind in nkNone: direct value
                      # for non-atom kinds: the number of nodes (for easy skipping)
     typeId*: PackedItemId
     info*: PackedLineInfo
 
   PackedTree* = object ## usually represents a full Nim module
-    nodes*: seq[PackedNode]
+    nodes: seq[PackedNode]
+    #withFlags: seq[(int, TNodeFlags)]
+    #withTypes: seq[(int, PackedItemId)]
 
   PackedInstantiation* = object
     key*, sym*: PackedItemId
@@ -118,25 +117,6 @@ proc newTreeFrom*(old: PackedTree): PackedTree =
   result.nodes = @[]
   when false: result.sh = old.sh
 
-when false:
-  proc declareSym*(tree: var PackedTree; kind: TSymKind;
-                  name: LitId; info: PackedLineInfo): SymId =
-    result = SymId(tree.sh.syms.len)
-    tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
-
-  proc litIdFromName*(tree: PackedTree; name: string): LitId =
-    result = tree.sh.strings.getOrIncl(name)
-
-  proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
-    tree.nodes.add PackedNode(kind: kind, info: info,
-                              operand: int32 getOrIncl(tree.sh.strings, token))
-
-  proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
-    tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
-
-proc throwAwayLastNode*(tree: var PackedTree) =
-  tree.nodes.setLen(tree.nodes.len-1)
-
 proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
   tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
 
@@ -144,42 +124,25 @@ proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
   tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
 
 proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
-  tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
+  tree.nodes.add PackedNode(kind: nkNone, operand: int32(s), info: info)
 
 proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
   tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
 
 proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
 
-proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
-  # and this is why the IR is superior. We can copy subtrees
-  # via a linear scan.
-  let pos = n.int
-  let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
-  let d = dest.nodes.len
-  dest.nodes.setLen(d + L)
-  for i in 0..<L:
-    dest.nodes[d+i] = tree.nodes[pos+i]
-
-when false:
-  proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
-    result = SymId(dest.sh.syms.len)
-    assert int(s) < tree.sh.syms.len
-    let oldSym = tree.sh.syms[s.int]
-    dest.sh.syms.add oldSym
-
 type
   PatchPos = distinct int
 
-when false:
-  proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
-    result = PatchPos tree.nodes.len
-    tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
+proc addNode*(t: var PackedTree; kind: TNodeKind; operand: int32;
+              typeId: PackedItemId = nilItemId; info: PackedLineInfo;
+              flags: TNodeFlags = {}) =
+  t.nodes.add PackedNode(kind: kind, flags: flags, operand: operand,
+                         typeId: typeId, info: info)
 
 proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
   result = PatchPos tree.nodes.len
-  tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
-                            typeId: typeId)
+  tree.addNode(kind = kind, flags = flags, operand = 0, info = info, typeId = typeId)
 
 proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
   result = PatchPos dest.nodes.len
@@ -193,8 +156,8 @@ proc patch*(tree: var PackedTree; pos: PatchPos) =
 
 proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
 
-proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
-  tree.nodes[i]
+proc `[]`*(tree: PackedTree; i: NodePos): lent PackedNode {.inline.} =
+  tree.nodes[i.int]
 
 proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
   if tree.nodes[pos].kind > nkNilLit:
@@ -357,16 +320,17 @@ const
     nkIntLit,
     nkInt8Lit,
     nkInt16Lit,
+    nkInt32Lit,
     nkInt64Lit,
     nkUIntLit,
     nkUInt8Lit,
     nkUInt16Lit,
     nkUInt32Lit,
-    nkUInt64Lit} # nkInt32Lit is missing by design!
+    nkUInt64Lit}
 
-  externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
+  externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit}
   externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
-  directIntLit* = nkInt32Lit
+  directIntLit* = nkNone
 
 when false:
   proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
@@ -420,3 +384,9 @@ iterator allNodes*(tree: PackedTree): NodePos =
 
 proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
   PackedItemId(module: LitId(0), item: item)
+
+proc load*(f: var RodFile; t: var PackedTree) =
+  loadSeq f, t.nodes
+
+proc store*(f: var RodFile; t: PackedTree) =
+  storeSeq f, t.nodes
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index be5095fbb..968bf255f 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -112,7 +112,7 @@ type
                        # better than exceptions.
 
 const
-  RodVersion = 1
+  RodVersion = 2
   defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
             byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
 
diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim
index 5f5d55086..f11ef7c42 100644
--- a/compiler/nir/nirlineinfos.nim
+++ b/compiler/nir/nirlineinfos.nim
@@ -42,6 +42,9 @@ type
   LineInfoManager* = object
     aside: seq[(LitId, int32, int32)]
 
+const
+  NoLineInfo* = PackedLineInfo(0'u32)
+
 proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
   if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
     let col = if col < 0'i32: 0'u32 else: col.uint32