summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim19
-rw-r--r--compiler/canonicalizer_unused.nim (renamed from compiler/canonicalizer.nim)0
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/ccgtypes.nim12
-rw-r--r--compiler/commands.nim17
-rw-r--r--compiler/evaltempl.nim2
-rw-r--r--compiler/ic/packed_ast.nim23
-rw-r--r--compiler/ic/rodfiles.nim48
-rw-r--r--compiler/ic/to_packed_ast.nim433
-rw-r--r--compiler/idents.nim5
-rw-r--r--compiler/importer.nim27
-rw-r--r--compiler/jsgen.nim6
-rw-r--r--compiler/lookups.nim56
-rw-r--r--compiler/magicsys.nim7
-rw-r--r--compiler/modulegraphs.nim104
-rw-r--r--compiler/modules.nim70
-rw-r--r--compiler/nimeval.nim13
-rw-r--r--compiler/packagehandling.nim3
-rw-r--r--compiler/passes.nim8
-rw-r--r--compiler/plugins/itersgen.nim4
-rw-r--r--compiler/pragmas.nim4
-rw-r--r--compiler/sem.nim3
-rw-r--r--compiler/semdata.nim22
-rw-r--r--compiler/semexprs.nim28
-rw-r--r--compiler/semfields.nim2
-rw-r--r--compiler/semgnrc.nim2
-rw-r--r--compiler/seminst.nim4
-rw-r--r--compiler/semstmts.nim12
-rw-r--r--compiler/semtempl.nim2
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--compiler/sighashes.nim2
-rw-r--r--compiler/suggest.nim77
-rw-r--r--compiler/transf.nim15
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmgen.nim13
-rw-r--r--nimsuggest/nimsuggest.nim2
36 files changed, 654 insertions, 401 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ccf4fe497..de4ab5b77 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -833,18 +833,6 @@ type
       procInstCache*: seq[PInstantiation]
       gcUnsafetyReason*: PSym  # for better error messages wrt gcsafe
       transformedBody*: PNode  # cached body after transf pass
-    of skModule, skPackage:
-      # modules keep track of the generic symbols they use from other modules.
-      # this is because in incremental compilation, when a module is about to
-      # be replaced with a newer version, we must decrement the usage count
-      # of all previously used generics.
-      # For 'import as' we copy the module symbol but shallowCopy the 'tab'
-      # and set the 'usedGenerics' to ... XXX gah! Better set module.name
-      # instead? But this doesn't work either. --> We need an skModuleAlias?
-      # No need, just leave it as skModule but set the owner accordingly and
-      # check for the owner when touching 'usedGenerics'.
-      usedGenerics*: seq[PInstantiation]
-      tab*: TStrTable         # interface table for modules
     of skLet, skVar, skField, skForVar:
       guard*: PSym
       bitsize*: int
@@ -1455,8 +1443,6 @@ proc copySym*(s: PSym; id: ItemId): PSym =
   result.typ = s.typ
   result.flags = s.flags
   result.magic = s.magic
-  if s.kind == skModule:
-    copyStrTable(result.tab, s.tab)
   result.options = s.options
   result.position = s.position
   result.loc = s.loc
@@ -1474,13 +1460,10 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
   result.ast = s.ast
   #result.id = s.id # XXX figure out what to do with the ID.
   result.flags = s.flags
-  system.shallowCopy(result.tab, s.tab)
   result.options = s.options
   result.position = s.position
   result.loc = s.loc
   result.annex = s.annex
-  # XXX once usedGenerics is used, ensure module aliases keep working!
-  assert s.usedGenerics.len == 0
 
 proc initStrTable*(x: var TStrTable) =
   x.counter = 0
@@ -1915,8 +1898,6 @@ template incompleteType*(t: PType): bool =
 template typeCompleted*(s: PSym) =
   incl s.flags, sfNoForward
 
-template getBody*(s: PSym): PNode = s.ast[bodyPos]
-
 template detailedInfo*(sym: PSym): string =
   sym.name.s
 
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer_unused.nim
index de0d0f30e..de0d0f30e 100644
--- a/compiler/canonicalizer.nim
+++ b/compiler/canonicalizer_unused.nim
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index b8a8d21b0..440ecdc7f 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1292,7 +1292,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
     genAssignment(p, a, b, {})
   else:
     let ti = genTypeInfoV1(p.module, typ, a.lode.info)
-    if bt.destructor != nil and not isTrivialProc(bt.destructor):
+    if bt.destructor != nil and not isTrivialProc(p.module.g.graph, bt.destructor):
       # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
       # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
       # convention at least:
@@ -2204,7 +2204,7 @@ proc genDestroy(p: BProc; n: PNode) =
     else: discard "nothing to do"
   else:
     let t = n[1].typ.skipTypes(abstractVar)
-    if t.destructor != nil and t.destructor.ast[bodyPos].len != 0:
+    if t.destructor != nil and getBody(p.module.g.graph, t.destructor).len != 0:
       internalError(p.config, n.info, "destructor turned out to be not trivial")
     discard "ignore calls to the default destructor"
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index ab12bad1e..798eaf7f9 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -36,14 +36,6 @@ proc mangleField(m: BModule; name: PIdent): string =
   if isKeyword(name):
     result.add "_0"
 
-when false:
-  proc hashOwner(s: PSym): SigHash =
-    var m = s
-    while m.kind != skModule: m = m.owner
-    let p = m.owner
-    assert p.kind == skPackage
-    result = gDebugInfo.register(p.name.s, m.name.s)
-
 proc mangleName(m: BModule; s: PSym): Rope =
   result = s.loc.r
   if result == nil:
@@ -1313,11 +1305,11 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
     it = it[0]
   result = makeCString(res)
 
-proc isTrivialProc(s: PSym): bool {.inline.} = s.ast[bodyPos].len == 0
+proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
 
 proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
   let theProc = t.attachedOps[op]
-  if theProc != nil and not isTrivialProc(theProc):
+  if theProc != nil and not isTrivialProc(m.g.graph, theProc):
     # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
     # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
     # convention at least:
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 9fb9b7e6e..adb85bdd7 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -801,14 +801,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectNoArg(conf, switch, arg, pass, info)
     helpOnError(conf, pass)
   of "symbolfiles": discard "ignore for backwards compat"
-  of "incremental":
-    case arg.normalize
-    of "on": conf.symbolFiles = v2Sf
-    of "off": conf.symbolFiles = disabledSf
-    of "writeonly": conf.symbolFiles = writeOnlySf
-    of "readonly": conf.symbolFiles = readOnlySf
-    of "v2": conf.symbolFiles = v2Sf
-    else: localError(conf, info, "invalid option for --incremental: " & arg)
+  of "incremental", "ic":
+    if pass in {passCmd2, passPP}:
+      case arg.normalize
+      of "on": conf.symbolFiles = v2Sf
+      of "off": conf.symbolFiles = disabledSf
+      of "writeonly": conf.symbolFiles = writeOnlySf
+      of "readonly": conf.symbolFiles = readOnlySf
+      of "v2": conf.symbolFiles = v2Sf
+      else: localError(conf, info, "invalid option for --incremental: " & arg)
   of "skipcfg":
     processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
   of "skipprojcfg":
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 691d33a2c..a85314ac2 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -187,7 +187,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
   ctx.instID = instID[]
   ctx.idgen = idgen
 
-  let body = tmpl.getBody
+  let body = tmpl.ast[bodyPos]
   #echo "instantion of ", renderTree(body, {renderIds})
   if isAtom(body):
     result = newNodeI(nkPar, body.info)
diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim
index 546e495c5..cbfb0c5d1 100644
--- a/compiler/ic/packed_ast.nim
+++ b/compiler/ic/packed_ast.nim
@@ -17,28 +17,6 @@ import bitabs
 import ".." / [ast, options]
 
 const
-  localNamePos* = 0
-  localExportMarkerPos* = 1
-  localPragmaPos* = 2
-  localTypePos* = 3
-  localValuePos* = 4
-
-  typeNamePos* = 0
-  typeExportMarkerPos* = 1
-  typeGenericParamsPos* = 2
-  typePragmaPos* = 3
-  typeBodyPos* = 4
-
-  routineNamePos* = 0
-  routineExportMarkerPos* = 1
-  routinePatternPos* = 2
-  routineGenericParamsPos* = 3
-  routineParamsPos* = 4
-  routineResultPos* = 5
-  routinePragmasPos* = 6
-  routineBodyPos* = 7
-
-const
   nkModuleRef* = nkNone # pair of (ModuleId, SymId)
 
 type
@@ -55,7 +33,6 @@ type
   TypeId* = PackedItemId
 
 const
-  nilTypeId* = PackedItemId(module: LitId(0), item: -1.int32)
   nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
 
 const
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index 99ce183f2..725262120 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -18,6 +18,14 @@ type
     depsSection
     integersSection
     floatsSection
+    exportsSection
+    reexportsSection
+    compilerProcsSection
+    trmacrosSection
+    convertersSection
+    methodsSection
+    pureEnumsSection
+    macroUsagesSection
     topLevelSection
     bodiesSection
     symsSection
@@ -36,26 +44,30 @@ type
 const
   RodVersion = 1
   cookie = [byte(0), byte('R'), byte('O'), byte('D'),
-            byte(0), byte(0), byte(0), byte(RodVersion)]
+            byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
+
+proc setError(f: var RodFile; err: RodFileError) {.inline.} =
+  f.err = err
+  #raise newException(IOError, "IO error")
 
 proc storePrim*(f: var RodFile; s: string) =
   if f.err != ok: return
   if s.len >= high(int32):
-    f.err = tooBig
+    setError f, tooBig
     return
   var lenPrefix = int32(s.len)
   if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
-    f.err = ioFailure
+    setError f, ioFailure
   else:
     if s.len != 0:
       if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
-        f.err = ioFailure
+        setError f, ioFailure
 
 proc storePrim*[T](f: var RodFile; x: T) =
   if f.err != ok: return
   when supportsCopyMem(T):
     if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
-      f.err = ioFailure
+      setError f, ioFailure
   elif T is tuple:
     for y in fields(x):
       storePrim(f, y)
@@ -65,11 +77,11 @@ proc storePrim*[T](f: var RodFile; x: T) =
 proc storeSeq*[T](f: var RodFile; s: seq[T]) =
   if f.err != ok: return
   if s.len >= high(int32):
-    f.err = tooBig
+    setError f, tooBig
     return
   var lenPrefix = int32(s.len)
   if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
-    f.err = ioFailure
+    setError f, ioFailure
   else:
     for i in 0..<s.len:
       storePrim(f, s[i])
@@ -78,18 +90,18 @@ proc loadPrim*(f: var RodFile; s: var string) =
   if f.err != ok: return
   var lenPrefix = int32(0)
   if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
-    f.err = ioFailure
+    setError f, ioFailure
   else:
     s = newString(lenPrefix)
     if lenPrefix > 0:
       if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
-        f.err = ioFailure
+        setError f, ioFailure
 
 proc loadPrim*[T](f: var RodFile; x: var T) =
   if f.err != ok: return
   when supportsCopyMem(T):
     if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
-      f.err = ioFailure
+      setError f, ioFailure
   elif T is tuple:
     for y in fields(x):
       loadPrim(f, y)
@@ -100,7 +112,7 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
   if f.err != ok: return
   var lenPrefix = int32(0)
   if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
-    f.err = ioFailure
+    setError f, ioFailure
   else:
     s = newSeq[T](lenPrefix)
     for i in 0..<lenPrefix:
@@ -109,15 +121,15 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
 proc storeHeader*(f: var RodFile) =
   if f.err != ok: return
   if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
-    f.err = ioFailure
+    setError f, ioFailure
 
 proc loadHeader*(f: var RodFile) =
   if f.err != ok: return
   var thisCookie: array[cookie.len, byte]
   if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
-    f.err = ioFailure
+    setError f, ioFailure
   elif thisCookie != cookie:
-    f.err = wrongHeader
+    setError f, wrongHeader
 
 proc storeSection*(f: var RodFile; s: RodSection) =
   if f.err != ok: return
@@ -129,15 +141,15 @@ proc loadSection*(f: var RodFile; expected: RodSection) =
   if f.err != ok: return
   var s: RodSection
   loadPrim(f, s)
-  if expected != s:
-    f.err = wrongSection
+  if expected != s and f.err == ok:
+    setError f, wrongSection
 
 proc create*(filename: string): RodFile =
   if not open(result.f, filename, fmWrite):
-    result.err = ioFailure
+    setError result, ioFailure
 
 proc close*(f: var RodFile) = close(f.f)
 
 proc open*(filename: string): RodFile =
   if not open(result.f, filename, fmRead):
-    result.err = ioFailure
+    setError result, ioFailure
diff --git a/compiler/ic/to_packed_ast.nim b/compiler/ic/to_packed_ast.nim
index 761e34f1e..fe7a60c28 100644
--- a/compiler/ic/to_packed_ast.nim
+++ b/compiler/ic/to_packed_ast.nim
@@ -14,8 +14,6 @@ import ".." / [ast, idents, lineinfos, msgs, ropes, options,
 
 from std / os import removeFile, isAbsolute
 
-when not defined(release): import ".." / astalgo # debug()
-
 type
   PackedConfig* = object
     backend: TBackend
@@ -32,6 +30,11 @@ type
     bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position.
     hidden*: PackedTree # instantiated generics and other trees not directly in the source code.
     #producedGenerics*: Table[GenericKey, SymId]
+    exports*: seq[(LitId, int32)]
+    reexports*: seq[(LitId, PackedItemId)]
+    compilerProcs*, trmacros*, converters*, pureEnums*: seq[(LitId, int32)]
+    methods*: seq[(LitId, PackedItemId, int32)]
+    macroUsages*: seq[(PackedItemId, PackedLineInfo)]
     sh*: Shared
     cfg: PackedConfig
 
@@ -61,19 +64,28 @@ proc definedSymbolsAsString(config: ConfigRef): string =
     result.add ' '
     result.add d
 
-proc rememberConfig(c: var PackedEncoder; config: ConfigRef) =
+proc rememberConfig(c: var PackedEncoder; config: ConfigRef; pc: PackedConfig) =
   c.m.definedSymbols = definedSymbolsAsString(config)
-
-  template rem(x) =
-    c.m.cfg.x = config.x
-  primConfigFields rem
+  #template rem(x) =
+  #  c.m.cfg.x = config.x
+  #primConfigFields rem
+  c.m.cfg = pc
 
 proc configIdentical(m: PackedModule; config: ConfigRef): bool =
   result = m.definedSymbols == definedSymbolsAsString(config)
+  #if not result:
+  #  echo "A ", m.definedSymbols, " ", definedSymbolsAsString(config)
   template eq(x) =
     result = result and m.cfg.x == config.x
+    #if not result:
+    #  echo "B ", m.cfg.x, " ", config.x
   primConfigFields eq
 
+proc rememberStartupConfig*(dest: var PackedConfig, config: ConfigRef) =
+  template rem(x) =
+    dest.x = config.x
+  primConfigFields rem
+
 proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string =
   result = msgs.getHash(conf, fileIdx)
   if result.len == 0:
@@ -104,7 +116,7 @@ proc includesIdentical(m: var PackedModule; config: ConfigRef): bool =
       return false
   result = true
 
-proc initEncoder*(c: var PackedEncoder; m: PSym; config: ConfigRef) =
+proc initEncoder*(c: var PackedEncoder; m: PSym; config: ConfigRef; pc: PackedConfig) =
   ## setup a context for serializing to packed ast
   c.m.sh = Shared()
   c.thisModule = m.itemId.module
@@ -122,12 +134,45 @@ proc initEncoder*(c: var PackedEncoder; m: PSym; config: ConfigRef) =
       msgs.setHash(config, thisNimFile, h)
   c.m.includes.add((toLitId(thisNimFile, c), h)) # the module itself
 
+  rememberConfig(c, config, pc)
+
 proc addIncludeFileDep*(c: var PackedEncoder; f: FileIndex) =
   c.m.includes.add((toLitId(f, c), hashFileCached(c.config, f)))
 
 proc addImportFileDep*(c: var PackedEncoder; f: FileIndex) =
   c.m.imports.add toLitId(f, c)
 
+proc addExported*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  c.m.exports.add((nameId, s.itemId.item))
+
+proc addConverter*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  c.m.converters.add((nameId, s.itemId.item))
+
+proc addTrmacro*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  c.m.trmacros.add((nameId, s.itemId.item))
+
+proc addPureEnum*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  assert s.kind == skType
+  c.m.pureEnums.add((nameId, s.itemId.item))
+
+proc addMethod*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  discard "to do"
+  # c.m.methods.add((nameId, s.itemId.item))
+
+proc addReexport*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  c.m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c),
+                                          item: s.itemId.item)))
+
+proc addCompilerProc*(c: var PackedEncoder; s: PSym) =
+  let nameId = getOrIncl(c.m.sh.strings, s.name.s)
+  c.m.compilerProcs.add((nameId, s.itemId.item))
+
 proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder)
 proc toPackedSym*(s: PSym; c: var PackedEncoder): PackedItemId
 proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId
@@ -197,7 +242,7 @@ template storeNode(dest, src, field) =
 
 proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId =
   ## serialize a ptype
-  if t.isNil: return nilTypeId
+  if t.isNil: return nilItemId
 
   if t.uniqueId.module != c.thisModule:
     # XXX Assert here that it already was serialized in the foreign module!
@@ -376,87 +421,48 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
   f.loadPrim m.definedSymbols
   f.loadPrim m.cfg
 
-  if not configIdentical(m, config):
+  if f.err == ok and not configIdentical(m, config):
     f.err = configMismatch
 
-  f.loadSection stringsSection
-  f.load m.sh.strings
+  template loadSeqSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.loadSeq data
 
-  f.loadSection checkSumsSection
-  f.loadSeq m.includes
-  if not includesIdentical(m, config):
-    f.err = includeFileChanged
+  template loadTabSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.load data
 
-  f.loadSection depsSection
-  f.loadSeq m.imports
+  loadTabSection stringsSection, m.sh.strings
 
-  f.loadSection integersSection
-  f.load m.sh.integers
-  f.loadSection floatsSection
-  f.load m.sh.floats
-
-  f.loadSection topLevelSection
-  f.loadSeq m.topLevel.nodes
-
-  f.loadSection bodiesSection
-  f.loadSeq m.bodies.nodes
+  loadSeqSection checkSumsSection, m.includes
+  if not includesIdentical(m, config):
+    f.err = includeFileChanged
 
-  f.loadSection symsSection
-  f.loadSeq m.sh.syms
+  loadSeqSection depsSection, m.imports
 
-  f.loadSection typesSection
-  f.loadSeq m.sh.types
+  loadTabSection integersSection, m.sh.integers
+  loadTabSection floatsSection, m.sh.floats
 
-  close(f)
-  result = f.err
+  loadSeqSection exportsSection, m.exports
 
-type
-  ModuleStatus* = enum
-    undefined,
-    loading,
-    loaded,
-    outdated
+  loadSeqSection reexportsSection, m.reexports
 
-  LoadedModule* = object
-    status: ModuleStatus
-    symsInit, typesInit: bool
-    fromDisk: PackedModule
-    syms: seq[PSym] # indexed by itemId
-    types: seq[PType]
+  loadSeqSection compilerProcsSection, m.compilerProcs
 
-  PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex
+  loadSeqSection trmacrosSection, m.trmacros
 
-proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef;
-                    fileIdx: FileIndex): bool =
-  let m = int(fileIdx)
-  if m >= g.len:
-    g.setLen(m+1)
+  loadSeqSection convertersSection, m.converters
+  loadSeqSection methodsSection, m.methods
+  loadSeqSection pureEnumsSection, m.pureEnums
+  loadSeqSection macroUsagesSection, m.macroUsages
 
-  case g[m].status
-  of undefined:
-    g[m].status = loading
-    let fullpath = msgs.toFullPath(conf, fileIdx)
-    let rod = toRodFile(conf, AbsoluteFile fullpath)
-    let err = loadRodFile(rod, g[m].fromDisk, conf)
-    if err == ok:
-      result = false
-      # check its dependencies:
-      for dep in g[m].fromDisk.imports:
-        let fid = toFileIndex(dep, g[m].fromDisk, conf)
-        # Warning: we need to traverse the full graph, so
-        # do **not use break here**!
-        if needsRecompile(g, conf, fid):
-          result = true
+  loadSeqSection topLevelSection, m.topLevel.nodes
+  loadSeqSection bodiesSection, m.bodies.nodes
+  loadSeqSection symsSection, m.sh.syms
+  loadSeqSection typesSection, m.sh.types
 
-      g[m].status = if result: outdated else: loaded
-    else:
-      loadError(err, rod)
-      g[m].status = outdated
-      result = true
-  of loading, loaded:
-    result = false
-  of outdated:
-    result = true
+  close(f)
+  result = f.err
 
 # -------------------------------------------------------------------------
 
@@ -465,7 +471,7 @@ proc storeError(err: RodFileError; filename: AbsoluteFile) =
   removeFile(filename.string)
 
 proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) =
-  rememberConfig(encoder, encoder.config)
+  #rememberConfig(encoder, encoder.config)
 
   var f = rodfiles.create(filename.string)
   f.storeHeader()
@@ -473,40 +479,50 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) =
   f.storePrim encoder.m.definedSymbols
   f.storePrim encoder.m.cfg
 
-  f.storeSection stringsSection
-  f.store encoder.m.sh.strings
+  template storeSeqSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.storeSeq data
+
+  template storeTabSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.store data
+
+  storeTabSection stringsSection, encoder.m.sh.strings
 
-  f.storeSection checkSumsSection
-  f.storeSeq encoder.m.includes
+  storeSeqSection checkSumsSection, encoder.m.includes
 
-  f.storeSection depsSection
-  f.storeSeq encoder.m.imports
+  storeSeqSection depsSection, encoder.m.imports
 
-  f.storeSection integersSection
-  f.store encoder.m.sh.integers
+  storeTabSection integersSection, encoder.m.sh.integers
+  storeTabSection floatsSection, encoder.m.sh.floats
 
-  f.storeSection floatsSection
-  f.store encoder.m.sh.floats
+  storeSeqSection exportsSection, encoder.m.exports
 
-  f.storeSection topLevelSection
-  f.storeSeq encoder.m.topLevel.nodes
+  storeSeqSection reexportsSection, encoder.m.reexports
 
-  f.storeSection bodiesSection
-  f.storeSeq encoder.m.bodies.nodes
+  storeSeqSection compilerProcsSection, encoder.m.compilerProcs
 
-  f.storeSection symsSection
-  f.storeSeq encoder.m.sh.syms
+  storeSeqSection trmacrosSection, encoder.m.trmacros
+  storeSeqSection convertersSection, encoder.m.converters
+  storeSeqSection methodsSection, encoder.m.methods
+  storeSeqSection pureEnumsSection, encoder.m.pureEnums
+  storeSeqSection macroUsagesSection, encoder.m.macroUsages
 
-  f.storeSection typesSection
-  f.storeSeq encoder.m.sh.types
+  storeSeqSection topLevelSection, encoder.m.topLevel.nodes
+
+  storeSeqSection bodiesSection, encoder.m.bodies.nodes
+  storeSeqSection symsSection, encoder.m.sh.syms
+
+  storeSeqSection typesSection, encoder.m.sh.types
   close(f)
   if f.err != ok:
-    loadError(f.err, filename)
+    storeError(f.err, filename)
 
-  when true:
+  when false:
     # basic loader testing:
     var m2: PackedModule
     discard loadRodFile(filename, m2, encoder.config)
+    echo "loaded ", filename.string
 
 # ----------------------------------------------------------------------------
 
@@ -516,7 +532,25 @@ type
     lastLit*: LitId
     lastFile*: FileIndex # remember the last lookup entry.
     config*: ConfigRef
-    ident: IdentCache
+    cache: IdentCache
+
+type
+  ModuleStatus* = enum
+    undefined,
+    loading,
+    loaded,
+    outdated
+
+  LoadedModule* = object
+    status*: ModuleStatus
+    symsInit, typesInit: bool
+    fromDisk: PackedModule
+    syms: seq[PSym] # indexed by itemId
+    types: seq[PType]
+    module*: PSym # the one true module symbol.
+    iface: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too
+
+  PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex
 
 proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType
 proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym
@@ -546,7 +580,7 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph;
   of nkEmpty, nkNilLit, nkType:
     discard
   of nkIdent:
-    result.ident = getIdent(c.ident, g[c.thisModule].fromDisk.sh.strings[n.litId])
+    result.ident = getIdent(c.cache, g[c.thisModule].fromDisk.sh.strings[n.litId])
   of nkSym:
     result.sym = loadSym(c, g, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
   of directIntLit:
@@ -567,6 +601,31 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph;
     for n0 in sonsReadonly(tree, n):
       result.add loadNodes(c, g, tree, n0)
 
+proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph;
+                    tree: PackedTree; n: NodePos): PNode =
+  # do not load the body of the proc. This will be done later in
+  # getProcBody, if required.
+  let k = n.kind
+  result = newNodeIT(k, translateLineInfo(c, g, n.info),
+    loadType(c, g, n.typ))
+  result.flags = n.flags
+  assert k in {nkProcDef, nkMethodDef, nkIteratorDef, nkFuncDef, nkConverterDef}
+  var i = 0
+  for n0 in sonsReadonly(tree, n):
+    if i != bodyPos:
+      result.add loadNodes(c, g, tree, n0)
+    else:
+      result.add nil
+    inc i
+
+proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph;
+                  tree: PackedTree; n: NodePos): PNode =
+  var i = 0
+  for n0 in sonsReadonly(tree, n):
+    if i == bodyPos:
+      result = loadNodes(c, g, tree, n0)
+    inc i
+
 proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph;
                   s: PackedItemId): int32 {.inline.} =
   result = if s.module == LitId(0): c.thisModule
@@ -579,13 +638,17 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
     info: translateLineInfo(c, g, s.info),
     options: s.options,
     position: s.position,
-    name: getIdent(c.ident, g[si].fromDisk.sh.strings[s.name])
+    name: getIdent(c.cache, g[si].fromDisk.sh.strings[s.name])
   )
 
 template loadAstBody(p, field) =
   if p.field != emptyNodeId:
     result.field = loadNodes(c, g, g[si].fromDisk.bodies, NodePos p.field)
 
+template loadAstBodyLazy(p, field) =
+  if p.field != emptyNodeId:
+    result.field = loadProcHeader(c, g, g[si].fromDisk.bodies, NodePos p.field)
+
 proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph;
              si, item: int32; l: PackedLib): PLib =
   # XXX: hack; assume a zero LitId means the PackedLib is all zero (empty)
@@ -600,7 +663,10 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
                        s: PackedSym; si, item: int32; result: PSym) =
   result.typ = loadType(c, g, s.typ)
   loadAstBody(s, constraint)
-  loadAstBody(s, ast)
+  if result.kind in {skProc, skFunc, skIterator, skConverter, skMethod}:
+    loadAstBodyLazy(s, ast)
+  else:
+    loadAstBody(s, ast)
   result.annex = loadLib(c, g, si, item, s.annex)
   when hasFFI:
     result.cname = g[si].fromDisk.sh.strings[s.cname]
@@ -615,7 +681,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
     result.loc.r = rope externalName
 
 proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym =
-  if s == nilTypeId:
+  if s == nilItemId:
     result = nil
   else:
     let si = moduleIndex(c, g, s)
@@ -626,10 +692,16 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): P
 
     if g[si].syms[s.item] == nil:
       let packed = addr(g[si].fromDisk.sh.syms[s.item])
-      result = symHeaderFromPacked(c, g, packed[], si, s.item)
-      # store it here early on, so that recursions work properly:
-      g[si].syms[s.item] = result
-      symBodyFromPacked(c, g, packed[], si, s.item, result)
+
+      if packed.kind != skModule:
+        result = symHeaderFromPacked(c, g, packed[], si, s.item)
+        # store it here early on, so that recursions work properly:
+        g[si].syms[s.item] = result
+        symBodyFromPacked(c, g, packed[], si, s.item, result)
+      else:
+        result = g[si].module
+        assert result != nil
+
     else:
       result = g[si].syms[s.item]
 
@@ -654,7 +726,7 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
     result.methods.add((gen, loadSym(c, g, id)))
 
 proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType =
-  if t == nilTypeId:
+  if t == nilItemId:
     result = nil
   else:
     let si = moduleIndex(c, g, t)
@@ -672,15 +744,142 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId):
     else:
       result = g[si].types[t.item]
 
+proc setupLookupTables(m: var LoadedModule; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex) =
+  m.iface = initTable[PIdent, seq[PackedItemId]]()
+  for e in m.fromDisk.exports:
+    let nameLit = e[0]
+    m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(PackedItemId(module: LitId(0), item: e[1]))
+  for re in m.fromDisk.reexports:
+    let nameLit = re[0]
+    m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(re[1])
+
+  let filename = AbsoluteFile toFullPath(conf, fileIdx)
+  # We cannot call ``newSym`` here, because we have to circumvent the ID
+  # mechanism, which we do in order to assign each module a persistent ID.
+  m.module = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
+                  name: getIdent(cache, splitFile(filename).name),
+                  info: newLineInfo(fileIdx, 1, 1))
+
+proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                    fileIdx: FileIndex): bool =
+  let m = int(fileIdx)
+  if m >= g.len:
+    g.setLen(m+1)
+
+  case g[m].status
+  of undefined:
+    g[m].status = loading
+    let fullpath = msgs.toFullPath(conf, fileIdx)
+    let rod = toRodFile(conf, AbsoluteFile fullpath)
+    let err = loadRodFile(rod, g[m].fromDisk, conf)
+    if err == ok:
+      result = false
+      # check its dependencies:
+      for dep in g[m].fromDisk.imports:
+        let fid = toFileIndex(dep, g[m].fromDisk, conf)
+        # Warning: we need to traverse the full graph, so
+        # do **not use break here**!
+        if needsRecompile(g, conf, cache, fid):
+          result = true
+
+      if not result:
+        setupLookupTables(g[m], conf, cache, fileIdx)
+      g[m].status = if result: outdated else: loaded
+    else:
+      loadError(err, rod)
+      g[m].status = outdated
+      result = true
+  of loading, loaded:
+    result = false
+  of outdated:
+    result = true
 
-when false:
-  proc initGenericKey*(s: PSym; types: seq[PType]): GenericKey =
-    result.module = s.owner.itemId.module
-    result.name = s.name.s
-    result.types = mapIt types: hashType(it, {CoType, CoDistinct}).MD5Digest
+proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                        fileIdx: FileIndex): PSym =
+  ## Returns 'nil' if the module needs to be recompiled.
+  if needsRecompile(g, conf, cache, fileIdx):
+    result = nil
+  else:
+    result = g[int fileIdx].module
+    assert result != nil
+
+template setupDecoder() {.dirty.} =
+  var decoder = PackedDecoder(
+    thisModule: int32(module),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+
+proc loadProcBody*(config: ConfigRef, cache: IdentCache;
+                   g: var PackedModuleGraph; s: PSym): PNode =
+  let mId = s.itemId.module
+  var decoder = PackedDecoder(
+    thisModule: mId,
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+  let pos = g[mId].fromDisk.sh.syms[s.itemId.item].ast
+  assert pos != emptyNodeId
+  result = loadProcBody(decoder, g, g[mId].fromDisk.bodies, NodePos pos)
+
+type
+  RodIter* = object
+    decoder: PackedDecoder
+    values: seq[PackedItemId]
+    i: int
+
+proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache;
+                  g: var PackedModuleGraph; module: FileIndex;
+                  name: PIdent): PSym =
+  it.decoder = PackedDecoder(
+    thisModule: int32(module),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+  it.values = g[int module].iface.getOrDefault(name)
+  it.i = 0
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, it.values[it.i])
+    inc it.i
+
+proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
+                         g: var PackedModuleGraph; module: FileIndex): PSym =
+  it.decoder = PackedDecoder(
+    thisModule: int32(module),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+  it.values = @[]
+  for v in g[int module].iface.values:
+    it.values.add v
+  it.i = 0
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, it.values[it.i])
+    inc it.i
+
+proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym =
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, it.values[it.i])
+    inc it.i
+
+iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache;
+                           g: var PackedModuleGraph; module: FileIndex;
+                           name: PIdent): PSym =
+  setupDecoder()
+  let values = g[int module].iface.getOrDefault(name)
+  for pid in values:
+    let s = loadSym(decoder, g, pid)
+    assert s != nil
+    yield s
+
+proc interfaceSymbol*(config: ConfigRef, cache: IdentCache;
+                      g: var PackedModuleGraph; module: FileIndex;
+                      name: PIdent): PSym =
+  setupDecoder()
+  let values = g[int module].iface.getOrDefault(name)
+  result = loadSym(decoder, g, values[0])
 
-  proc addGeneric*(m: var Module; c: var PackedEncoder; key: GenericKey; s: PSym) =
-    ## add a generic to the module
-    if key notin m.generics:
-      m.generics[key] = toPackedSym(s, m.ast, c)
-      toPackedNode(s.ast, m.ast, c)
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 1e8c912d9..d2a84fd36 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -113,3 +113,8 @@ proc newIdentCache*(): IdentCache =
 proc whichKeyword*(id: PIdent): TSpecialWord =
   if id.id < 0: result = wInvalid
   else: result = TSpecialWord(id.id)
+
+proc hash*(x: PIdent): Hash {.inline.} = x.h
+proc `==`*(a, b: PIdent): bool {.inline.} =
+  if a.isNil or b.isNil: result = system.`==`(a, b)
+  else: result = a.id == b.id
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 645c03b2b..0fde13eb9 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -11,7 +11,8 @@
 
 import
   intsets, ast, astalgo, msgs, options, idents, lookups,
-  semdata, modulepaths, sigmatch, lineinfos, sets
+  semdata, modulepaths, sigmatch, lineinfos, sets,
+  modulegraphs
 
 proc readExceptSet*(c: PContext, n: PNode): IntSet =
   assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
@@ -108,7 +109,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
   let ident = lookups.considerQuotedIdent(c, n)
-  let s = strTableGet(fromMod.tab, ident)
+  let s = someSym(c.graph, fromMod, ident)
   if s == nil:
     errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
@@ -118,16 +119,16 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
     # for an enumeration we have to add all identifiers
     if multiImport:
       # for a overloadable syms add all overloaded routines
-      var it: TIdentIter
-      var e = initIdentIter(it, fromMod.tab, s.name)
+      var it: ModuleIter
+      var e = initModuleIter(it, c.graph, fromMod, s.name)
       while e != nil:
         if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
         if s.kind in ExportableSymKinds:
           rawImportSymbol(c, e, fromMod, importSet)
-        e = nextIdentIter(it, fromMod.tab)
+        e = nextModuleIter(it, c.graph)
     else:
       rawImportSymbol(c, s, fromMod, importSet)
-    suggestSym(c.config, n.info, s, c.graph.usageSym, false)
+    suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
 
 proc addImport(c: PContext; im: sink ImportedModule) =
   for i in 0..high(c.imports):
@@ -176,18 +177,6 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
   c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
   addUnnamedIt(c, fromMod, it.id notin exceptSet)
 
-  when false:
-    var i: TTabIter
-    var s = initTabIter(i, fromMod.tab)
-    while s != nil:
-      if s.kind != skModule:
-        if s.kind != skEnumField:
-          if s.kind notin ExportableSymKinds:
-            internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
-          if exceptSet.isNil or s.name.id notin exceptSet:
-            rawImportSymbol(c, s, fromMod)
-      s = nextIter(i, fromMod.tab)
-
 proc importAllSymbols*(c: PContext, fromMod: PSym) =
   c.addImport ImportedModule(m: fromMod, mode: importAll)
   addUnnamedIt(c, fromMod, true)
@@ -256,7 +245,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
         message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
       else:
         message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
-    suggestSym(c.config, n.info, result, c.graph.usageSym, false)
+    suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
     importStmtResult.add newSymNode(result, n.info)
     #newStrNode(toFullPath(c.config, f), n.info)
 
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 4b369210d..f588f9555 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -887,7 +887,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
           var v = copyNode(e[0])
           inc(totalRange, int(e[1].intVal - v.intVal))
           if totalRange > 65535:
-            localError(p.config, n.info, 
+            localError(p.config, n.info,
                        "Your case statement contains too many branches, consider using if/else instead!")
           while v.intVal <= e[1].intVal:
             gen(p, v, cond)
@@ -1076,7 +1076,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
       useMagic(p, "nimCopy")
       # supports proc getF(): var T
       if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds:
-          lineF(p, "nimCopy($1, $2, $3);$n", 
+          lineF(p, "nimCopy($1, $2, $3);$n",
                 [a.res, b.res, genTypeInfo(p, y.typ)])
       else:
         lineF(p, "$1 = nimCopy($1, $2, $3);$n",
@@ -1426,7 +1426,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
     if lfNoDecl in s.loc.flags or s.magic != mNone or
        {sfImportc, sfInfixCall} * s.flags != {}:
       discard
-    elif s.kind == skMethod and s.getBody.kind == nkEmpty:
+    elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty:
       # we cannot produce code for the dispatcher yet:
       discard
     elif sfForward in s.flags:
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 3c0aabe9a..cf6f07b7c 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -11,7 +11,8 @@
 
 import
   intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, nimfix/prettybase, lineinfos, strutils
+  renderer, nimfix/prettybase, lineinfos, strutils,
+  modulegraphs
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -107,8 +108,9 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym =
     scope = scope.parent
     result = strTableGet(scope.symbols, s)
 
-proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym =
-  result = initIdentIter(ti, im.m.tab, name)
+proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; name: PIdent;
+                   g: ModuleGraph): PSym =
+  result = initModuleIter(ti, g, im.m, name)
   while result != nil:
     let b =
       case im.mode
@@ -117,11 +119,12 @@ proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; n
       of importExcept: name.id notin im.exceptSet
     if b and not containsOrIncl(marked, result.id):
       return result
-    result = nextIdentIter(ti, im.m.tab)
+    result = nextModuleIter(ti, g)
 
-proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): PSym =
+proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
+                   g: ModuleGraph): PSym =
   while true:
-    result = nextIdentIter(ti, im.m.tab)
+    result = nextModuleIter(ti, g)
     if result == nil: return nil
     case im.mode
     of importAll:
@@ -134,17 +137,17 @@ proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule):
       if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id):
         return result
 
-iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent): PSym =
-  var ti: TIdentIter
-  var candidate = initIdentIter(ti, marked, im, name)
+iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
+  var ti: ModuleIter
+  var candidate = initIdentIter(ti, marked, im, name, g)
   while candidate != nil:
     yield candidate
-    candidate = nextIdentIter(ti, marked, im)
+    candidate = nextIdentIter(ti, marked, im, g)
 
 iterator importedItems*(c: PContext; name: PIdent): PSym =
   var marked = initIntSet()
   for im in c.imports.mitems:
-    for s in symbols(im, marked, name):
+    for s in symbols(im, marked, name, c.graph):
       yield s
 
 proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
@@ -169,15 +172,15 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
   dec scopeN
   isLocal = false
   for im in c.imports.mitems:
-    for s in im.m.tab.data:
-      if s != nil:
-        yield (s, scopeN, isLocal)
+    for s in modulegraphs.allSyms(c.graph, im.m):
+      assert s != nil
+      yield (s, scopeN, isLocal)
 
 proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
   var marked = initIntSet()
   result = nil
   for im in c.imports.mitems:
-    for s in symbols(im, marked, name):
+    for s in symbols(im, marked, name, c.graph):
       if result == nil:
         result = s
       else:
@@ -214,7 +217,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
   if result.len == 0:
     var marked = initIntSet()
     for im in c.imports.mitems:
-      for s in symbols(im, marked, s):
+      for s in symbols(im, marked, s, c.graph):
         if s.kind in filter:
           result.add s
 
@@ -240,6 +243,7 @@ type
     oimSymChoiceLocalLookup
   TOverloadIter* = object
     it*: TIdentIter
+    mit*: ModuleIter
     m*: PSym
     mode*: TOverloadIterMode
     symChoiceIndex*: int
@@ -306,7 +310,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
 proc addInterfaceDeclAux(c: PContext, sym: PSym) =
   if sfExported in sym.flags:
     # add to interface:
-    if c.module != nil: strTableAdd(c.module.tab, sym)
+    if c.module != nil: exportSym(c, sym)
     else: internalError(c.config, sym.info, "addInterfaceDeclAux")
 
 proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
@@ -475,7 +479,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
         if m == c.module:
           result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
         else:
-          result = strTableGet(m.tab, ident).skipAlias(n, c.config)
+          result = someSym(c.graph, m, ident).skipAlias(n, c.config)
         if result == nil and checkUndeclared in flags:
           fixSpelling(n[1], ident, searchInScopes)
           errorUndeclaredIdentifier(c, n[1].info, ident.s)
@@ -509,7 +513,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         scope = scope.parent
         if scope == nil:
           for i in 0..c.imports.high:
-            result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config)
+            result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config)
             if result != nil:
               o.currentScope = nil
               o.importIdx = i
@@ -535,7 +539,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
                                  ident).skipAlias(n, c.config)
           o.mode = oimSelfModule
         else:
-          result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
+          result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config)
       else:
         noidentError(c.config, n[1], n)
         result = errorSym(c, n[1])
@@ -568,7 +572,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
   var idx = o.importIdx+1
   o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
   while idx < c.imports.len:
-    result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config)
+    result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config)
     if result != nil:
       # oh, we were wrong, some other module had the symbol, so remember that:
       o.importIdx = idx
@@ -578,7 +582,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
 proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
   assert o.currentScope == nil
   while o.importIdx < c.imports.len:
-    result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
+    result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
     #while result != nil and result.id in o.marked:
     #  result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
     if result != nil:
@@ -602,12 +606,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         else:
           o.importIdx = 0
           if c.imports.len > 0:
-            result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
+            result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
             if result == nil:
               result = nextOverloadIterImports(o, c, n)
           break
     elif o.importIdx < c.imports.len:
-      result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
       if result == nil:
         result = nextOverloadIterImports(o, c, n)
     else:
@@ -615,7 +619,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   of oimSelfModule:
     result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
   of oimOtherModule:
-    result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
+    result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
   of oimSymChoice:
     if o.symChoiceIndex < n.len:
       result = n[o.symChoiceIndex].sym
@@ -654,7 +658,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         incl o.marked, result.id
 
     elif o.importIdx < c.imports.len:
-      result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
       #assert result.id notin o.marked
       #while result != nil and result.id in o.marked:
       #  result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index d700ab9a7..e79be6776 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -26,7 +26,7 @@ proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
   result.align = size.int16
 
 proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
-  result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
+  result = systemModuleSym(g, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
     result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
@@ -34,15 +34,12 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
   if result.kind == skAlias: result = result.owner
 
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
-  var ti: TIdentIter
   let id = getIdent(g.cache, name)
-  var r = initIdentIter(ti, g.systemModule.tab, id)
-  while r != nil:
+  for r in systemModuleSyms(g, id):
     if r.magic == m:
       # prefer the tyInt variant:
       if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
       result = r
-    r = nextIdentIter(ti, g.systemModule.tab)
   if result != nil: return result
   localError(g.config, info, "system module needs: " & name)
   result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 5544668cc..ab3ef5ab8 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -10,25 +10,11 @@
 ## This module implements the module graph data structure. The module graph
 ## represents a complete Nim project. Single modules can either be kept in RAM
 ## or stored in a rod-file.
-##
-## The caching of modules is critical for 'nimsuggest' and is tricky to get
-## right. If module E is being edited, we need autocompletion (and type
-## checking) for E but we don't want to recompile depending
-## modules right away for faster turnaround times. Instead we mark the module's
-## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we
-## need to recompile it and all of its dependencies that are marked as 'dirty'.
-## 'nimsuggest sug' actually is invoked for the file being edited so we know
-## its content changed and there is no need to compute any checksums.
-## Instead of a recursive algorithm, we use an iterative algorithm:
-##
-## - If a module gets recompiled, its dependencies need to be updated.
-## - Its dependent module stays the same.
-##
-
-import ast, intsets, tables, options, lineinfos, hashes, idents,
+
+import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents,
   btrees, md5
 
-# import ic / packed_ast
+import ic / to_packed_ast
 
 type
   SigHash* = distinct MD5Digest
@@ -39,9 +25,12 @@ type
     converters*: seq[PSym]
     patterns*: seq[PSym]
     pureEnums*: seq[PSym]
+    interf: TStrTable
 
   ModuleGraph* = ref object
     ifaces*: seq[Iface]  ## indexed by int32 fileIdx
+    packed: PackedModuleGraph
+    startupPackedConfig*: PackedConfig
     packageSyms*: TStrTable
     deps*: IntSet # the dependency graph or potentially its transitive closure.
     importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
@@ -64,6 +53,7 @@ type
     sysTypes*: array[TTypeKind, PType]
     compilerprocs*: TStrTable
     exposed*: TStrTable
+    packageTypes*: TStrTable
     intTypeCache*: array[-5..64, PType]
     opContains*, opNot*: PSym
     emptyNode*: PNode
@@ -132,6 +122,62 @@ proc toBase64a(s: cstring, len: int): string =
     result.add cb64[a shr 2]
     result.add cb64[(a and 3) shl 4]
 
+template semtab*(m: PSym; g: ModuleGraph): TStrTable =
+  g.ifaces[m.position].interf
+
+proc cachedModule(g: ModuleGraph; m: PSym): bool {.inline.} =
+  m.position < g.packed.len and g.packed[m.position].status == loaded
+
+type
+  ModuleIter* = object
+    fromRod: bool
+    modIndex: int
+    ti: TIdentIter
+    rodIt: RodIter
+
+proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym =
+  assert m.kind == skModule
+  mi.modIndex = m.position
+  mi.fromRod = mi.modIndex < g.packed.len and g.packed[mi.modIndex].status == loaded
+  if mi.fromRod:
+    result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name)
+  else:
+    result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interf, name)
+
+proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
+  if mi.fromRod:
+    result = nextRodIter(mi.rodIt, g.packed)
+  else:
+    result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interf)
+
+iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
+  if cachedModule(g, m):
+    var rodIt: RodIter
+    var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position)
+    while r != nil:
+      yield r
+      r = nextRodIter(rodIt, g.packed)
+  else:
+    for s in g.ifaces[m.position].interf.data:
+      if s != nil:
+        yield s
+
+proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
+  if cachedModule(g, m):
+    result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name)
+  else:
+    result = strTableGet(g.ifaces[m.position].interf, name)
+
+proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
+  result = someSym(g, g.systemModule, name)
+
+iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
+  var mi: ModuleIter
+  var r = initModuleIter(mi, g, g.systemModule, name)
+  while r != nil:
+    yield r
+    r = nextModuleIter(mi, g)
+
 proc `$`*(u: SigHash): string =
   toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
 
@@ -186,6 +232,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
   if m.position >= g.ifaces.len:
     setLen(g.ifaces, m.position + 1)
   g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[])
+  initStrTable(g.ifaces[m.position].interf)
 
 proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result = ModuleGraph()
@@ -202,6 +249,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result.methods = @[]
   initStrTable(result.compilerprocs)
   initStrTable(result.exposed)
+  initStrTable(result.packageTypes)
   result.opNot = createMagic(result, "not", mNot)
   result.opContains = createMagic(result, "contains", mInSet)
   result.emptyNode = newNode(nkEmpty)
@@ -226,8 +274,11 @@ proc resetAllModules*(g: ModuleGraph) =
   initStrTable(g.exposed)
 
 proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
-  if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len:
-    result = g.ifaces[fileIdx.int32].module
+  if fileIdx.int32 >= 0:
+    if fileIdx.int32 < g.packed.len and g.packed[fileIdx.int32].status == loaded:
+      result = g.packed[fileIdx.int32].module
+    elif fileIdx.int32 < g.ifaces.len:
+      result = g.ifaces[fileIdx.int32].module
 
 proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
@@ -280,3 +331,18 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
 
 proc isDirty*(g: ModuleGraph; m: PSym): bool =
   result = g.suggestMode and sfDirty in m.flags
+
+proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
+  result = s.ast[bodyPos]
+  if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf}:
+    result = loadProcBody(g.config, g.cache, g.packed, s)
+    s.ast[bodyPos] = result
+  assert result != nil
+
+proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex): PSym =
+  ## Returns 'nil' if the module needs to be recompiled.
+  if g.config.symbolFiles in {readOnlySf, v2Sf}:
+    result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx)
+
+proc configComplete*(g: ModuleGraph) =
+  rememberStartupConfig(g.startupPackedConfig, g.config)
diff --git a/compiler/modules.nim b/compiler/modules.nim
index a8a9c4df8..deb0174b5 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -31,43 +31,49 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
     pck = getPackageName(graph.config, filename.string)
     pck2 = if pck.len > 0: pck else: "unknown"
     pack = getIdent(graph.cache, pck2)
-  var packSym = graph.packageSyms.strTableGet(pack)
-  if packSym == nil:
-    packSym = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
-    initStrTable(packSym.tab)
-    graph.packageSyms.strTableAdd(packSym)
+  result = graph.packageSyms.strTableGet(pack)
+  if result == nil:
+    result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
+    #initStrTable(packSym.tab)
+    graph.packageSyms.strTableAdd(result)
   else:
-    let existing = strTableGet(packSym.tab, name)
-    if existing != nil and existing.info.fileIndex != info.fileIndex:
-      when false:
-        # we used to produce an error:
-        localError(graph.config, info,
-          "module names need to be unique per Nimble package; module clashes with " &
-            toFullPath(graph.config, existing.info.fileIndex))
-      else:
-        # but starting with version 0.20 we now produce a fake Nimble package instead
-        # to resolve the conflicts:
-        let pck3 = fakePackageName(graph.config, filename)
-        # this makes the new `packSym`'s owner be the original `packSym`
-        packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info)
-        initStrTable(packSym.tab)
-        graph.packageSyms.strTableAdd(packSym)
-  result = packSym
+    # we now produce a fake Nimble package instead
+    # to resolve the conflicts:
+    let pck3 = fakePackageName(graph.config, filename)
+    # this makes the new `packSym`'s owner be the original `packSym`
+    result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info)
+    #initStrTable(packSym.tab)
+    graph.packageSyms.strTableAdd(result)
+
+    when false:
+      let existing = strTableGet(packSym.tab, name)
+      if existing != nil and existing.info.fileIndex != info.fileIndex:
+        when false:
+          # we used to produce an error:
+          localError(graph.config, info,
+            "module names need to be unique per Nimble package; module clashes with " &
+              toFullPath(graph.config, existing.info.fileIndex))
+        else:
+          # but starting with version 0.20 we now produce a fake Nimble package instead
+          # to resolve the conflicts:
+          let pck3 = fakePackageName(graph.config, filename)
+          # this makes the new `packSym`'s owner be the original `packSym`
+          packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info)
+          #initStrTable(packSym.tab)
+          graph.packageSyms.strTableAdd(packSym)
 
 proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
   let packSym = getPackage(graph, fileIdx)
   result.owner = packSym
   result.position = int fileIdx
 
-  graph.registerModule(result)
-
-  initStrTable(result.tab)
+  #initStrTable(result.tab(graph))
   when false:
     strTableAdd(result.tab, result) # a module knows itself
     # This is now implemented via
     #   c.moduleScope.addSym(module) # a module knows itself
     # in sem.nim, around line 527
-  strTableAdd(packSym.tab, result)
+  #strTableAdd(packSym.tab, result)
 
 proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
@@ -79,6 +85,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   if not isNimIdentifier(result.name.s):
     rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
   partialInitModule(result, graph, fileIdx, filename)
+  graph.registerModule(result)
 
 proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
   var flags = flags
@@ -92,21 +99,20 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
       elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
     discard processModule(graph, result, idGeneratorFromModule(result), s)
   if result == nil:
+    result = moduleFromRodFile(graph, fileIdx)
     let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
-    when false:
-      # XXX entry point for module loading from the rod file
-      result = loadModuleSym(graph, fileIdx, filename)
-    when true:
+    if result == nil:
       result = newModule(graph, fileIdx)
       result.flags.incl flags
       registerModule(graph, result)
+      processModuleAux()
     else:
       partialInitModule(result, graph, fileIdx, filename)
-    processModuleAux()
+      # XXX replay the pragmas here!
   elif graph.isDirty(result):
     result.flags.excl sfDirty
     # reset module fields:
-    initStrTable(result.tab)
+    initStrTable(result.semtab(graph))
     result.ast = nil
     processModuleAux()
     graph.markClientsDirty(fileIdx)
@@ -152,6 +158,8 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
   connectCallbacks(graph)
   let conf = graph.config
   wantMainModule(conf)
+  configComplete(graph)
+
   let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
   let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
   conf.projectMainIdx2 = projectFile
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index b93129852..0170c9949 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -24,11 +24,8 @@ type
 iterator exportedSymbols*(i: Interpreter): PSym =
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
-  var it: TTabIter
-  var s = initTabIter(it, i.mainModule.tab)
-  while s != nil:
+  for s in modulegraphs.allSyms(i.graph, i.mainModule):
     yield s
-    s = nextIter(it, i.mainModule.tab)
 
 proc selectUniqueSymbol*(i: Interpreter; name: string;
                          symKinds: set[TSymKind] = {skLet, skVar}): PSym =
@@ -37,14 +34,14 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
   let n = getIdent(i.graph.cache, name)
-  var it: TIdentIter
-  var s = initIdentIter(it, i.mainModule.tab, n)
+  var it: ModuleIter
+  var s = initModuleIter(it, i.graph, i.mainModule, n)
   result = nil
   while s != nil:
     if s.kind in symKinds:
       if result == nil: result = s
       else: return nil # ambiguous
-    s = nextIdentIter(it, i.mainModule.tab)
+    s = nextModuleIter(it, i.graph)
 
 proc selectRoutine*(i: Interpreter; name: string): PSym =
   ## Selects a declared routine (proc/func/etc) from the main module.
@@ -70,7 +67,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
   ## This can also be used to *reload* the script.
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
-  initStrTable(i.mainModule.tab)
+  initStrTable(i.mainModule.semtab(i.graph))
   i.mainModule.ast = nil
 
   let s = if scriptStream != nil: scriptStream
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
index a781f1d51..4af0c28fa 100644
--- a/compiler/packagehandling.nim
+++ b/compiler/packagehandling.nim
@@ -44,7 +44,8 @@ proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string =
   # in different directory get different name and they can be
   # placed in a directory.
   # foo-#head/../bar becomes @foo-@hhead@s..@sbar
-  result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace({$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
+  result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace(
+    {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
 
 proc demanglePackageName*(path: string): string =
   result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 997a10cd8..e3885540e 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -110,6 +110,12 @@ proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
 proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
   result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
 
+proc partOfStdlib(x: PSym): bool =
+  var it = x.owner
+  while it != nil and it.kind == skPackage and it.owner != nil:
+    it = it.owner
+  result = it != nil and it.name.s == "stdlib"
+
 proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
                     stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
@@ -131,7 +137,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
   while true:
     openParser(p, fileIdx, s, graph.cache, graph.config)
 
-    if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
+    if not partOfStdlib(module) or module.name.s == "distros":
       # XXX what about caching? no processing then? what if I change the
       # modules to include between compilation runs? we'd need to track that
       # in ROD files. I think we should enable this feature only
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 78c79bf59..24e26b2b7 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,7 +9,7 @@
 
 ## Plugin to transform an inline iterator into a data structure.
 
-import ".." / [ast, lookups, semdata, lambdalifting, msgs]
+import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs]
 
 proc iterToProcImpl*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
@@ -29,7 +29,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
     localError(c.config, n[2].info,
         "type must be a non-generic ref|ptr to object with state field")
     return
-  let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t, c.idgen)
+  let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen)
 
   let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info)
   prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index a79a471de..c1627fe0c 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -14,6 +14,8 @@ import
   wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
   types, lookups, lineinfos, pathutils, linter
 
+from ic / to_packed_ast import addCompilerProc
+
 const
   FirstCallConv* = wNimcall
   LastCallConv* = wNoconv
@@ -702,6 +704,8 @@ proc markCompilerProc(c: PContext; s: PSym) =
   incl(s.flags, sfCompilerProc)
   incl(s.flags, sfUsed)
   registerCompilerProc(c.graph, s)
+  if c.config.symbolFiles != disabledSf:
+    addCompilerProc(c.encoder, s)
 
 proc deprecatedStmt(c: PContext; outerPragma: PNode) =
   let pragma = outerPragma[1]
diff --git a/compiler/sem.nim b/compiler/sem.nim
index d5ae5a21d..6f3b15867 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -19,7 +19,8 @@ import
   lowerings, plugins/active, lineinfos, strtabs, int128,
   isolation_check, typeallowed
 
-from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
+from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward,
+  systemModuleSym, semtab, getBody, someSym, allSyms
 
 when defined(nimfix):
   import nimfix/prettybase
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index da38a6fc2..f2f074447 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -268,7 +268,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
   result.typesWithOps = @[]
   result.features = graph.config.features
   if graph.config.symbolFiles != disabledSf:
-    initEncoder result.encoder, module, graph.config
+    initEncoder result.encoder, module, graph.config, graph.startupPackedConfig
 
 proc addIncludeFileDep*(c: PContext; f: FileIndex) =
   if c.config.symbolFiles != disabledSf:
@@ -286,15 +286,29 @@ proc inclSym(sq: var seq[PSym], s: PSym) =
 proc addConverter*(c: PContext, conv: PSym) =
   inclSym(c.converters, conv)
   inclSym(c.graph.ifaces[c.module.position].converters, conv)
-  #addConverter(c.graph, c.module, conv) # upcoming
+  if c.config.symbolFiles != disabledSf:
+    addConverter(c.encoder, conv)
 
 proc addPureEnum*(c: PContext, e: PSym) =
   inclSym(c.graph.ifaces[c.module.position].pureEnums, e)
+  if c.config.symbolFiles != disabledSf:
+    addPureEnum(c.encoder, e)
 
 proc addPattern*(c: PContext, p: PSym) =
   inclSym(c.patterns, p)
   inclSym(c.graph.ifaces[c.module.position].patterns, p)
-  #addPattern(c.graph, c.module, p) # upcoming
+  if c.config.symbolFiles != disabledSf:
+    addTrmacro(c.encoder, p)
+
+proc exportSym*(c: PContext; s: PSym) =
+  strTableAdd(c.module.semtab(c.graph), s)
+  if c.config.symbolFiles != disabledSf:
+    addExported(c.encoder, s)
+
+proc reexportSym*(c: PContext; s: PSym) =
+  strTableAdd(c.module.semtab(c.graph), s)
+  if c.config.symbolFiles != disabledSf:
+    addReexport(c.encoder, s)
 
 proc newLib*(kind: TLibKind): PLib =
   new(result)
@@ -489,4 +503,4 @@ proc storeRodNode*(c: PContext, n: PNode) =
 
 proc saveRodFile*(c: PContext) =
   if c.config.symbolFiles != disabledSf:
-    saveRodFile(toRodFile(c.config, c.filename.AbsoluteFile), c.encoder)
+    saveRodFile(toRodFile(c.config, AbsoluteFile toFullPath(c.config, FileIndex c.module.position)), c.encoder)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 1e5772189..4a04cfb6d 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1014,7 +1014,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
 proc buildEchoStmt(c: PContext, n: PNode): PNode =
   # we MUST not check 'n' for semantics again here! But for now we give up:
   result = newNodeI(nkCall, n.info)
-  var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo"))
+  let e = systemModuleSym(c.graph, getIdent(c.cache, "echo"))
   if e != nil:
     result.add(newSymNode(e))
   else:
@@ -1897,7 +1897,7 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
       if m == c.module:
         result = strTableGet(c.topLevelScope.symbols, ident)
       else:
-        result = strTableGet(m.tab, ident)
+        result = someSym(c.graph, m, ident)
   of nkSym:
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
@@ -2504,7 +2504,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
     elif labl.owner == nil:
       labl.owner = c.p.owner
     n[0] = newSymNode(labl, n[0].info)
-    suggestSym(c.config, n[0].info, labl, c.graph.usageSym)
+    suggestSym(c.graph, n[0].info, labl, c.graph.usageSym)
     styleCheckDef(c.config, labl)
     onDef(n[0].info, labl)
   n[1] = semExpr(c, n[1], flags)
@@ -2522,15 +2522,12 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
   let exceptSet = readExceptSet(c, n)
   let exported = moduleName.sym
   result = newNodeI(nkExportStmt, n.info)
-  strTableAdd(c.module.tab, exported)
-  var i: TTabIter
-  var s = initTabIter(i, exported.tab)
-  while s != nil:
+  reexportSym(c, exported)
+  for s in allSyms(c.graph, exported):
     if s.kind in ExportableSymKinds+{skModule} and
        s.name.id notin exceptSet and sfError notin s.flags:
-      strTableAdd(c.module.tab, s)
+      reexportSym(c, s)
       result.add newSymNode(s, n.info)
-    s = nextIter(i, exported.tab)
   markUsed(c, n.info, exported)
 
 proc semExport(c: PContext, n: PNode): PNode =
@@ -2548,15 +2545,12 @@ proc semExport(c: PContext, n: PNode): PNode =
       localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
     elif s.kind == skModule:
       # forward everything from that module:
-      strTableAdd(c.module.tab, s)
-      var ti: TTabIter
-      var it = initTabIter(ti, s.tab)
-      while it != nil:
+      reexportSym(c, s)
+      for it in allSyms(c.graph, s):
         if it.kind in ExportableSymKinds+{skModule}:
-          strTableAdd(c.module.tab, it)
+          reexportSym(c, it)
           result.add newSymNode(it, a.info)
           specialSyms(c, it)
-        it = nextIter(ti, s.tab)
       markUsed(c, n.info, s)
     else:
       while s != nil:
@@ -2565,7 +2559,7 @@ proc semExport(c: PContext, n: PNode): PNode =
             "; enum field cannot be exported individually")
         if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags:
           result.add(newSymNode(s, a.info))
-          strTableAdd(c.module.tab, s)
+          reexportSym(c, s)
           markUsed(c, n.info, s)
           specialSyms(c, s)
           if s.kind == skType and sfPure notin s.flags:
@@ -2575,7 +2569,7 @@ proc semExport(c: PContext, n: PNode): PNode =
                 var e = etyp.n[j].sym
                 if e.kind != skEnumField:
                   internalError(c.config, s.info, "rawImportSymbol")
-                strTableAdd(c.module.tab, e)
+                reexportSym(c, e)
 
         s = nextOverloadIter(o, c, a)
 
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 7e8fffc01..93184c568 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # so that 'break' etc. work as expected, we produce
   # a 'while true: stmt; break' loop ...
   result = newNodeI(nkWhileStmt, n.info, 2)
-  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
+  var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
     trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 1f633549a..dfbb022c8 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -481,7 +481,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       if sfGenSym in s.flags and s.ast == nil:
         body = n[bodyPos]
       else:
-        body = s.getBody
+        body = getBody(c.graph, s)
     else: body = n[bodyPos]
     n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
     closeScope(c)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 3f1cdace3..d273cac14 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -160,7 +160,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
-      n[bodyPos] = copyTree(s.getBody)
+      n[bodyPos] = copyTree(getBody(c.graph, s))
       instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
       popInfoContext(c.config)
@@ -383,7 +383,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     if n[pragmasPos].kind != nkEmpty:
       pragma(c, result, n[pragmasPos], allRoutinePragmas)
     if isNil(n[bodyPos]):
-      n[bodyPos] = copyTree(fn.getBody)
+      n[bodyPos] = copyTree(getBody(c.graph, fn))
     if c.inGenericContext == 0:
       instantiateBody(c, n, fn.typ.n, result, fn)
     sideEffectsCheck(c, result)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index c2c59bb31..a76116b1c 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -66,7 +66,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
         x.info = n.info
         incl(s.flags, sfUsed)
         n[0] = x
-        suggestSym(c.config, x.info, s, c.graph.usageSym)
+        suggestSym(c.graph, x.info, s, c.graph.usageSym)
         onUse(x.info, s)
       else:
         localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
@@ -332,7 +332,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
       discard
     result = n.info
   let info = getLineInfo(n)
-  suggestSym(c.config, info, result, c.graph.usageSym)
+  suggestSym(c.graph, info, result, c.graph.usageSym)
 
 proc checkNilable(c: PContext; v: PSym) =
   if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and v.typ.requiresInit:
@@ -1055,14 +1055,14 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
     if pkg.isNil or pkg.kind != skPackage:
       localError(c.config, name.info, "unknown package name: " & pkgName.s)
     else:
-      let typsym = pkg.tab.strTableGet(typName)
+      let typsym = c.graph.packageTypes.strTableGet(typName)
       if typsym.isNil:
         s = semIdentDef(c, name[1], skType)
         onDef(name[1].info, s)
         s.typ = newTypeS(tyObject, c)
         s.typ.sym = s
         s.flags.incl sfForward
-        pkg.tab.strTableAdd s
+        c.graph.packageTypes.strTableAdd s
         addInterfaceDecl(c, s)
       elif typsym.kind == skType and sfForward in typsym.flags:
         s = typsym
@@ -1088,7 +1088,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
       if not isTopLevel(c) or pkg.isNil:
         localError(c.config, name.info, "only top level types in a package can be 'package'")
       else:
-        let typsym = pkg.tab.strTableGet(s.name)
+        let typsym = c.graph.packageTypes.strTableGet(s.name)
         if typsym != nil:
           if sfForward notin typsym.flags or sfNoForward notin typsym.flags:
             typeCompleted(typsym)
@@ -1956,7 +1956,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     if not comesFromShadowScope:
       excl(proto.flags, sfForward)
       incl(proto.flags, sfWasForwarded)
-    suggestSym(c.config, s.info, proto, c.graph.usageSym)
+    suggestSym(c.graph, s.info, proto, c.graph.usageSym)
     closeScope(c)         # close scope with wrong parameter symbols
     openScope(c)          # open scope for old (correct) parameter symbols
     if proto.ast[genericParamsPos].kind != nkEmpty:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 14c3b9a11..65dc95916 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -250,7 +250,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
     else: result = newSymNode(s, n.info)
     # Issue #12832
     when defined(nimsuggest):
-      suggestSym(c.config, n.info, s, c.graph.usageSym, false)
+      suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
     if {optStyleHint, optStyleError} * c.config.globalOptions != {}:
       styleCheckUse(c.config, n.info, s)
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 99c588657..64113a4c6 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -140,7 +140,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     if result.sym != nil and sfExported in result.sym.flags:
       incl(e.flags, sfUsed)
       incl(e.flags, sfExported)
-      if not isPure: strTableAdd(c.module.tab, e)
+      if not isPure: exportSym(c, e)
     result.n.add symNode
     styleCheckDef(c.config, e)
     onDef(e.info, e)
@@ -779,7 +779,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
                    n[i][1].info
                  else:
                    n[i].info
-      suggestSym(c.config, info, f, c.graph.usageSym)
+      suggestSym(c.graph, info, f, c.graph.usageSym)
       f.typ = typ
       f.position = pos
       f.options = c.config.options
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 9a16cc3e0..156bc66d7 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -371,7 +371,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
   if sym.ast != nil:
     md5Init(c)
     c.md5Update(cast[cstring](result.addr), sizeof(result))
-    hashBodyTree(graph, c, sym.ast[bodyPos])
+    hashBodyTree(graph, c, getBody(graph, sym))
     c.md5Final(result.MD5Digest)
     graph.symBodyHashes[sym.id] = result
 
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 73929f813..560d20b3f 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -56,10 +56,10 @@ proc findDocComment(n: PNode): PNode =
   elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2:
     result = findDocComment(n[1])
 
-proc extractDocComment(s: PSym): string =
+proc extractDocComment(g: ModuleGraph; s: PSym): string =
   var n = findDocComment(s.ast)
   if n.isNil and s.kind in routineKinds and s.ast != nil:
-    n = findDocComment(s.ast[bodyPos])
+    n = findDocComment(getBody(g, s))
   if not n.isNil:
     result = n.comment.replace("\n##", "\n").strip
   else:
@@ -117,7 +117,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
     elif sourceIdent != ident:
       result = 0
 
-proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
+proc symToSuggest(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
                   inTypeContext: bool; scope: int;
                   useSuppliedInfo = false): Suggest =
@@ -136,7 +136,7 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
       if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
   result.symkind = byte s.kind
-  if optIdeTerse notin conf.globalOptions:
+  if optIdeTerse notin g.config.globalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
@@ -156,20 +156,20 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
     else:
       result.forth = ""
     when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
-      result.doc = s.extractDocComment
+      result.doc = extractDocComment(g, s)
   let infox =
     if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}:
       info
     else:
       s.info
-  result.filePath = toFullPath(conf, infox)
+  result.filePath = toFullPath(g.config, infox)
   result.line = toLinenumber(infox)
   result.column = toColumn(infox)
-  result.version = conf.suggestVersion
+  result.version = g.config.suggestVersion
   result.tokenLen = if section != ideHighlight:
                       s.name.s.len
                     else:
-                      getTokenLenFromSource(conf, s.name.s, infox)
+                      getTokenLenFromSource(g.config, s.name.s, infox)
 
 proc `$`*(suggest: Suggest): string =
   result = $suggest.section
@@ -261,7 +261,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
 proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
   var pm: PrefixMatch
   if filterSym(s, f, pm) and fieldVisible(c, s):
-    outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
+    outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
 
 proc getQuality(s: PSym): range[0..100] =
   if s.typ != nil and s.typ.len > 1:
@@ -275,7 +275,7 @@ template wholeSymTab(cond, section: untyped) {.dirty.} =
     let it = item
     var pm: PrefixMatch
     if cond:
-      outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it),
                                 pm, c.inTypeContext > 0, scopeN))
 
 proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
@@ -346,7 +346,7 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
   for (it, scopeN, isLocal) in allSyms(c):
     var pm: PrefixMatch
     if filterSym(it, f, pm):
-      outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm,
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, 0, pm,
                                 c.inTypeContext > 0, scopeN))
 
 proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
@@ -365,10 +365,10 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullPath))
         if m == nil: typ = nil
         else:
-          for it in items(n.sym.tab):
+          for it in allSyms(c.graph, n.sym):
             if filterSym(it, field, pm):
-              outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
-          outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
+              outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
+          outputs.add(symToSuggest(c.graph, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
             c.inTypeContext > 0, -99))
 
   if typ == nil:
@@ -378,11 +378,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
       else:
-        for it in items(n.sym.tab):
+        for it in allSyms(c.graph, n.sym):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
     else:
       # fallback:
       suggestEverything(c, n, field, outputs)
@@ -440,27 +440,27 @@ when defined(nimsuggest):
       if infoB.infoToInt == infoAsInt: return
     s.allUsages.add(info)
 
-proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
-  if conf.suggestVersion == 1:
-    if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len):
+proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
+  if g.config.suggestVersion == 1:
+    if usageSym == nil and isTracked(info, g.config.m.trackPos, s.name.s.len):
       usageSym = s
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
-      if conf.lastLineInfo != info:
-        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
-      conf.lastLineInfo = info
+      if g.config.lastLineInfo != info:
+        suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      g.config.lastLineInfo = info
 
 when defined(nimsuggest):
-  proc listUsages*(conf: ConfigRef; s: PSym) =
+  proc listUsages*(g: ModuleGraph; s: PSym) =
     #echo "usages ", s.allUsages.len
     for info in s.allUsages:
       let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(g.config, symToSuggest(g, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
-proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
+proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
   if s.isNil: return
-  if isTracked(info, conf.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags):
-    suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym))
+  if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags):
+    suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym))
     if sfForward notin s.flags:
       suggestQuit()
     else:
@@ -472,8 +472,9 @@ proc ensureIdx[T](x: var T, y: int) =
 proc ensureSeq[T](x: var seq[T]) =
   if x == nil: newSeq(x, 0)
 
-proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
+proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
+  let conf = g.config
   when defined(nimsuggest):
     if conf.suggestVersion == 0:
       if s.allUsages.len == 0:
@@ -482,15 +483,15 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
         s.addNoDup(info)
 
     if conf.ideCmd == ideUse:
-      findUsages(conf, info, s, usageSym)
+      findUsages(g, info, s, usageSym)
     elif conf.ideCmd == ideDef:
-      findDefinition(conf, info, s, usageSym)
+      findDefinition(g, info, s, usageSym)
     elif conf.ideCmd == ideDus and s != nil:
       if isTracked(info, conf.m.trackPos, s.name.s.len):
-        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
-      findUsages(conf, info, s, usageSym)
+        suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+      findUsages(g, info, s, usageSym)
     elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
     elif conf.ideCmd == ideOutline and isDecl:
       # if a module is included then the info we have is inside the include and
       # we need to walk up the owners until we find the outer most module,
@@ -503,7 +504,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
         parentModule = parentModule.owner
 
       if parentFileIndex == conf.m.trackPos.fileIndex:
-        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
+        suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
 proc extractPragma(s: PSym): PNode =
   if s.kind in routineKinds:
@@ -572,7 +573,7 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
 
     if sfError in s.flags: userError(conf, info, s)
   when defined(nimsuggest):
-    suggestSym(conf, info, s, c.graph.usageSym, false)
+    suggestSym(c.graph, info, s, c.graph.usageSym, false)
   if {optStyleHint, optStyleError} * conf.globalOptions != {}:
     styleCheckUse(conf, info, s)
   markOwnerModuleAsUsed(c, s)
@@ -658,7 +659,7 @@ proc suggestSentinel*(c: PContext) =
   for (it, scopeN, isLocal) in allSyms(c):
     var pm: PrefixMatch
     if filterSymNoOpr(it, nil, pm):
-      outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug,
           newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0,
           PrefixMatch.None, false, scopeN))
 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 461db9e89..e9ab6b47d 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -126,7 +126,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   var tc = c.transCon
   if sfBorrow in s.flags and s.kind in routineKinds:
     # simply exchange the symbol:
-    b = s.getBody
+    b = getBody(c.graph, s)
     if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
     b = newSymNode(b.sym, n.info)
   elif c.inlining > 0:
@@ -594,7 +594,7 @@ proc findWrongOwners(c: PTransf, n: PNode) =
   else:
     for i in 0..<n.safeLen: findWrongOwners(c, n[i])
 
-proc isSimpleIteratorVar(iter: PSym): bool =
+proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool =
   proc rec(n: PNode; owner: PSym; dangerousYields: var int) =
     case n.kind
     of nkEmpty..nkNilLit: discard
@@ -607,7 +607,7 @@ proc isSimpleIteratorVar(iter: PSym): bool =
       for c in n: rec(c, owner, dangerousYields)
 
   var dangerousYields = 0
-  rec(iter.ast[bodyPos], iter, dangerousYields)
+  rec(getBody(c.graph, iter), iter, dangerousYields)
   result = dangerousYields == 0
 
 proc transformFor(c: PTransf, n: PNode): PNode =
@@ -650,7 +650,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
       for j in 0..<n[i].len-1:
         addVar(v, copyTree(n[i][j])) # declare new vars
     else:
-      if n[i].kind == nkSym and isSimpleIteratorVar(iter):
+      if n[i].kind == nkSym and isSimpleIteratorVar(c, iter):
         incl n[i].sym.flags, sfCursor
       addVar(v, copyTree(n[i])) # declare new vars
   stmtList.add(v)
@@ -1087,12 +1087,12 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool):
 
   if prc.transformedBody != nil:
     result = prc.transformedBody
-  elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}:
-    result = prc.ast[bodyPos]
+  elif nfTransf in getBody(g, prc).flags or prc.kind in {skTemplate}:
+    result = getBody(g, prc)
   else:
     prc.transformedBody = newNode(nkEmpty) # protects from recursion
     var c = openTransf(g, prc.getModule, "", idgen)
-    result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly, c.idgen)
+    result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen)
     result = processTransf(c, result, prc)
     liftDefer(c, result)
     result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen)
@@ -1108,6 +1108,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool):
       prc.transformedBody = result
     else:
       prc.transformedBody = nil
+    # XXX Rodfile support for transformedBody!
 
   #if prc.name.s == "main":
   #  echo "transformed into ", renderTree(result, {renderIds})
diff --git a/compiler/vm.nim b/compiler/vm.nim
index c92e2d23d..0f75faee3 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1204,7 +1204,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]),
                  currentException: c.currentExceptionA,
                  currentLineInfo: c.debug[pc]))
-      elif importcCond(prc):
+      elif importcCond(c, prc):
         if compiletimeFFI notin c.config.features:
           globalError(c.config, c.debug[pc], "VM not allowed to do FFI, see `compiletimeFFI`")
         # we pass 'tos.slots' instead of 'regs' so that the compiler can keep
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index efb657d17..46931eb54 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -31,6 +31,8 @@ import
   strutils, ast, types, msgs, renderer, vmdef,
   intsets, magicsys, options, lowerings, lineinfos, transf
 
+from modulegraphs import getBody
+
 const
   debugEchoCode* = defined(nimVMDebug)
 
@@ -1570,11 +1572,11 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
   n.typ = t
   genLit(c, n, dest)
 
-proc importcCond*(s: PSym): bool {.inline.} =
+proc importcCond*(c: PCtx; s: PSym): bool {.inline.} =
   ## return true to importc `s`, false to execute its body instead (refs #8405)
   if sfImportc in s.flags:
     if s.kind in routineKinds:
-      return s.ast[bodyPos].kind == nkEmpty
+      return getBody(c.graph, s).kind == nkEmpty
 
 proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
   when hasFFI:
@@ -1615,7 +1617,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     elif s.position == 0:
       cannotEval(c, n)
     if s.position == 0:
-      if importcCond(s) or isImportcVar: c.importcSym(n.info, s)
+      if importcCond(c, s) or isImportcVar: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
     assert s.typ != nil
@@ -1841,7 +1843,7 @@ proc genVarSection(c: PCtx; n: PNode) =
       checkCanEval(c, a[0])
       if s.isGlobal:
         if s.position == 0:
-          if importcCond(s): c.importcSym(a.info, s)
+          if importcCond(c, s): c.importcSym(a.info, s)
           else:
             let sa = getNullValue(s.typ, a.info, c.config)
             #if s.ast.isNil: getNullValue(s.typ, a.info)
@@ -1964,6 +1966,7 @@ proc matches(s: PSym; x: string): bool =
     if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"):
       return false
     s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
+    while s != nil and s.kind == skPackage and s.owner != nil: s = s.owner
   result = true
 
 proc procIsCallback(c: PCtx; s: PSym): bool =
@@ -1989,7 +1992,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
         globalError(c.config, n.info, "Closure iterators are not supported by VM!")
       if procIsCallback(c, s): discard
-      elif importcCond(s): c.importcSym(n.info, s)
+      elif importcCond(c, s): c.importcSym(n.info, s)
       genLit(c, n, dest)
     of skConst:
       let constVal = if s.ast != nil: s.ast else: s.typ.n
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 2e28a09ee..58f39e0cd 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -195,7 +195,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
   if conf.ideCmd in {ideUse, ideDus}:
     let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym
     if u != nil:
-      listUsages(conf, u)
+      listUsages(graph, u)
     else:
       localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))