summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--changelog.md9
-rw-r--r--compiler/ast.nim66
-rw-r--r--compiler/astalgo.nim228
-rw-r--r--compiler/btrees.nim233
-rw-r--r--compiler/ccgcalls.nim30
-rw-r--r--compiler/ccgexprs.nim150
-rw-r--r--compiler/ccgmerge.nim32
-rw-r--r--compiler/ccgstmts.nim40
-rw-r--r--compiler/ccgthreadvars.nim27
-rw-r--r--compiler/ccgtrav.nim3
-rw-r--r--compiler/ccgtypes.nim156
-rw-r--r--compiler/ccgutils.nim115
-rw-r--r--compiler/cgen.nim173
-rw-r--r--compiler/cgendata.nim16
-rw-r--r--compiler/cgmeth.nim10
-rw-r--r--compiler/commands.nim62
-rw-r--r--compiler/configuration.nim362
-rw-r--r--compiler/depends.nim31
-rw-r--r--compiler/destroyer.nim20
-rw-r--r--compiler/dfa.nim7
-rw-r--r--compiler/docgen.nim109
-rw-r--r--compiler/docgen2.nim12
-rw-r--r--compiler/evalffi.nim4
-rw-r--r--compiler/evaltempl.nim11
-rw-r--r--compiler/extccomp.nim198
-rw-r--r--compiler/filter_tmpl.nim2
-rw-r--r--compiler/gorgeimpl.nim5
-rw-r--r--compiler/guards.nim26
-rw-r--r--compiler/hlo.nim6
-rw-r--r--compiler/idents.nim36
-rw-r--r--compiler/importer.nim39
-rw-r--r--compiler/incremental.nim188
-rw-r--r--compiler/jsgen.nim75
-rw-r--r--compiler/jstypes.nim12
-rw-r--r--compiler/lambdalifting.nim50
-rw-r--r--compiler/lexer.nim45
-rw-r--r--compiler/liftlocals.nim9
-rw-r--r--compiler/lineinfos.nim267
-rw-r--r--compiler/lookups.nim72
-rw-r--r--compiler/lowerings.nim103
-rw-r--r--compiler/macrocacheimpl.nim79
-rw-r--r--compiler/magicsys.nim39
-rw-r--r--compiler/main.nim159
-rw-r--r--compiler/modulegraphs.nim41
-rw-r--r--compiler/modulepaths.nim8
-rw-r--r--compiler/modules.nim94
-rw-r--r--compiler/msgs.nim316
-rw-r--r--compiler/ndi.nim12
-rw-r--r--compiler/nim.cfg3
-rw-r--r--compiler/nim.nim27
-rw-r--r--compiler/nimblecmd.nim2
-rw-r--r--compiler/nimconf.nim6
-rw-r--r--compiler/nimeval.nim131
-rw-r--r--compiler/nimfix/pretty.nim44
-rw-r--r--compiler/nimfix/prettybase.nim65
-rw-r--r--compiler/nimsets.nim55
-rw-r--r--compiler/nversion.nim2
-rw-r--r--compiler/options.nim157
-rw-r--r--compiler/parser.nim96
-rw-r--r--compiler/passaux.nim4
-rw-r--r--compiler/passes.nim95
-rw-r--r--compiler/platform.nim60
-rw-r--r--compiler/plugins/active.nim13
-rw-r--r--compiler/plugins/itersgen.nim16
-rw-r--r--compiler/plugins/locals.nim (renamed from compiler/plugins/locals/locals.nim)6
-rw-r--r--compiler/pluginsupport.nim28
-rw-r--r--compiler/pragmas.nim86
-rw-r--r--compiler/prefixmatches.nim6
-rw-r--r--compiler/procfind.nim2
-rw-r--r--compiler/renderer.nim11
-rw-r--r--compiler/reorder.nim73
-rw-r--r--compiler/rod.nim19
-rw-r--r--compiler/rodimpl.nim575
-rw-r--r--compiler/rodread.nim1244
-rw-r--r--compiler/rodwrite.nim659
-rw-r--r--compiler/ropes.nim41
-rw-r--r--compiler/scriptconfig.nim19
-rw-r--r--compiler/sem.nim78
-rw-r--r--compiler/semasgn.nim20
-rw-r--r--compiler/semcall.nim19
-rw-r--r--compiler/semdata.nim12
-rw-r--r--compiler/semexprs.nim111
-rw-r--r--compiler/semfields.nim12
-rw-r--r--compiler/semfold.nim64
-rw-r--r--compiler/semgnrc.nim22
-rw-r--r--compiler/seminst.nim14
-rw-r--r--compiler/semmagic.nim18
-rw-r--r--compiler/semobjconstr.nim6
-rw-r--r--compiler/semparallel.nim8
-rw-r--r--compiler/sempass2.nim38
-rw-r--r--compiler/semstmts.nim113
-rw-r--r--compiler/semtempl.nim18
-rw-r--r--compiler/semtypes.nim122
-rw-r--r--compiler/semtypinst.nim21
-rw-r--r--compiler/service.nim23
-rw-r--r--compiler/sigmatch.nim65
-rw-r--r--compiler/suggest.nim141
-rw-r--r--compiler/syntaxes.nim10
-rw-r--r--compiler/transf.nim36
-rw-r--r--compiler/types.nim189
-rw-r--r--compiler/vm.nim235
-rw-r--r--compiler/vmdef.nim6
-rw-r--r--compiler/vmdeps.nim78
-rw-r--r--compiler/vmgen.nim62
-rw-r--r--compiler/vmmarshal.nim23
-rw-r--r--compiler/vmops.nim23
-rw-r--r--compiler/writetracking.nim3
-rw-r--r--doc/intern.txt175
-rw-r--r--examples/allany.nim26
-rw-r--r--examples/readme.txt3
-rw-r--r--lib/core/macrocache.nim47
-rw-r--r--lib/core/macros.nim3
-rw-r--r--lib/packages/docutils/rstgen.nim13
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/osproc.nim2
-rw-r--r--lib/system/platforms.nim4
-rw-r--r--nimsuggest/nimsuggest.nim120
-rw-r--r--nimsuggest/nimsuggest.nim.cfg3
-rw-r--r--tests/compilerapi/exposed.nim3
-rw-r--r--tests/compilerapi/myscript.nim9
-rw-r--r--tests/compilerapi/tcompilerapi.nim47
-rw-r--r--tools/nimpretty.nim7
123 files changed, 3917 insertions, 5470 deletions
diff --git a/.travis.yml b/.travis.yml
index a3fa3f1da..3bc9a9778 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,7 +29,6 @@ before_script:
   - cd csources
   - sh build.sh
   - cd ..
-  - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
   - export PATH=$(pwd)/bin${PATH:+:$PATH}
 script:
   - nim c koch
diff --git a/changelog.md b/changelog.md
index 514bfbaca..e0389f9e2 100644
--- a/changelog.md
+++ b/changelog.md
@@ -84,8 +84,13 @@
 - The `terminal` module now exports additional procs for generating ANSI color
   codes as strings.
 - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
-- An exception raised from ``test`` block of ``unittest`` now shows its type in
-  the error message
+- An exception raised from a ``test`` block of ``unittest`` now shows its type in
+  error message.
+- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
+  API". Using the Nim compiler and its VM as a scripting engine has never been
+  easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
+  use the Nim VM in a native Nim application.
+
 
 ### Language additions
 
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5a84b2b02..40c1b064d 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,7 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents,
+  lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents,
   intsets, idgen
 
 type
@@ -634,14 +634,18 @@ type
     mNaN, mInf, mNegInf,
     mCompileOption, mCompileOptionArg,
     mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
-    mNKind, mNSymKind
+    mNKind, mNSymKind,
+
+    mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt,
+    mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
+
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
     mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
     mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent,
     mNBindSym, mLocals, mNCallSite,
-    mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
+    mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
     mNHint, mNWarning, mNError,
-    mInstantiationInfo, mGetTypeInfo, mNGenSym,
+    mInstantiationInfo, mGetTypeInfo,
     mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
     mException, mBuiltinType
 
@@ -998,8 +1002,8 @@ const
                   skMethod, skConverter}
 
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
-var
-  gMainPackageId*: int
+#var
+#  gMainPackageId*: int
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
@@ -1058,22 +1062,6 @@ proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
     result.info = children[0].info
   result.sons = @children
 
-proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
-  result = newNode(kind)
-  result.intVal = intVal
-
-proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
-  result = newIntNode(kind, intVal)
-  result.typ = typ
-
-proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
-  result = newNode(kind)
-  result.floatVal = floatVal
-
-proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
-  result = newNode(kind)
-  result.strVal = strVal
-
 template previouslyInferred*(t: PType): PType =
   if t.sons.len > 1: t.lastSon else: nil
 
@@ -1095,9 +1083,6 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   #  writeStacktrace()
   #  MessageOut(name.s & " has id: " & toString(result.id))
 
-var emptyNode* = newNode(nkEmpty) # XXX global variable here!
-# There is a single empty node that is shared! Do not overwrite it!
-
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil) or
@@ -1224,18 +1209,35 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result.info = info
   result.typ = typ
 
+proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
+  result = newNode(kind)
+  result.intVal = intVal
+
+proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
+  result = newIntNode(kind, intVal)
+  result.typ = typ
+
+proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
+  result = newNode(kind)
+  result.floatVal = floatVal
+
+proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
+  result = newNode(kind)
+  result.strVal = strVal
+
+proc newStrNode*(strVal: string; info: TLineInfo): PNode =
+  result = newNodeI(nkStrLit, info)
+  result.strVal = strVal
+
 proc addSon*(father, son: PNode) =
   assert son != nil
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
 
-var emptyParams = newNode(nkFormalParams)
-emptyParams.addSon(emptyNode)
-
 proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
-                 params = emptyParams,
+                 params,
                  name, pattern, genericParams,
-                 pragmas, exceptions = ast.emptyNode): PNode =
+                 pragmas, exceptions: PNode): PNode =
   result = newNodeI(kind, info)
   result.sons = @[name, pattern, genericParams, params,
                   pragmas, exceptions, body]
@@ -1705,7 +1707,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
     result = true
 
 proc isInfixAs*(n: PNode): bool =
-  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id
+  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
 
 proc findUnresolvedStatic*(n: PNode): PNode =
   if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
@@ -1730,3 +1732,5 @@ template incompleteType*(t: PType): bool =
 
 template typeCompleted*(s: PSym) =
   incl s.flags, sfNoForward
+
+template getBody*(s: PSym): PNode = s.ast[bodyPos]
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 15072e175..fff1527d3 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -12,79 +12,43 @@
 # the data structures here are used in various places of the compiler.
 
 import
-  ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
+  ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
+  msgs
 
 proc hashNode*(p: RootRef): Hash
-proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
   # Convert a tree into its YAML representation; this is used by the
   # YAML code generator and it is invaluable for debugging purposes.
   # If maxRecDepht <> -1 then it won't print the whole graph.
-proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc lineInfoToStr*(info: TLineInfo): Rope
+proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
 
-# ----------------------- node sets: ---------------------------------------
-proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
-  # returns true whether n is in t
-proc objectSetIncl*(t: var TObjectSet, obj: RootRef)
-  # include an element n in the table t
-proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool
-  # more are not needed ...
-
-# ----------------------- str table -----------------------------------------
-proc strTableContains*(t: TStrTable, n: PSym): bool
-proc strTableAdd*(t: var TStrTable, n: PSym)
-proc strTableGet*(t: TStrTable, name: PIdent): PSym
-
-type
-  TTabIter*{.final.} = object # consider all fields here private
-    h*: Hash                  # current hash
-
-proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
-proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
-  # usage:
-  # var
-  #   i: TTabIter
-  #   s: PSym
-  # s = InitTabIter(i, table)
-  # while s != nil:
-  #   ...
-  #   s = NextIter(i, table)
-  #
-
-type
-  TIdentIter*{.final.} = object # iterator over all syms with same identifier
-    h*: Hash                    # current hash
-    name*: PIdent
-
-
-proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym
-proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym
-
-# these are for debugging only: They are not really deprecated, but I want
-# the warning so that release versions do not contain debugging statements:
-proc debug*(n: PSym) {.deprecated.}
-proc debug*(n: PType) {.deprecated.}
-proc debug*(n: PNode) {.deprecated.}
+when declared(echo):
+  # these are for debugging only: They are not really deprecated, but I want
+  # the warning so that release versions do not contain debugging statements:
+  proc debug*(conf: ConfigRef; n: PSym) {.deprecated.}
+  proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
+  proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
 
 template mdbg*: bool {.dirty.} =
   when compiles(c.module):
-    c.module.fileIdx.int32 == c.config.projectMainIdx
+    c.module.fileIdx == c.config.projectMainIdx
   elif compiles(c.c.module):
-    c.c.module.fileIdx.int32 == c.c.config.projectMainIdx
+    c.c.module.fileIdx == c.c.config.projectMainIdx
   elif compiles(m.c.module):
-    m.c.module.fileIdx.int32 == m.c.config.projectMainIdx
+    m.c.module.fileIdx == m.c.config.projectMainIdx
   elif compiles(cl.c.module):
-    cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx
+    cl.c.module.fileIdx == cl.c.config.projectMainIdx
   elif compiles(p):
     when compiles(p.lex):
-      p.lex.fileIdx.int32 == p.lex.config.projectMainIdx
+      p.lex.fileIdx == p.lex.config.projectMainIdx
     else:
-      p.module.module.fileIdx.int32 == p.config.projectMainIdx
+      p.module.module.fileIdx == p.config.projectMainIdx
   elif compiles(m.module.fileIdx):
-    m.module.fileIdx.int32 == m.config.projectMainIdx
+    m.module.fileIdx == m.config.projectMainIdx
   elif compiles(L.fileIdx):
-    L.fileIdx.int32 == L.config.projectMainIdx
+    L.fileIdx == L.config.projectMainIdx
   else:
     error()
 
@@ -250,16 +214,16 @@ proc flagsToStr[T](flags: set[T]): Rope =
       add(result, makeYamlString($x))
     result = "[" & result & "]"
 
-proc lineInfoToStr(info: TLineInfo): Rope =
-  result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)),
+proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
+  result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
                              rope(toLinenumber(info)),
                              rope(toColumn(info))]
 
-proc treeToYamlAux(n: PNode, marker: var IntSet,
+proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
                    indent, maxRecDepth: int): Rope
-proc symToYamlAux(n: PSym, marker: var IntSet,
+proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
                   indent, maxRecDepth: int): Rope
-proc typeToYamlAux(n: PType, marker: var IntSet,
+proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
                    indent, maxRecDepth: int): Rope
 
 proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
@@ -273,7 +237,7 @@ proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
     inc(i, 2)
   addf(result, "$N$1}", [rspaces(indent)])
 
-proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
+proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
                   maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -281,20 +245,20 @@ proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
     result = "\"$1 @$2\"" % [rope(n.name.s), rope(
         strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
   else:
-    var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
+    var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
     result = ropeConstr(indent, [rope("kind"),
                                  makeYamlString($n.kind),
                                  rope("name"), makeYamlString(n.name.s),
-                                 rope("typ"), typeToYamlAux(n.typ, marker,
+                                 rope("typ"), typeToYamlAux(conf, n.typ, marker,
                                    indent + 2, maxRecDepth - 1),
-                                 rope("info"), lineInfoToStr(n.info),
+                                 rope("info"), lineInfoToStr(conf, n.info),
                                  rope("flags"), flagsToStr(n.flags),
                                  rope("magic"), makeYamlString($n.magic),
                                  rope("ast"), ast, rope("options"),
                                  flagsToStr(n.options), rope("position"),
                                  rope(n.position)])
 
-proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
+proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
                    maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -306,15 +270,15 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
       result = rope("[")
       for i in countup(0, sonsLen(n) - 1):
         if i > 0: add(result, ",")
-        addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
+        addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i],
             marker, indent + 4, maxRecDepth - 1)])
       addf(result, "$N$1]", [rspaces(indent + 2)])
     else:
       result = rope("null")
     result = ropeConstr(indent, [rope("kind"),
                                  makeYamlString($n.kind),
-                                 rope("sym"), symToYamlAux(n.sym, marker,
-        indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
+                                 rope("sym"), symToYamlAux(conf, n.sym, marker,
+        indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(conf, n.n, marker,
         indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
                                  rope("callconv"),
                                  makeYamlString(CallingConvToStr[n.callConv]),
@@ -322,7 +286,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
                                  rope("align"), rope(n.align),
                                  rope("sons"), result])
 
-proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
+proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
                    maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -330,7 +294,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
     var istr = rspaces(indent + 2)
     result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
     if maxRecDepth != 0:
-      addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
+      addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
       case n.kind
       of nkCharLit..nkInt64Lit:
         addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
@@ -344,7 +308,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
           addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
       of nkSym:
         addf(result, ",$N$1\"sym\": $2",
-             [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
+             [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
       of nkIdent:
         if n.ident != nil:
           addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
@@ -355,27 +319,27 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
             if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
+            addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i],
                 marker, indent + 4, maxRecDepth - 1)])
           addf(result, "$N$1]", [istr])
       addf(result, ",$N$1\"typ\": $2",
-           [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
+           [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
     addf(result, "$N$1}", [rspaces(indent)])
 
-proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = treeToYamlAux(n, marker, indent, maxRecDepth)
+  result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = typeToYamlAux(n, marker, indent, maxRecDepth)
+  result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = symToYamlAux(n, marker, indent, maxRecDepth)
+  result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
-proc debugType(n: PType, maxRecDepth=100): Rope =
+proc debugTree*(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
+proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope =
   if n == nil:
     result = rope("null")
   else:
@@ -385,7 +349,7 @@ proc debugType(n: PType, maxRecDepth=100): Rope =
       add(result, n.sym.name.s)
     if n.kind in IntegralTypes and n.n != nil:
       add(result, ", node: ")
-      add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+      add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
     if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
       add(result, "(")
       for i in countup(0, sonsLen(n) - 1):
@@ -393,13 +357,13 @@ proc debugType(n: PType, maxRecDepth=100): Rope =
         if n.sons[i] == nil:
           add(result, "null")
         else:
-          add(result, debugType(n.sons[i], maxRecDepth-1))
+          add(result, debugType(conf, n.sons[i], maxRecDepth-1))
       if n.kind == tyObject and n.n != nil:
         add(result, ", node: ")
-        add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+        add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
       add(result, ")")
 
-proc debugTree(n: PNode, indent: int, maxRecDepth: int;
+proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
                renderType=false): Rope =
   if n == nil:
     result = rope("null")
@@ -409,7 +373,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
              [istr, makeYamlString($n.kind)]
     when defined(useNodeIds):
       addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)])
-    addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
+    addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
     if maxRecDepth != 0:
       addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)])
       case n.kind
@@ -429,7 +393,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
         #     [istr, symToYaml(n.sym, indent, maxRecDepth),
         #     rope(n.sym.id)])
         if renderType and n.sym.typ != nil:
-          addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
+          addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)])
       of nkIdent:
         if n.ident != nil:
           addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
@@ -440,27 +404,28 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
             if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
+            addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(conf, n.sons[i],
                 indent + 4, maxRecDepth - 1, renderType)])
           addf(result, "$N$1]", [istr])
     addf(result, "$N$1}", [rspaces(indent)])
 
-proc debug(n: PSym) =
-  if n == nil:
-    echo("null")
-  elif n.kind == skUnknown:
-    echo("skUnknown")
-  else:
-    #writeLine(stdout, $symToYaml(n, 0, 1))
-    echo("$1_$2: $3, $4, $5, $6" % [
-      n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
-      $lineInfoToStr(n.info), $n.kind])
+when declared(echo):
+  proc debug(conf: ConfigRef; n: PSym) =
+    if n == nil:
+      echo("null")
+    elif n.kind == skUnknown:
+      echo("skUnknown")
+    else:
+      #writeLine(stdout, $symToYaml(n, 0, 1))
+      echo("$1_$2: $3, $4, $5, $6" % [
+        n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
+        $lineInfoToStr(conf, n.info), $n.kind])
 
-proc debug(n: PType) =
-  echo($debugType(n))
+  proc debug(conf: ConfigRef; n: PType) =
+    echo($debugType(conf, n))
 
-proc debug(n: PNode) =
-  echo($debugTree(n, 0, 100))
+  proc debug(conf: ConfigRef; n: PNode) =
+    echo($debugTree(conf, n, 0, 100))
 
 proc nextTry(h, maxHash: Hash): Hash =
   result = ((5 * h) + 1) and maxHash
@@ -468,7 +433,7 @@ proc nextTry(h, maxHash: Hash): Hash =
   # generates each int in range(maxHash) exactly once (see any text on
   # random-number generation for proof).
 
-proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
+proc objectSetContains*(t: TObjectSet, obj: RootRef): bool =
   # returns true whether n is in t
   var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
   while t.data[h] != nil:
@@ -492,12 +457,12 @@ proc objectSetEnlarge(t: var TObjectSet) =
     if t.data[i] != nil: objectSetRawInsert(n, t.data[i])
   swap(t.data, n)
 
-proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
+proc objectSetIncl*(t: var TObjectSet, obj: RootRef) =
   if mustRehash(len(t.data), t.counter): objectSetEnlarge(t)
   objectSetRawInsert(t.data, obj)
   inc(t.counter)
 
-proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
+proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
   # returns true if obj is already in the string table:
   var h: Hash = hashNode(obj) and high(t.data)
   while true:
@@ -515,7 +480,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
   inc(t.counter)
   result = false
 
-proc strTableContains(t: TStrTable, n: PSym): bool =
+proc strTableContains*(t: TStrTable, n: PSym): bool =
   var h: Hash = n.name.h and high(t.data) # start with real hash value
   while t.data[h] != nil:
     if (t.data[h] == n):
@@ -571,7 +536,7 @@ proc strTableEnlarge(t: var TStrTable) =
     if t.data[i] != nil: strTableRawInsert(n, t.data[i])
   swap(t.data, n)
 
-proc strTableAdd(t: var TStrTable, n: PSym) =
+proc strTableAdd*(t: var TStrTable, n: PSym) =
   if mustRehash(len(t.data), t.counter): strTableEnlarge(t)
   strTableRawInsert(t.data, n)
   inc(t.counter)
@@ -607,7 +572,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d
   inc(t.counter)
   result = false
 
-proc strTableGet(t: TStrTable, name: PIdent): PSym =
+proc strTableGet*(t: TStrTable, name: PIdent): PSym =
   var h: Hash = name.h and high(t.data)
   while true:
     result = t.data[h]
@@ -615,13 +580,13 @@ proc strTableGet(t: TStrTable, name: PIdent): PSym =
     if result.name.id == name.id: break
     h = nextTry(h, high(t.data))
 
-proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
-  ti.h = s.h
-  ti.name = s
-  if tab.counter == 0: result = nil
-  else: result = nextIdentIter(ti, tab)
 
-proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
+type
+  TIdentIter* = object # iterator over all syms with same identifier
+    h*: Hash           # current hash
+    name*: PIdent
+
+proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
   var h = ti.h and high(tab.data)
   var start = h
   result = tab.data[h]
@@ -634,6 +599,12 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
     result = tab.data[h]
   ti.h = nextTry(h, high(tab.data))
 
+proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
+  ti.h = s.h
+  ti.name = s
+  if tab.counter == 0: result = nil
+  else: result = nextIdentIter(ti, tab)
+
 proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
                          excluding: IntSet): PSym =
   var h: Hash = ti.h and high(tab.data)
@@ -657,20 +628,33 @@ proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent,
   if tab.counter == 0: result = nil
   else: result = nextIdentExcluding(ti, tab, excluding)
 
-proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym =
-  ti.h = 0                    # we start by zero ...
-  if tab.counter == 0:
-    result = nil              # FIX 1: removed endless loop
-  else:
-    result = nextIter(ti, tab)
+type
+  TTabIter* = object
+    h: Hash
 
-proc nextIter(ti: var TTabIter, tab: TStrTable): PSym =
+proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym =
+  # usage:
+  # var
+  #   i: TTabIter
+  #   s: PSym
+  # s = InitTabIter(i, table)
+  # while s != nil:
+  #   ...
+  #   s = NextIter(i, table)
+  #
   result = nil
   while (ti.h <= high(tab.data)):
     result = tab.data[ti.h]
     inc(ti.h)                 # ... and increment by one always
     if result != nil: break
 
+proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
+  ti.h = 0
+  if tab.counter == 0:
+    result = nil
+  else:
+    result = nextIter(ti, tab)
+
 iterator items*(tab: TStrTable): PSym =
   var it: TTabIter
   var s = initTabIter(it, tab)
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
new file mode 100644
index 000000000..6cd6e51f4
--- /dev/null
+++ b/compiler/btrees.nim
@@ -0,0 +1,233 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## BTree implementation with few features, but good enough for the
+## Nim compiler's needs.
+
+const
+  M = 512    # max children per B-tree node = M-1
+             # (must be even and greater than 2)
+  Mhalf = M div 2
+
+type
+  Node[Key, Val] = ref object
+    entries: int
+    keys: array[M, Key]
+    case isInternal: bool
+    of false:
+      vals: array[M, Val]
+    of true:
+      links: array[M, Node[Key, Val]]
+  BTree*[Key, Val] = object
+    root: Node[Key, Val]
+    entries: int      ## number of key-value pairs
+
+proc initBTree*[Key, Val](): BTree[Key, Val] =
+  BTree[Key, Val](root: Node[Key, Val](entries: 0, isInternal: false))
+
+template less(a, b): bool = cmp(a, b) < 0
+template eq(a, b): bool = cmp(a, b) == 0
+
+proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
+  var x = b.root
+  while x.isInternal:
+    for j in 0 ..< x.entries:
+      if j+1 == x.entries or less(key, x.keys[j+1]):
+        x = x.links[j]
+        break
+  assert(not x.isInternal)
+  for j in 0 ..< x.entries:
+    if eq(key, x.keys[j]): return x.vals[j]
+
+proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
+  var x = b.root
+  while x.isInternal:
+    for j in 0 ..< x.entries:
+      if j+1 == x.entries or less(key, x.keys[j+1]):
+        x = x.links[j]
+        break
+  assert(not x.isInternal)
+  for j in 0 ..< x.entries:
+    if eq(key, x.keys[j]): return true
+  return false
+
+proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
+  for j in 0 ..< Mhalf:
+    result.keys[j] = h.keys[Mhalf + j]
+  if h.isInternal:
+    for j in 0 ..< Mhalf:
+      result.links[j] = h.links[Mhalf + j]
+  else:
+    for j in 0 ..< Mhalf:
+      shallowCopy(result.vals[j], h.vals[Mhalf + j])
+
+proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
+  ## split node in half
+  result = Node[Key, Val](entries: Mhalf, isInternal: h.isInternal)
+  h.entries = Mhalf
+  copyHalf(h, result)
+
+proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
+  #var t = Entry(key: key, val: val, next: nil)
+  var newKey = key
+  var j = 0
+  if not h.isInternal:
+    while j < h.entries:
+      if less(key, h.keys[j]): break
+      inc j
+    for i in countdown(h.entries, j+1):
+      shallowCopy(h.vals[i], h.vals[i-1])
+    h.vals[j] = val
+  else:
+    var newLink: Node[Key, Val] = nil
+    while j < h.entries:
+      if j+1 == h.entries or less(key, h.keys[j+1]):
+        let u = insert(h.links[j], key, val)
+        inc j
+        if u == nil: return nil
+        newKey = u.keys[0]
+        newLink = u
+        break
+      inc j
+    for i in countdown(h.entries, j+1):
+      h.links[i] = h.links[i-1]
+    h.links[j] = newLink
+
+  for i in countdown(h.entries, j+1):
+    h.keys[i] = h.keys[i-1]
+  h.keys[j] = newKey
+  inc h.entries
+  return if h.entries < M: nil else: split(h)
+
+proc add*[Key, Val](b: var BTree[Key, Val]; key: Key; val: Val) =
+  let u = insert(b.root, key, val)
+  inc b.entries
+  if u == nil: return
+
+  # need to split root
+  let t = Node[Key, Val](entries: 2, isInternal: true)
+  t.keys[0] = b.root.keys[0]
+  t.links[0] = b.root
+  t.keys[1] = u.keys[0]
+  t.links[1] = u
+  b.root = t
+
+proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) =
+  if not h.isInternal:
+    for j in 0..<h.entries:
+      result.add(indent)
+      result.add($h.keys[j] & " " & $h.vals[j] & "\n")
+  else:
+    for j in 0..<h.entries:
+      if j > 0: result.add(indent & "(" & $h.keys[j] & ")\n")
+      toString(h.links[j], indent & "   ", result)
+
+proc `$`[Key, Val](b: BTree[Key, Val]): string =
+  result = ""
+  toString(b.root, "", result)
+
+proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool =
+  result = index < b.entries
+
+proc countSubTree[Key, Val](it: Node[Key, Val]): int =
+  if it.isInternal:
+    result = 0
+    for k in 0..<it.entries:
+      inc result, countSubTree(it.links[k])
+  else:
+    result = it.entries
+
+proc next*[Key, Val](b: BTree[Key, Val]; index: int): (Key, Val, int) =
+  var it = b.root
+  var i = index
+  # navigate to the right leaf:
+  while it.isInternal:
+    var sum = 0
+    for k in 0..<it.entries:
+      let c = countSubTree(it.links[k])
+      inc sum, c
+      if sum > i:
+        it = it.links[k]
+        dec i, (sum - c)
+        break
+  result = (it.keys[i], it.vals[i], index+1)
+
+iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
+  var i = 0
+  while hasNext(b, i):
+    let (k, v, i2) = next(b, i)
+    i = i2
+    yield (k, v)
+
+proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
+
+when isMainModule:
+
+  import random, tables
+
+  proc main =
+    var st = initBTree[string, string]()
+    st.add("www.cs.princeton.edu", "abc")
+    st.add("www.princeton.edu",    "128.112.128.15")
+    st.add("www.yale.edu",         "130.132.143.21")
+    st.add("www.simpsons.com",     "209.052.165.60")
+    st.add("www.apple.com",        "17.112.152.32")
+    st.add("www.amazon.com",       "207.171.182.16")
+    st.add("www.ebay.com",         "66.135.192.87")
+    st.add("www.cnn.com",          "64.236.16.20")
+    st.add("www.google.com",       "216.239.41.99")
+    st.add("www.nytimes.com",      "199.239.136.200")
+    st.add("www.microsoft.com",    "207.126.99.140")
+    st.add("www.dell.com",         "143.166.224.230")
+    st.add("www.slashdot.org",     "66.35.250.151")
+    st.add("www.espn.com",         "199.181.135.201")
+    st.add("www.weather.com",      "63.111.66.11")
+    st.add("www.yahoo.com",        "216.109.118.65")
+
+    assert st.getOrDefault("www.cs.princeton.edu") == "abc"
+    assert st.getOrDefault("www.harvardsucks.com") == nil
+
+    assert st.getOrDefault("www.simpsons.com") == "209.052.165.60"
+    assert st.getOrDefault("www.apple.com") == "17.112.152.32"
+    assert st.getOrDefault("www.ebay.com") == "66.135.192.87"
+    assert st.getOrDefault("www.dell.com") == "143.166.224.230"
+    assert(st.entries == 16)
+
+    for k, v in st:
+      echo k, ": ", v
+
+    when false:
+      var b2 = initBTree[string, string]()
+      const iters = 10_000
+      for i in 1..iters:
+        b2.add($i, $(iters - i))
+      for i in 1..iters:
+        let x = b2.getOrDefault($i)
+        if x != $(iters - i):
+          echo "got ", x, ", but expected ", iters - i
+      echo b2.entries
+
+    when true:
+      var b2 = initBTree[int, string]()
+      var t2 = initTable[int, string]()
+      const iters = 100_000
+      for i in 1..iters:
+        let x = rand(high(int))
+        if not t2.hasKey(x):
+          doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x
+          t2[x] = $x
+          b2.add(x, $x)
+
+      doAssert b2.entries == t2.len
+      echo "unique entries ", b2.entries
+      for k, v in t2:
+        doAssert $k == v
+        doAssert b2.getOrDefault(k) == $k
+
+  main()
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 7d355db5f..43a9f570a 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -24,7 +24,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri.sons[0].typ, abstractInst)
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if params != nil: pl.add(~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
@@ -33,13 +33,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         add(pl, ~");$n")
         line(p, cpsStmts, pl)
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         add(pl, ~");$n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
@@ -102,7 +102,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
     let ty = skipTypes(a.t, abstractVar+{tyPtr})
     case ty.kind
     of tyArray:
-      let first = firstOrd(ty)
+      let first = firstOrd(p.config, ty)
       if first == 0:
         result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
       else:
@@ -129,13 +129,13 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
       else:
         result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
     of tyArray:
-      result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
+      result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
     of tyPtr, tyRef:
       case lastSon(a.t).kind
       of tyString, tySequence:
         result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       of tyArray:
-        result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
+        result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
       else:
         internalError(p.config, "openArrayLoc: " & typeToString(a.t))
     else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -152,9 +152,9 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
   elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
     var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
     result = openArrayLoc(p, n)
-  elif ccgIntroducedPtr(param):
+  elif ccgIntroducedPtr(p.config, param):
     initLocExpr(p, n, a)
-    result = addrLoc(a)
+    result = addrLoc(p.config, a)
   elif p.module.compileToCpp and param.typ.kind == tyVar and
       n.kind == nkHiddenAddr:
     initLocExprSingleUse(p, n.sons[0], a)
@@ -164,7 +164,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
     if callee.kind == nkSym and
         {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
         {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
-      result = addrLoc(a)
+      result = addrLoc(p.config, a)
     else:
       result = rdLoc(a)
   else:
@@ -231,7 +231,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   let rawProc = getRawProcType(p, typ)
   let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if sonsLen(ri) > 1: add(pl, ~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
@@ -241,12 +241,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         genCallPattern()
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         genCallPattern()
         genAssignment(p, d, tmp, {}) # no need for deep copying
     else:
@@ -509,20 +509,20 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     add(pl, ~": ")
     add(pl, genArg(p, ri.sons[i], param, ri))
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if sonsLen(ri) > 1: add(pl, ~" ")
       # beware of 'result = p(result)'. We always allocate a temporary:
       if d.k in {locTemp, locNone}:
         # We already got a temp. Great, special case it:
         if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
         add(pl, ~"Result: ")
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         add(pl, ~"];$n")
         line(p, cpsStmts, pl)
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         add(pl, ~"];$n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 335aa2f84..9401f89c3 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -114,8 +114,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope =
 
 proc genSetNode(p: BProc, n: PNode): Rope =
   var cs: TBitSet
-  var size = int(getSize(n.typ))
-  toBitSet(n, cs)
+  var size = int(getSize(p.config, n.typ))
+  toBitSet(p.config, n, cs)
   if size > 8:
     let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
     result = p.module.tmpBase & rope(id)
@@ -185,13 +185,13 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     #    lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)])
     if canFormAcycle(dest.t):
       linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
-              addrLoc(dest), rdLoc(src))
+              addrLoc(p.config, dest), rdLoc(src))
     else:
       linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n",
-              addrLoc(dest), rdLoc(src))
+              addrLoc(p.config, dest), rdLoc(src))
   else:
     linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
-            addrLoc(dest), rdLoc(src))
+            addrLoc(p.config, dest), rdLoc(src))
 
 proc asgnComplexity(n: PNode): int =
   if n != nil:
@@ -260,13 +260,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
-           addrLoc(dest), addrLoc(src), rdLoc(dest))
+           addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
     else:
       linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
-              addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
+              addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
   else:
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
-            addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
+            addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
 
 proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # This function replaces all other methods for generating
@@ -284,7 +284,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       genRefAssign(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
-              addrLoc(dest), rdLoc(src),
+              addrLoc(p.config, dest), rdLoc(src),
               genTypeInfo(p.module, dest.t, dest.lode.info))
   of tyString:
     if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
@@ -301,7 +301,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
         linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", tmp.rdLoc)
       else:
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
-               addrLoc(dest), rdLoc(src))
+               addrLoc(p.config, dest), rdLoc(src))
   of tyProc:
     if needsComplexAssignment(dest.t):
       # optimize closure assignment:
@@ -346,7 +346,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     if needsComplexAssignment(dest.t):
       linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-           addrLoc(dest), addrLoc(src),
+           addrLoc(p.config, dest), addrLoc(p.config, src),
            genTypeInfo(p.module, dest.t, dest.lode.info))
     else:
       useStringh(p.module)
@@ -354,10 +354,10 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
            rdLoc(dest), rdLoc(src))
   of tySet:
-    if mapType(ty) == ctArray:
+    if mapType(p.config, ty) == ctArray:
       useStringh(p.module)
       linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
+              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
@@ -369,8 +369,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     #writeStackTrace()
     #echo p.currLineInfo, " requesting"
     linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
-            addrLoc(dest), rope getSize(dest.t),
-            makeCString(p.currLineInfo.toFullPath),
+            addrLoc(p.config, dest), rope getSize(p.config, dest.t),
+            makeCString(toFullPath(p.config, p.currLineInfo)),
             rope p.currLineInfo.safeLineNm)
 
 proc genDeepCopy(p: BProc; dest, src: TLoc) =
@@ -379,31 +379,31 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
       var tmp: TLoc
       getTemp(p, a.t, tmp)
       genAssignment(p, tmp, a, {})
-      addrLoc(tmp)
+      addrLoc(p.config, tmp)
     else:
-      addrLoc(a)
+      addrLoc(p.config, a)
 
   var ty = skipTypes(dest.t, abstractVarRange)
   case ty.kind
   of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
     # XXX optimize this
     linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
-            addrLoc(dest), addrLocOrTemp(src),
+            addrLoc(p.config, dest), addrLocOrTemp(src),
             genTypeInfo(p.module, dest.t, dest.lode.info))
   of tySequence, tyString:
     linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
-            addrLoc(dest), rdLoc(src),
+            addrLoc(p.config, dest), rdLoc(src),
             genTypeInfo(p.module, dest.t, dest.lode.info))
   of tyOpenArray, tyVarargs:
     linefmt(p, cpsStmts,
          "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-         addrLoc(dest), addrLocOrTemp(src),
+         addrLoc(p.config, dest), addrLocOrTemp(src),
          genTypeInfo(p.module, dest.t, dest.lode.info))
   of tySet:
-    if mapType(ty) == ctArray:
+    if mapType(p.config, ty) == ctArray:
       useStringh(p.module)
       linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
+              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   of tyPointer, tyChar, tyBool, tyEnum, tyCString,
@@ -489,15 +489,15 @@ proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
 
 proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
                             frmt: string): Rope =
-  var size = getSize(t)
-  let storage = if size < platform.intSize: rope("NI")
+  var size = getSize(p.config, t)
+  let storage = if size < p.config.target.intSize: rope("NI")
                 else: getTypeDesc(p.module, t)
   result = getTempName(p.module)
   linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
   lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
-  if size < platform.intSize or t.kind in {tyRange, tyEnum}:
+  if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
     linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
-            result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t)))
+            result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)))
 
 proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   const
@@ -545,8 +545,8 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   t = skipTypes(e.typ, abstractRange)
   if optOverflowCheck in p.options:
     linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
-            rdLoc(a), intLiteral(firstOrd(t)))
-  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
+            rdLoc(a), intLiteral(firstOrd(p.config, t)))
+  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)])
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   const
@@ -602,8 +602,8 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   # BUGFIX: cannot use result-type here, as it may be a boolean
-  s = max(getSize(a.t), getSize(b.t)) * 8
-  k = getSize(a.t) * 8
+  s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
+  k = getSize(p.config, a.t) * 8
   putIntoDest(p, d, e,
               binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
                                       getSimpleTypeDesc(p.module, e.typ), rope(k)])
@@ -656,7 +656,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   initLocExpr(p, e.sons[1], a)
   t = skipTypes(e.typ, abstractRange)
   putIntoDest(p, d, e,
-              unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
+              unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8),
                 getSimpleTypeDesc(p.module, e.typ)])
 
 proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
@@ -665,7 +665,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
       tfVarIsPtr notin skipTypes(typ, abstractInst).flags
 
 proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
-  let mt = mapType(e.sons[0].typ)
+  let mt = mapType(p.config, e.sons[0].typ)
   if mt in {ctArray, ctPtrToArray} and not enforceDeref:
     # XXX the amount of hacks for C's arrays is incredible, maybe we should
     # simply wrap them in a struct? --> Losing auto vectorization then?
@@ -724,12 +724,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e.sons[0], a)
     putIntoDest(p, d, e, "&" & a.r, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
-  elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
+  elif mapType(p.config, e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
     expr(p, e.sons[0], d)
   else:
     var a: TLoc
     initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e, addrLoc(a), a.storage)
+    putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
   if d.k == locNone: d.storage = a.storage
@@ -844,21 +844,21 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   initLocExpr(p, x, a)
   initLocExpr(p, y, b)
   var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
-  var first = intLiteral(firstOrd(ty))
+  var first = intLiteral(firstOrd(p.config, ty))
   # emit range check:
   if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
     if not isConstExpr(y):
       # semantic pass has already checked for const index expressions
-      if firstOrd(ty) == 0:
-        if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)):
+      if firstOrd(p.config, ty) == 0:
+        if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
           linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n",
-                  rdCharLoc(b), intLiteral(lastOrd(ty)))
+                  rdCharLoc(b), intLiteral(lastOrd(p.config, ty)))
       else:
         linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
-                rdCharLoc(b), first, intLiteral(lastOrd(ty)))
+                rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
     else:
       let idx = getOrdValue(y)
-      if idx < firstOrd(ty) or idx > lastOrd(ty):
+      if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
         localError(p.config, x.info, "index out of bounds")
   d.inheritLocation(a)
   putIntoDest(p, d, n,
@@ -880,10 +880,10 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) =
     linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
             rdLoc(idx), rdLoc(arr))
   of tyArray:
-    let first = intLiteral(firstOrd(ty))
+    let first = intLiteral(firstOrd(p.config, ty))
     if tfUncheckedArray notin ty.flags:
       linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
-              rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
+              rdCharLoc(idx), first, intLiteral(lastOrd(p.config, ty)))
   of tySequence, tyString:
     linefmt(p, cpsStmts,
           "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
@@ -978,7 +978,7 @@ proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
   internalAssert p.config, n.kind == nkBracket
-  if platform.targetOS == osGenode:
+  if p.config.target.targetOS == osGenode:
     # bypass libc and print directly to the Genode LOG session
     var args: Rope = nil
     var a: TLoc
@@ -1000,7 +1000,7 @@ proc genEcho(p: BProc, n: PNode) =
     when false:
       p.module.includeHeader("<stdio.h>")
       linefmt(p, cpsStmts, "printf($1$2);$n",
-              makeCString(repeat("%s", n.len) & tnl), args)
+              makeCString(repeat("%s", n.len) & "\L"), args)
       linefmt(p, cpsStmts, "fflush(stdout);$n")
 
 proc gcUsage(conf: ConfigRef; n: PNode) =
@@ -1114,7 +1114,7 @@ proc genReset(p: BProc, n: PNode) =
   var a: TLoc
   initLocExpr(p, n.sons[1], a)
   linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-          addrLoc(a),
+          addrLoc(p.config, a),
           genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info))
 
 proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
@@ -1299,7 +1299,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
   if d.k == locNone:
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  let L = int(lengthOrd(n.sons[1].typ))
+  let L = int(lengthOrd(p.config, n.sons[1].typ))
   genNewSeqAux(p, d, intLiteral(L))
   initLocExpr(p, n.sons[1], a)
   # bug #5007; do not produce excessive C source code:
@@ -1413,7 +1413,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
   of tySet:
     putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
-                addrLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
+                addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
   of tyOpenArray, tyVarargs:
     var b: TLoc
     case a.t.kind
@@ -1424,7 +1424,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
                   "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
     of tyArray:
       putIntoDest(p, b, e,
-                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
+                  "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
     else: internalError(p.config, e.sons[0].info, "genRepr()")
     putIntoDest(p, d, e,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
@@ -1437,7 +1437,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     localError(p.config, e.info, "'repr' doesn't support 'void' type")
   else:
     putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
-                              [addrLoc(a), genTypeInfo(p.module, t, e.info)]),
+                              [addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]),
                                a.storage)
   gcUsage(p.config, e)
 
@@ -1491,8 +1491,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     putIntoDest(p, d, e, tmp.r)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
-    if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ)))
-    else: putIntoDest(p, d, e, rope(lengthOrd(typ)))
+    if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
+    else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
   else: internalError(p.config, e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
@@ -1530,19 +1530,19 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   genAssignment(p, a, b, {})
   genAssignment(p, b, tmp, {})
 
-proc rdSetElemLoc(a: TLoc, setType: PType): Rope =
+proc rdSetElemLoc(conf: ConfigRef; a: TLoc, setType: PType): Rope =
   # read a location of an set element; it may need a subtraction operation
   # before the set operation
   result = rdCharLoc(a)
   assert(setType.kind == tySet)
-  if firstOrd(setType) != 0:
-    result = "($1- $2)" % [result, rope(firstOrd(setType))]
+  if firstOrd(conf, setType) != 0:
+    result = "($1- $2)" % [result, rope(firstOrd(conf, setType))]
 
-proc fewCmps(s: PNode): bool =
+proc fewCmps(conf: ConfigRef; s: PNode): bool =
   # this function estimates whether it is better to emit code
   # for constructing the set or generating a bunch of comparisons directly
   if s.kind != nkCurly: return false
-  if (getSize(s.typ) <= platform.intSize) and (nfAllConst in s.flags):
+  if (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
     result = false            # it is better to emit the set generation code
   elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
     result = true             # better not emit the set if int is basetype!
@@ -1550,10 +1550,10 @@ proc fewCmps(s: PNode): bool =
     result = sonsLen(s) <= 8  # 8 seems to be a good value
 
 proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
-  putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
+  putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
-  case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
+  case int(getSize(p.config, skipTypes(e.sons[1].typ, abstractVar)))
   of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
   of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
   of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
@@ -1565,11 +1565,11 @@ proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   assert(d.k == locNone)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(b, a.t)])
+  lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
 
 proc genInOp(p: BProc, e: PNode, d: var TLoc) =
   var a, b, x, y: TLoc
-  if (e.sons[1].kind == nkCurly) and fewCmps(e.sons[1]):
+  if (e.sons[1].kind == nkCurly) and fewCmps(p.config, e.sons[1]):
     # a set constructor but not a constant set:
     # do not emit the set, but generate a bunch of comparisons; and if we do
     # so, we skip the unnecessary range check: This is a semantical extension
@@ -1613,7 +1613,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "&", "|", "& ~", "^"]
   var a, b, i: TLoc
   var setType = skipTypes(e.sons[1].typ, abstractVar)
-  var size = int(getSize(setType))
+  var size = int(getSize(p.config, setType))
   case size
   of 1, 2, 4, 8:
     case op
@@ -1680,7 +1680,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   let etyp = skipTypes(e.typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
     putIntoDest(p, d, e, "(*($1*) ($2))" %
-        [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage)
+        [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
   elif etyp.kind == tyProc and etyp.callConv == ccClosure:
     putIntoDest(p, d, e, "(($1) ($2))" %
         [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
@@ -1917,7 +1917,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, genSetNode(p, e))
   else:
     if d.k == locNone: getTemp(p, e.typ, d)
-    if getSize(e.typ) > 8:
+    if getSize(p.config, e.typ) > 8:
       # big set:
       useStringh(p.module)
       lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
@@ -1929,14 +1929,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
               "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
-              rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)])
+              rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)])
         else:
           initLocExpr(p, it, a)
           lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
-               [rdLoc(d), rdSetElemLoc(a, e.typ)])
+               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
     else:
       # small set
-      var ts = "NU" & $(getSize(e.typ) * 8)
+      var ts = "NU" & $(getSize(p.config, e.typ) * 8)
       lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
       for it in e.sons:
         if it.kind == nkRange:
@@ -1945,13 +1945,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
               "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
-              rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ),
-              rdSetElemLoc(b, e.typ)])
+              rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
+              rdSetElemLoc(p.config, b, e.typ)])
         else:
           initLocExpr(p, it, a)
           lineF(p, cpsStmts,
                "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
-               [rdLoc(d), rdSetElemLoc(a, e.typ)])
+               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
 
 proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
   var rec: TLoc
@@ -2069,7 +2069,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
                 "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
   else:
     putIntoDest(p, d, n, "(*($1*) ($2))" %
-                        [getTypeDesc(p.module, dest), addrLoc(a)], a.storage)
+                        [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
 
 proc downConv(p: BProc, n: PNode, d: var TLoc) =
   if p.module.compileToCpp:
@@ -2290,7 +2290,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         incl a.flags, lfSingleUse
         genCall(p, ex, a)
         if lfSingleUse notin a.flags:
-          line(p, cpsStmts, a.r & ";" & tnl)
+          line(p, cpsStmts, a.r & ";\L")
       else:
         initLocExpr(p, ex, a)
   of nkAsmStmt: genAsmStmt(p, n)
@@ -2306,7 +2306,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     genTypeSection(p.module, n)
   of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef:
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt:
     discard
   of nkPragma: genPragma(p, n)
   of nkPragmaBlock: expr(p, n.lastSon, d)
@@ -2360,7 +2360,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
     result.add "}"
   of tyArray: result = rope"{}"
   of tySet:
-    if mapType(t) == ctArray: result = rope"{}"
+    if mapType(p.config, t) == ctArray: result = rope"{}"
     else: result = rope"0"
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
@@ -2457,8 +2457,8 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
     result = genConstExpr(p, n.sons[1])
   of nkCurly:
     var cs: TBitSet
-    toBitSet(n, cs)
-    result = genRawSetData(cs, int(getSize(n.typ)))
+    toBitSet(p.config, n, cs)
+    result = genRawSetData(cs, int(getSize(p.config, n.typ)))
   of nkBracket, nkPar, nkTupleConstr, nkClosure:
     var t = skipTypes(n.typ, abstractInst)
     if t.kind == tySequence:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 4b4f9c0e6..664f89b73 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -47,34 +47,32 @@ const
 
 proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(tnl)
-    add(result, "/*\t")
+    result = nil
+    add(result, "\n/*\t")
     add(result, CFileSectionNames[fs])
-    add(result, ":*/")
-    add(result, tnl)
+    add(result, ":*/\n")
 
 proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & tnl)
+    result = rope(NimMergeEndMark & "\n")
 
 proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(tnl)
-    add(result, "/*\t")
+    result = rope(nil)
+    add(result, "\n/*\t")
     add(result, CProcSectionNames[ps])
-    add(result, ":*/")
-    add(result, tnl)
+    add(result, ":*/\n")
 
 proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & tnl)
+    result = rope(NimMergeEndMark & "\n")
 
 proc writeTypeCache(a: TypeCache, s: var string) =
   var i = 0
   for id, value in pairs(a):
     if i == 10:
       i = 0
-      s.add(tnl)
+      s.add('\L')
     else:
       s.add(' ')
     encodeStr($id, s)
@@ -88,7 +86,7 @@ proc writeIntSet(a: IntSet, s: var string) =
   for x in items(a):
     if i == 10:
       i = 0
-      s.add(tnl)
+      s.add('\L')
     else:
       s.add(' ')
     encodeVInt(x, s)
@@ -97,8 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) =
 
 proc genMergeInfo*(m: BModule): Rope =
   if not compilationCachePresent(m.config): return nil
-  var s = "/*\tNIM_merge_INFO:"
-  s.add(tnl)
+  var s = "/*\tNIM_merge_INFO:\n"
   s.add("typeCache:{")
   writeTypeCache(m.typeCache, s)
   s.add("declared:{")
@@ -110,8 +107,7 @@ proc genMergeInfo*(m: BModule): Rope =
   encodeVInt(m.labels, s)
   s.add(" flags:")
   encodeVInt(cast[int](m.flags), s)
-  s.add(tnl)
-  s.add("*/")
+  s.add("\n*/")
   result = s.rope
 
 template `^`(pos: int): untyped = L.buf[pos]
@@ -155,11 +151,11 @@ proc readVerbatimSection(L: var TBaseLexer): Rope =
     of CR:
       pos = nimlexbase.handleCR(L, pos)
       buf = L.buf
-      r.add(tnl)
+      r.add('\L')
     of LF:
       pos = nimlexbase.handleLF(L, pos)
       buf = L.buf
-      r.add(tnl)
+      r.add('\L')
     of '\0':
       doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
       break
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 1e0a3c818..dd0e80f0e 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -28,9 +28,9 @@ proc registerGcRoot(p: BProc, v: PSym) =
       appcg(p.module, p.module.initProc.procSec(cpsInit),
         "#nimRegisterGlobalMarker($1);$n", [prc])
 
-proc isAssignedImmediately(n: PNode): bool {.inline.} =
+proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
   if n.kind == nkEmpty: return false
-  if isInvalidReturnType(n.typ):
+  if isInvalidReturnType(conf, n.typ):
     # var v = f()
     # is transformed into: var v;  f(addr v)
     # where 'f' **does not** initialize the result!
@@ -65,7 +65,7 @@ proc genVarTuple(p: BProc, n: PNode) =
       registerGcRoot(p, v)
     else:
       assignLocalVar(p, vn)
-      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
+      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[L-1]))
     initLoc(field, locExpr, vn, tup.storage)
     if t.kind == tyTuple:
       field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
@@ -168,7 +168,7 @@ proc genGotoState(p: BProc, n: PNode) =
   lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
   p.beforeRetNeeded = true
   lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
-  var statesCounter = lastOrd(n.sons[0].typ)
+  var statesCounter = lastOrd(p.config, n.sons[0].typ)
   if n.len >= 2 and n[1].kind == nkIntLit:
     statesCounter = n[1].intVal
   let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
@@ -227,7 +227,7 @@ proc genSingleVar(p: BProc, a: PNode) =
     registerGcRoot(p, v)
   else:
     let value = a.sons[2]
-    let imm = isAssignedImmediately(value)
+    let imm = isAssignedImmediately(p.config, value)
     if imm and p.module.compileToCpp and p.splitDecls == 0 and
         not containsHiddenPointer(v.typ):
       # C++ really doesn't like things like 'Foo f; f = x' as that invokes a
@@ -401,12 +401,12 @@ proc genComputedGoto(p: BProc; n: PNode) =
         localError(p.config, it.info,
             "case statement must be exhaustive for computed goto"); return
       casePos = i
-      let aSize = lengthOrd(it.sons[0].typ)
+      let aSize = lengthOrd(p.config, it.sons[0].typ)
       if aSize > 10_000:
         localError(p.config, it.info,
             "case statement has too many cases for computed goto"); return
       arraySize = aSize.int
-      if firstOrd(it.sons[0].typ) != 0:
+      if firstOrd(p.config, it.sons[0].typ) != 0:
         localError(p.config, it.info,
             "case statement has to start at 0 for computed goto"); return
   if casePos < 0:
@@ -477,7 +477,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
       lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
     var loopBody = t.sons[1]
     if loopBody.stmtsContainPragma(wComputedGoto) and
-        hasComputedGoto in CC[cCompiler].props:
+        hasComputedGoto in CC[p.config.cCompiler].props:
       # for closure support weird loop bodies are generated:
       if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty:
         loopBody = loopBody.sons[1]
@@ -650,7 +650,7 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
     assert(b.sons[i].kind != nkRange)
     initLocExpr(p, b.sons[i], x)
     assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit})
-    var j = int(hashString(b.sons[i].strVal) and high(branches))
+    var j = int(hashString(p.config, b.sons[i].strVal) and high(branches))
     appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
          [rdLoc(e), rdLoc(x), labl])
 
@@ -703,7 +703,7 @@ proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
     var stmtBlock = lastSon(branch)
     if stmtBlock.stmtsContainPragma(wLinearScanEnd):
       result = i
-    elif hasSwitchRange notin CC[cCompiler].props:
+    elif hasSwitchRange notin CC[p.config.cCompiler].props:
       if branch.kind == nkOfBranch and branchHasTooBigRange(branch):
         result = i
 
@@ -711,7 +711,7 @@ proc genCaseRange(p: BProc, branch: PNode) =
   var length = branch.len
   for j in 0 .. length-2:
     if branch[j].kind == nkRange:
-      if hasSwitchRange in CC[cCompiler].props:
+      if hasSwitchRange in CC[p.config.cCompiler].props:
         lineF(p, cpsStmts, "case $1 ... $2:$n", [
             genLiteral(p, branch[j][0]),
             genLiteral(p, branch[j][1])])
@@ -751,7 +751,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
         hasDefault = true
       exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n", [])
-    if (hasAssume in CC[cCompiler].props) and not hasDefault:
+    if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
       lineF(p, cpsStmts, "default: __assume(0);$n", [])
     lineF(p, cpsStmts, "}$n", [])
   if lend != nil: fixLabel(p, lend)
@@ -967,21 +967,21 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
       initLocExpr(p, it, a)
       res.add($a.rdLoc)
 
-  if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
+  if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
     for x in splitLines(res):
       var j = 0
       while x[j] in {' ', '\t'}: inc(j)
       if x[j] in {'"', ':'}:
         # don't modify the line if already in quotes or
         # some clobber register list:
-        add(result, x); add(result, tnl)
+        add(result, x); add(result, "\L")
       elif x[j] != '\0':
         # ignore empty lines
         add(result, "\"")
         add(result, x)
         add(result, "\\n\"\n")
   else:
-    res.add(tnl)
+    res.add("\L")
     result = res.rope
 
 proc genAsmStmt(p: BProc, t: PNode) =
@@ -993,9 +993,9 @@ proc genAsmStmt(p: BProc, t: PNode) =
   # work:
   if p.prc == nil:
     # top level asm statement?
-    addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
+    addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s])
   else:
-    lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
+    lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s])
 
 proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
@@ -1028,7 +1028,7 @@ proc genBreakPoint(p: BProc, t: PNode) =
     genLineDir(p, t)          # BUGFIX
     appcg(p.module, p.module.g.breakpoints,
          "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
-        rope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
+        rope(toLinenumber(t.info)), makeCString(toFilename(p.config, t.info)),
         makeCString(name)])
 
 proc genWatchpoint(p: BProc, n: PNode) =
@@ -1037,7 +1037,7 @@ proc genWatchpoint(p: BProc, n: PNode) =
   initLocExpr(p, n.sons[1], a)
   let typ = skipTypes(n.sons[1].typ, abstractVarRange)
   lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
-        [a.addrLoc, makeCString(renderTree(n.sons[1])),
+        [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])),
         genTypeInfo(p.module, typ, n.info)])
 
 proc genPragma(p: BProc, n: PNode) =
@@ -1068,7 +1068,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
   var t = skipTypes(objtype, abstractVar)
   assert t.kind == tyObject
   discard genTypeInfo(p.module, t, a.lode.info)
-  var L = lengthOrd(field.typ)
+  var L = lengthOrd(p.config, field.typ)
   if not containsOrIncl(p.module.declaredThings, field.id):
     appcg(p.module, cfsVars, "extern $1",
           discriminatorTableDecl(p.module, t, field))
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index da5c624b7..3e8a87041 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -23,27 +23,14 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
     add(p.procSec(cpsInit),
       ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n"))
 
-var
-  nimtv: Rope                 # Nim thread vars; the struct body
-  nimtvDeps: seq[PType] = @[]  # type deps: every module needs whole struct
-  nimtvDeclared = initIntSet() # so that every var/field exists only once
-                               # in the struct
-
-# 'nimtv' is incredibly hard to modularize! Best effort is to store all thread
-# vars in a ROD section and with their type deps and load them
-# unconditionally...
-
-# nimtvDeps is VERY hard to cache because it's not a list of IDs nor can it be
-# made to be one.
-
 proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
   if emulatedThreadVars(m.config):
     # we gather all thread locals var into a struct; we need to allocate
     # storage for that somehow, can't use the thread local storage
     # allocator for it :-(
-    if not containsOrIncl(nimtvDeclared, s.id):
-      nimtvDeps.add(s.loc.t)
-      addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
+    if not containsOrIncl(m.g.nimtvDeclared, s.id):
+      m.g.nimtvDeps.add(s.loc.t)
+      addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
   else:
     if isExtern: add(m.s[cfsVars], "extern ")
     if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
@@ -51,12 +38,12 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     addf(m.s[cfsVars], " $1;$n", [s.loc.r])
 
 proc generateThreadLocalStorage(m: BModule) =
-  if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
-    for t in items(nimtvDeps): discard getTypeDesc(m, t)
-    addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
+  if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
+    for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
+    addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
 
 proc generateThreadVarsSize(m: BModule) =
-  if nimtv != nil:
+  if m.g.nimtv != nil:
     let externc = if m.config.cmd == cmdCompileToCpp or
                        sfCompileToCpp in m.module.flags: "extern \"C\" "
                   else: ""
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index c265064a1..4514ce7dc 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -70,7 +70,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
      tySink:
     genTraverseProc(c, accessor, lastSon(typ))
   of tyArray:
-    let arraySize = lengthOrd(typ.sons[0])
+    let arraySize = lengthOrd(c.p.config, typ.sons[0])
     var i: TLoc
     getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
     let oldCode = p.s(cpsStmts)
@@ -122,7 +122,6 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
   var p = newProc(nil, m)
   result = "Marker_" & getTypeName(m, origTyp, sig)
   var typ = origTyp.skipTypes(abstractInst)
-  if typ.kind == tyOpt: typ = optLowering(typ)
 
   let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result]
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 7b44cddad..d0433f9ae 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -50,7 +50,7 @@ proc mangleName(m: BModule; s: PSym): Rope =
     result = s.name.s.mangle.rope
     add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
     s.loc.r = result
-    writeMangledName(m.ndi, s)
+    writeMangledName(m.ndi, s, m.config)
 
 proc mangleParamName(m: BModule; s: PSym): Rope =
   ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
@@ -63,7 +63,7 @@ proc mangleParamName(m: BModule; s: PSym): Rope =
       res.add "_0"
     result = res.rope
     s.loc.r = result
-    writeMangledName(m.ndi, s)
+    writeMangledName(m.ndi, s, m.config)
 
 proc mangleLocalName(p: BProc; s: PSym): Rope =
   assert s.kind in skLocalVars+{skTemp}
@@ -81,7 +81,7 @@ proc mangleLocalName(p: BProc; s: PSym): Rope =
       result.add "_" & rope(counter+1)
     p.sigConflicts.inc(key)
     s.loc.r = result
-    if s.kind != skTemp: writeMangledName(p.module.ndi, s)
+    if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
 
 proc scopeMangledParam(p: BProc; param: PSym) =
   ## parameter generation only takes BModule, not a BProc, so we have to
@@ -124,40 +124,40 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   result = typ.loc.r
   if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
 
-proc mapSetType(typ: PType): TCTypeKind =
-  case int(getSize(typ))
+proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
+  case int(getSize(conf, typ))
   of 1: result = ctInt8
   of 2: result = ctInt16
   of 4: result = ctInt32
   of 8: result = ctInt64
   else: result = ctArray
 
-proc mapType(typ: PType): TCTypeKind =
+proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
   ## Maps a Nim type to a C type
   case typ.kind
   of tyNone, tyStmt: result = ctVoid
   of tyBool: result = ctBool
   of tyChar: result = ctChar
-  of tySet: result = mapSetType(typ)
+  of tySet: result = mapSetType(conf, typ)
   of tyOpenArray, tyArray, tyVarargs: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(typ.lastSon)
+    return mapType(conf, typ.lastSon)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred:
-    result = mapType(lastSon(typ))
+    result = mapType(conf, lastSon(typ))
   of tyEnum:
-    if firstOrd(typ) < 0:
+    if firstOrd(conf, typ) < 0:
       result = ctInt32
     else:
-      case int(getSize(typ))
+      case int(getSize(conf, typ))
       of 1: result = ctUInt8
       of 2: result = ctUInt16
       of 4: result = ctInt32
       of 8: result = ctInt64
       else: result = ctInt32
-  of tyRange: result = mapType(typ.sons[0])
+  of tyRange: result = mapType(conf, typ.sons[0])
   of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
     var base = skipTypes(typ.lastSon, typedescInst)
     case base.kind
@@ -169,27 +169,20 @@ proc mapType(typ: PType): TCTypeKind =
     else: result = ctPtr
   of tyPointer: result = ctPtr
   of tySequence: result = ctNimSeq
-  of tyOpt:
-    case optKind(typ)
-    of oBool: result = ctStruct
-    of oNil, oPtr: result = ctPtr
-    of oEnum:
-      # The 'nil' value is always negative, so we always use a signed integer
-      result = if getSize(typ.sons[0]) == 8: ctInt64 else: ctInt32
   of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
   of tyString: result = ctNimStr
   of tyCString: result = ctCString
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
-    if typ.n != nil: result = mapType(lastSon typ)
+    if typ.n != nil: result = mapType(conf, lastSon typ)
     else: doAssert(false, "mapType")
   else: doAssert(false, "mapType")
 
-proc mapReturnType(typ: PType): TCTypeKind =
+proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
   #else:
-  result = mapType(typ)
+  result = mapType(conf, typ)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -207,14 +200,14 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
       (typ.sons[0] == nil) or isPureObject(typ))
 
-proc isInvalidReturnType(rettype: PType): bool =
+proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
   # Arrays and sets cannot be returned by a C procedure, because C is
   # such a poor programming language.
   # We exclude records with refs too. This enhances efficiency and
   # is necessary for proper code generation of assignments.
   if rettype == nil: result = true
   else:
-    case mapType(rettype)
+    case mapType(conf, rettype)
     of ctArray:
       result = not (skipTypes(rettype, typedescInst).kind in
           {tyVar, tyLent, tyRef, tyPtr})
@@ -240,9 +233,9 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
 
 proc addAbiCheck(m: BModule, t: PType, name: Rope) =
   if isDefined(m.config, "checkabi"):
-    addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))])
+    addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(m.config, t))])
 
-proc ccgIntroducedPtr(s: PSym): bool =
+proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool =
   var pt = skipTypes(s.typ, typedescInst)
   assert skResult != s.kind
   if tfByRef in pt.flags: return true
@@ -252,7 +245,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
     if s.typ.sym != nil and sfForward in s.typ.sym.flags:
       # forwarded objects are *always* passed by pointers for consistency!
       result = true
-    elif (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3):
+    elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
       result = true           # requested anyway
     elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
       result = false          # no need, because no subtyping possible
@@ -260,14 +253,14 @@ proc ccgIntroducedPtr(s: PSym): bool =
       result = true           # ordinary objects are always passed by reference,
                               # otherwise casting doesn't work
   of tyTuple:
-    result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options)
+    result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
   else: result = false
 
-proc fillResult(param: PNode) =
+proc fillResult(conf: ConfigRef; param: PNode) =
   fillLoc(param.sym.loc, locParam, param, ~"Result",
           OnStack)
   let t = param.sym.typ
-  if mapReturnType(t) != ctArray and isInvalidReturnType(t):
+  if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t):
     incl(param.sym.loc.flags, lfIndirect)
     param.sym.loc.storage = OnUnknown
 
@@ -364,12 +357,6 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
   of tySequence:
     result = getTypeForward(m, t, hashType(t)) & "*"
     pushType(m, t)
-  of tyOpt:
-    if optKind(etB) == oPtr:
-      result = getTypeForward(m, t, hashType(t)) & "*"
-      pushType(m, t)
-    else:
-      result = getTypeDescAux(m, t, check)
   else:
     result = getTypeDescAux(m, t, check)
 
@@ -384,7 +371,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
                    check: var IntSet, declareEnvironment=true;
                    weakDep=false) =
   params = nil
-  if t.sons[0] == nil or isInvalidReturnType(t.sons[0]):
+  if t.sons[0] == nil or isInvalidReturnType(m.config, t.sons[0]):
     rettype = ~"void"
   else:
     rettype = getTypeDescAux(m, t.sons[0], check)
@@ -395,7 +382,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     if params != nil: add(params, ~", ")
     fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param),
             param.paramStorageLoc)
-    if ccgIntroducedPtr(param):
+    if ccgIntroducedPtr(m.config, param):
       add(params, getTypeDescWeak(m, param.typ, check))
       add(params, ~"*")
       incl(param.loc.flags, lfIndirect)
@@ -417,10 +404,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
       addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
       inc(j)
       arr = arr.sons[0]
-  if t.sons[0] != nil and isInvalidReturnType(t.sons[0]):
+  if t.sons[0] != nil and isInvalidReturnType(m.config, t.sons[0]):
     var arr = t.sons[0]
     if params != nil: add(params, ", ")
-    if mapReturnType(t.sons[0]) != ctArray:
+    if mapReturnType(m.config, t.sons[0]) != ctArray:
       add(params, getTypeDescWeak(m, arr, check))
       add(params, "*")
     else:
@@ -471,13 +458,13 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
             if tfPacked notin rectype.flags:
               add(unionBody, "struct {")
             else:
-              if hasAttribute in CC[cCompiler].props:
+              if hasAttribute in CC[m.config.cCompiler].props:
                 add(unionBody, "struct __attribute__((__packed__)){" )
               else:
                 addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
             add(unionBody, a)
             addf(unionBody, "} $1;$n", [sname])
-            if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props:
+            if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
               addf(unionBody, "#pragma pack(pop)$n", [])
         else:
           add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
@@ -526,10 +513,10 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
   var hasField = false
 
   if tfPacked in typ.flags:
-    if hasAttribute in CC[cCompiler].props:
+    if hasAttribute in CC[m.config.cCompiler].props:
       result = structOrUnion(typ) & " __attribute__((__packed__))"
     else:
-      result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ)
+      result = "#pragma pack(push, 1)\L" & structOrUnion(typ)
   else:
     result = structOrUnion(typ)
 
@@ -556,7 +543,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
           # proper request to generate popCurrentExceptionEx not possible for 2 reasons:
           # generated function will be below declared Exception type and circular dependency
           # between Exception and popCurrentExceptionEx function
-          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result
+          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
@@ -570,9 +557,9 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
     addf(result, "char dummy;$n", [])
   else:
     add(result, desc)
-  add(result, "};" & tnl)
-  if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props:
-    result.add "#pragma pack(pop)" & tnl
+  add(result, "};\L")
+  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    result.add "#pragma pack(pop)\L"
 
 proc getTupleDesc(m: BModule, typ: PType, name: Rope,
                   check: var IntSet): Rope =
@@ -581,9 +568,9 @@ proc getTupleDesc(m: BModule, typ: PType, name: Rope,
   for i in countup(0, sonsLen(typ) - 1):
     addf(desc, "$1 Field$2;$n",
          [getTypeDescAux(m, typ.sons[i], check), rope(i)])
-  if desc == nil: add(result, "char dummy;" & tnl)
+  if desc == nil: add(result, "char dummy;\L")
   else: add(result, desc)
-  add(result, "};" & tnl)
+  add(result, "};\L")
 
 proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
   # A helper proc for handling cppimport patterns, involving numeric
@@ -657,21 +644,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       result = name & "*" & star
       m.typeCache[sig] = result
       pushType(m, et)
-    of tyOpt:
-      if etB.sons[0].kind in {tyObject, tyTuple}:
-        let name = getTypeForward(m, et, hashType et)
-        result = name & "*" & star
-        m.typeCache[sig] = result
-        pushType(m, et)
-      elif optKind(etB) == oBool:
-        let name = getTypeForward(m, et, hashType et)
-        result = name & "*"
-        m.typeCache[sig] = result
-        pushType(m, et)
-      else:
-        # else we have a strong dependency  :-(
-        result = getTypeDescAux(m, et, check) & star
-        m.typeCache[sig] = result
     else:
       # else we have a strong dependency  :-(
       result = getTypeDescAux(m, et, check) & star
@@ -687,11 +659,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
           (sfImportc in t.sym.flags and t.sym.magic == mNone)):
         m.typeCache[sig] = result
         var size: int
-        if firstOrd(t) < 0:
+        if firstOrd(m.config, t) < 0:
           addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
           size = 4
         else:
-          size = int(getSize(t))
+          size = int(getSize(m.config, t))
           case size
           of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
           of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
@@ -747,40 +719,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       else:
         result = rope("TGenericSeq")
     add(result, "*")
-  of tyOpt:
-    result = cacheGetType(m.typeCache, sig)
-    if result == nil:
-      case optKind(t)
-      of oBool:
-        result = cacheGetType(m.forwTypeCache, sig)
-        if result == nil:
-          result = getTypeName(m, origTyp, sig)
-          addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-              [structOrUnion(t), result])
-          m.forwTypeCache[sig] = result
-        appcg(m, m.s[cfsSeqTypes], "struct $2 {$n" &
-           "  NIM_BOOL Field0;$n" &
-           "  $1 Field1;$n" &
-           "};$n", [getTypeDescAux(m, t.sons[0], check), result])
-      of oPtr:
-        let et = t.sons[0]
-        if et.kind in {tyTuple, tyObject}:
-          let name = getTypeForward(m, et, hashType et)
-          result = name & "*"
-          pushType(m, et)
-        else:
-          result = getTypeDescAux(m, t.sons[0], check) & "*"
-      of oNil:
-        result = getTypeDescAux(m, t.sons[0], check)
-      of oEnum:
-        result = getTypeName(m, origTyp, sig)
-        if getSize(t.sons[0]) == 8:
-          addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
-        else:
-          addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
-      m.typeCache[sig] = result
   of tyArray:
-    var n: BiggestInt = lengthOrd(t)
+    var n: BiggestInt = lengthOrd(m.config, t)
     if n <= 0: n = 1   # make an array of at least one element
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
@@ -866,11 +806,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let s = int(getSize(t))
+      let s = int(getSize(m.config, t))
       case s
       of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
       else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
-             [result, rope(getSize(t))])
+             [result, rope(getSize(m.config, t))])
   of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink,
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
     result = getTypeDescAux(m, lastSon(t), check)
@@ -972,7 +912,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
     var typename = typeToString(if origType.typeInst != nil: origType.typeInst
                                 else: origType, preferName)
     if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
-      typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info
+      typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
     addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
         [name, makeCstring typename])
     discard cgsym(m, "nimTypeRoot")
@@ -1006,7 +946,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
 proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
   discard cgsym(m, "TNimNode")
   var tmp = discriminatorTableName(m, objtype, d)
-  result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)]
+  result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
 
 proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
                      info: TLineInfo) =
@@ -1030,7 +970,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
     assert(n.sons[0].kind == nkSym)
     var field = n.sons[0].sym
     var tmp = discriminatorTableName(m, typ, field)
-    var L = lengthOrd(field.typ)
+    var L = lengthOrd(m.config, field.typ)
     assert L > 0
     if field.loc.r == nil: fillObjectFields(m, typ)
     if field.loc.t == nil:
@@ -1140,7 +1080,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
       add(enumNames, makeCString(field.name.s))
     else:
       add(enumNames, makeCString(field.ast.strVal))
-    if i < length - 1: add(enumNames, ", " & tnl)
+    if i < length - 1: add(enumNames, ", \L")
     if field.position != i or tfEnumHasHoles in typ.flags:
       addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)])
       hasHoles = true
@@ -1166,7 +1106,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAux(m, typ, typ, name, info)
   var tmp = getNimNode(m)
   addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
-       [tmp, rope(firstOrd(typ)), name])
+       [tmp, rope(firstOrd(m.config, typ)), name])
 
 proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
@@ -1190,8 +1130,6 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
 proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
-  if t.kind == tyOpt:
-    return genTypeInfo(m, optLowering(t), info)
 
   let sig = hashType(origType)
   result = m.typeInfoMarker.getOrDefault(sig)
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index a6080a808..75cd3d35d 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -27,9 +27,9 @@ proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
 proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
   result = getPragmaStmt(n, w) != nil
 
-proc hashString*(s: string): BiggestInt =
+proc hashString*(conf: ConfigRef; s: string): BiggestInt =
   # has to be the same algorithm as system.hashString!
-  if CPU[targetCPU].bit == 64:
+  if CPU[conf.target.targetCPU].bit == 64:
     # we have to use the same bitwidth
     # as the target CPU
     var b = 0'i64
@@ -52,117 +52,12 @@ proc hashString*(s: string): BiggestInt =
     a = a +% `shl`(a, 15'i32)
     result = a
 
-var
-  gTypeTable: array[TTypeKind, TIdTable]  # XXX globals here
-  gCanonicalTypes: array[TTypeKind, PType]
-
-proc initTypeTables() =
-  for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
-
-proc resetCaches* =
-  ## XXX: fix that more properly
-  initTypeTables()
-  for i in low(gCanonicalTypes)..high(gCanonicalTypes):
-    gCanonicalTypes[i] = nil
-
-when false:
-  proc echoStats*() =
-    for i in countup(low(TTypeKind), high(TTypeKind)):
-      echo i, " ", gTypeTable[i].counter
-
-proc slowSearch(key: PType; k: TTypeKind): PType =
-  # tuples are quite horrible as C does not support them directly and
-  # tuple[string, string] is a (strange) subtype of
-  # tuple[nameA, nameB: string]. This bites us here, so we
-  # use 'sameBackendType' instead of 'sameType'.
-  if idTableHasObjectAsKey(gTypeTable[k], key): return key
-  for h in countup(0, high(gTypeTable[k].data)):
-    var t = PType(gTypeTable[k].data[h].key)
-    if t != nil and sameBackendType(t, key):
-      return t
-  idTablePut(gTypeTable[k], key, key)
-  result = key
-
-proc getUniqueType*(key: PType): PType =
-  # this is a hotspot in the compiler!
-  result = key
-  when false:
-    if key == nil: return
-    var k = key.kind
-    case k
-    of tyBool, tyChar, tyInt..tyUInt64:
-      # no canonicalization for integral types, so that e.g. ``pid_t`` is
-      # produced instead of ``NI``.
-      result = key
-    of  tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
-        tyCString, tyNone, tyVoid:
-      result = gCanonicalTypes[k]
-      if result == nil:
-        gCanonicalTypes[k] = key
-        result = key
-    of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr:
-      if key.isResolvedUserTypeClass:
-        return getUniqueType(lastSon(key))
-      if key.sym != nil:
-        internalError(key.sym.info, "metatype not eliminated")
-      else:
-        internalError("metatype not eliminated")
-    of tyDistinct:
-      if key.deepCopy != nil: result = key
-      else: result = getUniqueType(lastSon(key))
-    of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred:
-      result = getUniqueType(lastSon(key))
-      #let obj = lastSon(key)
-      #if obj.sym != nil and obj.sym.name.s == "TOption":
-      #  echo "for ", typeToString(key), " I returned "
-      #  debug result
-    of tyPtr, tyRef, tyVar, tyLent:
-      let elemType = lastSon(key)
-      if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
-        # no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
-        # produced instead of ``ptr NI``.
-        result = key
-      else:
-        result = slowSearch(key, k)
-    of tyGenericInvocation, tyGenericBody,
-       tyOpenArray, tyArray, tySet, tyRange, tyTuple,
-       tySequence, tyForward, tyVarargs, tyProxy, tyOpt:
-      # we have to do a slow linear search because types may need
-      # to be compared by their structure:
-      result = slowSearch(key, k)
-    of tyObject:
-      if tfFromGeneric notin key.flags:
-        # fast case; lookup per id suffices:
-        result = PType(idTableGet(gTypeTable[k], key))
-        if result == nil:
-          idTablePut(gTypeTable[k], key, key)
-          result = key
-      else:
-        # ugly slow case: need to compare by structure
-        if idTableHasObjectAsKey(gTypeTable[k], key): return key
-        for h in countup(0, high(gTypeTable[k].data)):
-          var t = PType(gTypeTable[k].data[h].key)
-          if t != nil and sameBackendType(t, key):
-            return t
-        idTablePut(gTypeTable[k], key, key)
-        result = key
-    of tyEnum:
-      result = PType(idTableGet(gTypeTable[k], key))
-      if result == nil:
-        idTablePut(gTypeTable[k], key, key)
-        result = key
-    of tyProc:
-      if key.callConv != ccClosure:
-        result = key
-      else:
-        # ugh, we need the canon here:
-        result = slowSearch(key, k)
-    of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType")
+template getUniqueType*(key: PType): PType = key
 
 proc makeSingleLineCString*(s: string): string =
   result = "\""
   for c in items(s):
-    result.add(c.toCChar)
+    c.toCChar(result)
   result.add('\"')
 
 proc mangle*(name: string): string =
@@ -210,5 +105,3 @@ proc mangle*(name: string): string =
       requiresUnderscore = true
   if requiresUnderscore:
     result.add "_"
-
-initTypeTables()
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 6a16474c0..c66b25b00 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -12,14 +12,15 @@
 import
   ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
-  ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
+  ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
   condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
-  lowerings, semparallel, tables, sets, ndi
+  lowerings, semparallel, tables, sets, ndi, lineinfos
 
 import strutils except `%` # collides with ropes.`%`
 
 from modulegraphs import ModuleGraph
-from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated
+from lineinfos import
+  warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile
 import dynlib
 
 when not declared(dynlib.libCandidates):
@@ -120,10 +121,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
           internalError(m.config, "ropes: invalid format string $" & $j)
         add(result, args[j-1])
       of 'n':
-        if optLineDir notin m.config.options: add(result, rnl)
+        if optLineDir notin m.config.options: add(result, "\L")
         inc(i)
       of 'N':
-        add(result, rnl)
+        add(result, "\L")
         inc(i)
       else: internalError(m.config, "ropes: invalid format string $" & frmt[i])
     elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
@@ -193,7 +194,7 @@ proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
         [rope(makeSingleLineCString(filename)), rope(line)])
 
 proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
-  genCLineDir(r, info.toFullPath, info.safeLineNm, conf)
+  genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -211,13 +212,13 @@ proc genLineDir(p: BProc, t: PNode) =
   let line = tt.info.safeLineNm
 
   if optEmbedOrigSrc in p.config.globalOptions:
-    add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl)
-  genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line, p.config)
+    add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & "\L")
+  genCLineDir(p.s(cpsStmts), toFullPath(p.config, tt.info), line, p.config)
   if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
       (p.prc == nil or sfPure notin p.prc.flags):
     if freshLineInfo(p, tt.info):
       linefmt(p, cpsStmts, "#endb($1, $2);$N",
-              line.rope, makeCString(toFilename(tt.info)))
+              line.rope, makeCString(toFilename(p.config, tt.info)))
   elif ({optLineTrace, optStackTrace} * p.options ==
       {optLineTrace, optStackTrace}) and
       (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
@@ -249,9 +250,9 @@ proc rdLoc(a: TLoc): Rope =
   result = a.r
   if lfIndirect in a.flags: result = "(*$1)" % [result]
 
-proc addrLoc(a: TLoc): Rope =
+proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
   result = a.r
-  if lfIndirect notin a.flags and mapType(a.t) != ctArray:
+  if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
     result = "(&" & result & ")"
 
 proc rdCharLoc(a: TLoc): Rope =
@@ -281,7 +282,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
     linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
   of frEmbedded:
     # worst case for performance:
-    var r = if takeAddr: addrLoc(a) else: rdLoc(a)
+    var r = if takeAddr: addrLoc(p.config, a) else: rdLoc(a)
     linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t, a.lode.info))
 
 type
@@ -310,10 +311,10 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
   else:
     if optNilCheck in p.options:
-      linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
+      linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(p.config, loc))
     if loc.storage != OnStack:
       linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-              addrLoc(loc), genTypeInfo(p.module, loc.t, loc.lode.info))
+              addrLoc(p.config, loc), genTypeInfo(p.module, loc.t, loc.lode.info))
       # XXX: generated reset procs should not touch the m_type
       # field, so disabling this should be safe:
       genObjectInit(p, cpsStmts, loc.t, loc, true)
@@ -322,7 +323,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
       linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-              addrLoc(loc), getTypeDesc(p.module, loc.t))
+              addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, true)
@@ -339,7 +340,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
       if not isImportedCppType(typ):
         useStringh(p.module)
         linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-                addrLoc(loc), getTypeDesc(p.module, typ))
+                addrLoc(p.config, loc), getTypeDesc(p.module, typ))
     genObjectInit(p, cpsStmts, loc.t, loc, true)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
@@ -386,7 +387,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
   # XXX work around a bug: No type information for open arrays possible:
   if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
   var a = "&" & s.loc.r
-  if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
+  if s.kind == skParam and ccgIntroducedPtr(p.config, s): a = s.loc.r
   lineF(p, cpsInit,
        "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
        [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
@@ -414,7 +415,7 @@ proc assignLocalVar(p: BProc, n: PNode) =
   #assert(s.loc.k == locNone) # not yet assigned
   # this need not be fulfilled for inline procs; they are regenerated
   # for each module that uses them!
-  let nl = if optLineDir in p.config.options: "" else: tnl
+  let nl = if optLineDir in p.config.options: "" else: "\L"
   let decl = localVarDecl(p, n) & ";" & nl
   line(p, cpsLocals, decl)
   localDebugInfo(p, n.sym)
@@ -651,33 +652,33 @@ proc cgsym(m: BModule, name: string): Rope =
   result = sym.loc.r
 
 proc generateHeaders(m: BModule) =
-  add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
+  add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L")
 
   for it in m.headerFiles:
     if it[0] == '#':
-      add(m.s[cfsHeaders], rope(it.replace('`', '"') & tnl))
+      add(m.s[cfsHeaders], rope(it.replace('`', '"') & "\L"))
     elif it[0] notin {'\"', '<'}:
       addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
     else:
       addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
-  add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl)
-  add(m.s[cfsHeaders], "#undef MIPSEB" & tnl)
-  add(m.s[cfsHeaders], "#undef MIPSEL" & tnl)
-  add(m.s[cfsHeaders], "#undef PPC" & tnl)
-  add(m.s[cfsHeaders], "#undef R3000" & tnl)
-  add(m.s[cfsHeaders], "#undef R4000" & tnl)
-  add(m.s[cfsHeaders], "#undef i386" & tnl)
-  add(m.s[cfsHeaders], "#undef linux" & tnl)
-  add(m.s[cfsHeaders], "#undef mips" & tnl)
-  add(m.s[cfsHeaders], "#undef near" & tnl)
-  add(m.s[cfsHeaders], "#undef powerpc" & tnl)
-  add(m.s[cfsHeaders], "#undef unix" & tnl)
+  add(m.s[cfsHeaders], "#undef LANGUAGE_C\L")
+  add(m.s[cfsHeaders], "#undef MIPSEB\L")
+  add(m.s[cfsHeaders], "#undef MIPSEL\L")
+  add(m.s[cfsHeaders], "#undef PPC\L")
+  add(m.s[cfsHeaders], "#undef R3000\L")
+  add(m.s[cfsHeaders], "#undef R4000\L")
+  add(m.s[cfsHeaders], "#undef i386\L")
+  add(m.s[cfsHeaders], "#undef linux\L")
+  add(m.s[cfsHeaders], "#undef mips\L")
+  add(m.s[cfsHeaders], "#undef near\L")
+  add(m.s[cfsHeaders], "#undef powerpc\L")
+  add(m.s[cfsHeaders], "#undef unix\L")
 
 proc openNamespaceNim(): Rope =
-  result.add("namespace Nim {" & tnl)
+  result.add("namespace Nim {\L")
 
 proc closeNamespaceNim(): Rope =
-  result.add("}" & tnl)
+  result.add("}\L")
 
 proc closureSetup(p: BProc, prc: PSym) =
   if tfCapturesEnv notin prc.typ.flags: return
@@ -727,7 +728,7 @@ proc genProcAux(m: BModule, prc: PSym) =
       internalError(m.config, prc.info, "proc has no result symbol")
     let resNode = prc.ast.sons[resultPos]
     let res = resNode.sym # get result symbol
-    if not isInvalidReturnType(prc.typ.sons[0]):
+    if not isInvalidReturnType(m.config, prc.typ.sons[0]):
       if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
       if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
         var decl = localVarDecl(p, resNode)
@@ -741,7 +742,7 @@ proc genProcAux(m: BModule, prc: PSym) =
         initLocalVar(p, res, immediateAsgn=false)
       returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
     else:
-      fillResult(resNode)
+      fillResult(p.config, resNode)
       assignParam(p, res)
       if sfNoInit notin prc.flags: resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
@@ -756,10 +757,10 @@ proc genProcAux(m: BModule, prc: PSym) =
   genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
   var generatedProc: Rope
   if sfNoReturn in prc.flags:
-    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
       header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
       header = "__declspec(naked) " & header
     generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
                          header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
@@ -803,13 +804,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
                         getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
   elif not containsOrIncl(m.declaredProtos, sym.id):
     var header = genProcHeader(m, sym)
-    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
+    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
       header = "__declspec(noreturn) " & header
     if sym.typ.callConv != ccInline and requiresExternC(m, sym):
       header = "extern \"C\" " & header
-    if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
+    if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
       header.add(" __attribute__((naked))")
-    if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
+    if sfNoReturn in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
       header.add(" __attribute__((noreturn))")
     add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header))
 
@@ -918,10 +919,10 @@ proc genVarPrototype(m: BModule, n: PNode) =
       addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
 
 proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} =
-  addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
-               "#define NIM_INTBITS $1" & tnl, [
-    platform.CPU[targetCPU].intSize.rope])
-  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl)
+  addf(result, "#define NIM_NEW_MANGLING_RULES\L" &
+               "#define NIM_INTBITS $1\L", [
+    platform.CPU[conf.target.targetCPU].intSize.rope])
+  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE\L")
 
 proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
   if optCompileOnly in conf.globalOptions:
@@ -936,9 +937,9 @@ proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
         "/* Compiled for: $2, $3, $4 */$N" &
         "/* Command for C compiler:$n   $5 */$N") %
         [rope(VersionAsString),
-        rope(platform.OS[targetOS].name),
-        rope(platform.CPU[targetCPU].name),
-        rope(extccomp.CC[extccomp.cCompiler].name),
+        rope(platform.OS[conf.target.targetOS].name),
+        rope(platform.CPU[conf.target.targetCPU].name),
+        rope(extccomp.CC[conf.cCompiler].name),
         rope(getCompileCFileCmd(conf, cfile))]
 
 proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
@@ -948,8 +949,8 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
 proc genFilenames(m: BModule): Rope =
   discard cgsym(m, "dbgRegisterFilename")
   result = nil
-  for i in 0..<fileInfos.len:
-    result.addf("dbgRegisterFilename($1);$N", [fileInfos[i].projPath.makeCString])
+  for i in 0..<m.config.m.fileInfos.len:
+    result.addf("dbgRegisterFilename($1);$N", [m.config.m.fileInfos[i].projPath.makeCString])
 
 proc genMainProc(m: BModule) =
   const
@@ -1047,7 +1048,7 @@ proc genMainProc(m: BModule) =
       "}$N$N"
 
   var nimMain, otherMain: FormatStr
-  if platform.targetOS == osWindows and
+  if m.config.target.targetOS == osWindows and
       m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
     if optGenGuiApp in m.config.globalOptions:
       nimMain = WinNimMain
@@ -1056,13 +1057,13 @@ proc genMainProc(m: BModule) =
       nimMain = WinNimDllMain
       otherMain = WinCDllMain
     m.includeHeader("<windows.h>")
-  elif platform.targetOS == osGenode:
+  elif m.config.target.targetOS == osGenode:
     nimMain = GenodeNimMain
     otherMain = ComponentConstruct
   elif optGenDynLib in m.config.globalOptions:
     nimMain = PosixNimDllMain
     otherMain = PosixCDllMain
-  elif platform.targetOS == osStandalone:
+  elif m.config.target.targetOS == osStandalone:
     nimMain = PosixNimMain
     otherMain = StandaloneCMain
   else:
@@ -1073,12 +1074,12 @@ proc genMainProc(m: BModule) =
     m.g.breakpoints.add(m.genFilenames)
 
   let initStackBottomCall =
-    if platform.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
+    if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
   inc(m.labels)
   appcg(m, m.s[cfsProcs], PreMainBody, [
     m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
-     if emulatedThreadVars(m.config) and platform.targetOS != osStandalone:
+     if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
        ropecg(m, "\t#initThreadVarsEmulation();$N")
      else:
        "".rope,
@@ -1088,7 +1089,7 @@ proc genMainProc(m: BModule) =
         [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
   if optNoMain notin m.config.globalOptions:
     if optUseNimNamespace in m.config.globalOptions:
-      m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl
+      m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;\L"
 
     appcg(m, m.s[cfsProcs], otherMain, [])
     if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim()
@@ -1249,7 +1250,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
     excl(result.postInitProc.options, optStackTrace)
   let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
                 else: ""
-  open(result.ndi, ndiName)
+  open(result.ndi, ndiName, g.config)
 
 proc nullify[T](arr: var T) =
   for i in low(arr)..high(arr):
@@ -1299,13 +1300,15 @@ proc resetModule*(m: BModule) =
 proc resetCgenModules*(g: BModuleList) =
   for m in cgenModules(g): resetModule(m)
 
-proc rawNewModule(g: BModuleList; module: PSym): BModule =
-  result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
+proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
+  result = rawNewModule(g, module, toFullPath(conf, module.position.FileIndex))
 
-proc newModule(g: BModuleList; module: PSym): BModule =
+proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
   # we should create only one cgen module for each module sym
-  result = rawNewModule(g, module)
-  growCache g.modules, module.position
+  result = rawNewModule(g, module, conf)
+  if module.position >= g.modules.len:
+    setLen(g.modules, module.position + 1)
+  #growCache g.modules, module.position
   g.modules[module.position] = result
 
 template injectG() {.dirty.} =
@@ -1313,9 +1316,9 @@ template injectG() {.dirty.} =
     graph.backend = newModuleList(graph)
   let g = BModuleList(graph.backend)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   injectG()
-  result = newModule(g, module)
+  result = newModule(g, module, graph.config)
   if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
     let f = if graph.config.headerFile.len > 0: graph.config.headerFile
             else: graph.config.projectFull
@@ -1347,7 +1350,8 @@ proc writeHeader(m: BModule) =
   result.addf("N_CDECL(void, NimMain)(void);$n", [])
   if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
   result.addf("#endif /* $1 */$n", [guard])
-  writeRope(result, m.filename)
+  if not writeRope(result, m.filename):
+    rawMessage(m.config, errCannotOpenFile, m.filename)
 
 proc getCFile(m: BModule): string =
   let ext =
@@ -1356,11 +1360,12 @@ proc getCFile(m: BModule): string =
       else: ".c"
   result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
 
-proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
-  injectG()
-  var m = newModule(g, module)
-  readMergeInfo(getCFile(m), m)
-  result = m
+when false:
+  proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
+    injectG()
+    var m = newModule(g, module, graph.config)
+    readMergeInfo(getCFile(m), m)
+    result = m
 
 proc myProcess(b: PPassContext, n: PNode): PNode =
   result = n
@@ -1368,7 +1373,8 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   var m = BModule(b)
   if passes.skipCodegen(m.config, n): return
   m.initProc.options = initProcOptions(m)
-  softRnl = if optLineDir in m.config.options: noRnl else: rnl
+  #softRnl = if optLineDir in m.config.options: noRnl else: rnl
+  # XXX replicate this logic!
   genStmts(m.initProc, n)
 
 proc finishModule(m: BModule) =
@@ -1395,12 +1401,14 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
           echo "diff ", cfile.cname, ".backup ", cfile.cname
         else:
           echo "new file ", cfile.cname
-      writeRope(code, cfile.cname)
+      if not writeRope(code, cfile.cname):
+        rawMessage(m.config, errCannotOpenFile, cfile.cname)
       return
     if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
       result = false
   else:
-    writeRope(code, cfile.cname)
+    if not writeRope(code, cfile.cname):
+      rawMessage(m.config, errCannotOpenFile, cfile.cname)
 
 # We need 2 different logics here: pending modules (including
 # 'nim__dat') may require file merging for the combination of dead code
@@ -1412,7 +1420,7 @@ proc writeModule(m: BModule, pending: bool) =
   # generate code for the init statements of the module:
   let cfile = getCFile(m)
 
-  if m.rd == nil or optForceFullMake in m.config.globalOptions:
+  if true or optForceFullMake in m.config.globalOptions:
     genInitCode(m)
     finishTypeDescriptions(m)
     if sfMainModule in m.module.flags:
@@ -1435,7 +1443,8 @@ proc writeModule(m: BModule, pending: bool) =
     genInitCode(m)
     finishTypeDescriptions(m)
     var code = genModule(m, cf)
-    writeRope(code, cfile)
+    if not writeRope(code, cfile):
+      rawMessage(m.config, errCannotOpenFile, cfile)
     addFileToCompile(m.config, cf)
   else:
     # Consider: first compilation compiles ``system.nim`` and produces
@@ -1456,7 +1465,8 @@ proc updateCachedModule(m: BModule) =
     finishTypeDescriptions(m)
 
     var code = genModule(m, cf)
-    writeRope(code, cfile)
+    if not writeRope(code, cfile):
+      rawMessage(m.config, errCannotOpenFile, cfile)
   else:
     cf.flags = {CfileFlag.Cached}
   addFileToCompile(m.config, cf)
@@ -1469,7 +1479,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   # if the module is cached, we don't regenerate the main proc
   # nor the dispatchers? But if the dispatchers changed?
   # XXX emit the dispatchers into its own .c file?
-  if b.rd != nil: return
   if n != nil:
     m.initProc.options = initProcOptions(m)
     genStmts(m.initProc, n)
@@ -1492,14 +1501,10 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
   if g.generatedHeader != nil: finishModule(g.generatedHeader)
   while g.forwardedProcsCounter > 0:
     for m in cgenModules(g):
-      if m.rd == nil:
-        finishModule(m)
+      finishModule(m)
   for m in cgenModules(g):
-    if m.rd != nil:
-      m.updateCachedModule
-    else:
-      m.writeModule(pending=true)
+    m.writeModule(pending=true)
   writeMapping(config, g.mapping)
   if g.generatedHeader != nil: writeHeader(g.generatedHeader)
 
-const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
+const cgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index ce3fc2f90..aba317e7c 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -11,9 +11,8 @@
 
 import
   ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
-  tables, ndi
+  tables, ndi, lineinfos
 
-from msgs import TLineInfo
 from modulegraphs import ModuleGraph
 
 type
@@ -120,6 +119,17 @@ type
     graph*: ModuleGraph
     strVersion*, seqVersion*: int # version of the string/seq implementation to use
 
+    nimtv*: Rope            # Nim thread vars; the struct body
+    nimtvDeps*: seq[PType]  # type deps: every module needs whole struct
+    nimtvDeclared*: IntSet  # so that every var/field exists only once
+                            # in the struct
+                            # 'nimtv' is incredibly hard to modularize! Best
+                            # effort is to store all thread vars in a ROD
+                            # section and with their type deps and load them
+                            # unconditionally...
+                            # nimtvDeps is VERY hard to cache because it's
+                            # not a list of IDs nor can it be made to be one.
+
   TCGen = object of TPassContext # represents a C source file
     s*: TCFileSections        # sections of the C file
     flags*: set[Codegenflag]
@@ -178,7 +188,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
 
 proc newModuleList*(g: ModuleGraph): BModuleList =
   BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config,
-    graph: g)
+    graph: g, nimtvDeps: @[], nimtvDeclared: initIntSet())
 
 iterator cgenModules*(g: BModuleList): BModule =
   for i in 0..high(g.modules):
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 1d72952e2..5b58e6498 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs, configuration
+  sempass2, strutils, modulegraphs, lineinfos
 
 proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
@@ -115,7 +115,7 @@ proc createDispatcher(s: PSym): PSym =
   # we can't inline the dispatcher itself (for now):
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
   disp.ast = copyTree(s.ast)
-  disp.ast.sons[bodyPos] = ast.emptyNode
+  disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
   disp.loc.r = nil
   if s.typ.sons[0] != nil:
     if disp.ast.sonsLen > resultPos:
@@ -124,7 +124,7 @@ proc createDispatcher(s: PSym): PSym =
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
       # be updated in fixupDispatcher().
-      disp.ast.addSon(ast.emptyNode)
+      disp.ast.addSon(newNodeI(nkEmpty, s.info))
   attachDispatcher(s, newSymNode(disp))
   # attach to itself to prevent bugs:
   attachDispatcher(disp, newSymNode(disp))
@@ -137,7 +137,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # the lock level of the dispatcher needs to be updated/checked
   # against that of the method.
   if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
-     disp.ast.sons[resultPos] == ast.emptyNode:
+     disp.ast.sons[resultPos].kind == nkEmpty:
     disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
 
   # The following code works only with lock levels, so we disable
@@ -184,7 +184,7 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
   #  internalError(s.info, "no method dispatcher found")
   if witness != nil:
     localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
-                       "' to method defined here: " & $witness.info)
+                       "' to method defined here: " & g.config$witness.info)
   elif sfBase notin s.flags:
     message(g.config, s.info, warnUseBase)
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 09f63f0f5..cd9ebbe7d 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -26,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
   os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
-  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration
+  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos
 
 # but some have deps to imported modules. Yay.
 bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@@ -56,21 +56,21 @@ const
     x
   AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc
 
-proc getCommandLineDesc(): string =
-  result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
-                           CPU[platform.hostCPU].name, CompileDate]) &
+proc getCommandLineDesc(conf: ConfigRef): string =
+  result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
+                           CPU[conf.target.hostCPU].name, CompileDate]) &
                            Usage
 
 proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
     msgQuit(0)
 
 proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, (HelpMessage % [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]) &
                                  AdvancedUsage,
                {msgStdout})
     msgQuit(0)
@@ -78,8 +78,8 @@ proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
 proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]) &
                                  Usage & AdvancedUsage,
                {msgStdout})
     msgQuit(0)
@@ -87,8 +87,8 @@ proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
 proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]),
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]),
                {msgStdout})
 
     const gitHash = gorge("git log -n 1 --format=%H").strip
@@ -103,7 +103,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
 
 proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) =
   if not helpWritten:
-    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
     helpWritten = true
 
 proc addPrefix(switch: string): string =
@@ -178,11 +178,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
   if i < len(arg) and (arg[i] in {':', '='}): inc(i)
   else: invalidCmdLineOption(conf, pass, orig, info)
   if state == wHint:
-    let x = findStr(configuration.HintsToStr, id)
+    let x = findStr(lineinfos.HintsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(hintMin))
     else: localError(conf, info, "unknown hint: " & id)
   else:
-    let x = findStr(configuration.WarningsToStr, id)
+    let x = findStr(lineinfos.WarningsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(warnMin))
     else: localError(conf, info, "unknown warning: " & id)
   case substr(arg, i).normalize
@@ -289,14 +289,14 @@ proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
           else:
             conf.projectPath / path
   try:
-    result = pathSubs(conf, p, info.toFullPath().splitFile().dir)
+    result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
   except ValueError:
     localError(conf, info, "invalid path: " & p)
     result = p
 
 proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string =
   let path = if path[0] == '"': strutils.unescape(path) else: path
-  let basedir = info.toFullPath().splitFile().dir
+  let basedir = toFullPath(conf, info).splitFile().dir
   let p = if os.isAbsolute(path) or '$' in path:
             path
           else:
@@ -322,9 +322,9 @@ proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
 
   let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
   if dirtyOriginalIdx.int32 >= 0:
-    msgs.setDirtyFile(dirtyOriginalIdx, a[0])
+    msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0])
 
-  gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
+  conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
 
 proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
@@ -334,7 +334,7 @@ proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
     localError(conf, info, errInvalidNumber % a[1])
   if parseUtils.parseInt(a[2], column) <= 0:
     localError(conf, info, errInvalidNumber % a[2])
-  gTrackPos = newLineInfo(conf, a[0], line, column)
+  conf.m.trackPos = newLineInfo(conf, a[0], line, column)
 
 proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
@@ -344,8 +344,6 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in
 proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
                     conf: ConfigRef) =
   var
-    theOS: TSystemOS
-    cpu: TSystemCPU
     key, val: string
   case switch.normalize
   of "path", "p":
@@ -565,13 +563,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
   of "cincludes":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cIncludes.add processPath(conf, arg, info)
   of "clibdir":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
   of "clib":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cLinkedLibs.add processPath(conf, arg, info)
   of "header":
     if conf != nil: conf.headerFile = arg
     incl(conf.globalOptions, optGenIndex)
@@ -592,17 +590,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "os":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
-      theOS = platform.nameToOS(arg)
+      let theOS = platform.nameToOS(arg)
       if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg)
-      elif theOS != platform.hostOS:
-        setTarget(theOS, targetCPU)
+      elif theOS != conf.target.hostOS:
+        setTarget(conf.target, theOS, conf.target.targetCPU)
   of "cpu":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
-      cpu = platform.nameToCPU(arg)
+      let cpu = platform.nameToCPU(arg)
       if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg)
-      elif cpu != platform.hostCPU:
-        setTarget(targetOS, cpu)
+      elif cpu != conf.target.hostCPU:
+        setTarget(conf.target, conf.target.targetOS, cpu)
   of "run", "r":
     expectNoArg(conf, switch, arg, pass, info)
     incl(conf.globalOptions, optRun)
@@ -628,9 +626,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "help", "h":
     expectNoArg(conf, switch, arg, pass, info)
     helpOnError(conf, pass)
-  of "symbolfiles":
+  of "symbolfiles", "incremental":
     case arg.normalize
-    of "on": conf.symbolFiles = enabledSf
+    of "on": conf.symbolFiles = v2Sf
     of "off": conf.symbolFiles = disabledSf
     of "writeonly": conf.symbolFiles = writeOnlySf
     of "readonly": conf.symbolFiles = readOnlySf
diff --git a/compiler/configuration.nim b/compiler/configuration.nim
index f9f0e623c..22e0b834e 100644
--- a/compiler/configuration.nim
+++ b/compiler/configuration.nim
@@ -1,360 +1,6 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2018 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
+## Use the module 'lineinfos' instead!
 
-## This module contains the rather excessive configuration object that
-## needs to be passed around to everything so that the compiler becomes
-## more useful as a library.
+{.deprecated.}
 
-import tables
-
-const
-  explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
-
-type
-  TMsgKind* = enum
-    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
-    errXExpected,
-    errGridTableNotImplemented,
-    errGeneralParseError,
-    errNewSectionExpected,
-    errInvalidDirectiveX,
-    errGenerated,
-    errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnTypelessParam,
-    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnInconsistentSpacing, warnUser,
-    hintSuccess, hintSuccessX,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
-    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
-    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
-    hintConditionAlwaysTrue, hintName, hintPattern,
-    hintExecuting, hintLinking, hintDependency,
-    hintSource, hintPerformance, hintStackTrace, hintGCStats,
-    hintUser, hintUserRaw
-
-const
-  MsgKindToStr*: array[TMsgKind, string] = [
-    errUnknown: "unknown error",
-    errInternal: "internal error: $1",
-    errIllFormedAstX: "illformed AST: $1",
-    errCannotOpenFile: "cannot open '$1'",
-    errXExpected: "'$1' expected",
-    errGridTableNotImplemented: "grid table is not implemented",
-    errGeneralParseError: "general parse error",
-    errNewSectionExpected: "new section expected",
-    errInvalidDirectiveX: "invalid directive: '$1'",
-    errGenerated: "$1",
-    errUser: "$1",
-    warnCannotOpenFile: "cannot open '$1'",
-    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
-    warnXIsNeverRead: "'$1' is never read",
-    warnXmightNotBeenInit: "'$1' might not have been initialized",
-    warnDeprecated: "$1 is deprecated",
-    warnConfigDeprecated: "config file '$1' is deprecated",
-    warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
-    warnUnknownMagic: "unknown magic '$1' might crash the compiler",
-    warnRedefinitionOfLabel: "redefinition of label '$1'",
-    warnUnknownSubstitutionX: "unknown substitution '$1'",
-    warnLanguageXNotSupported: "language '$1' not supported",
-    warnFieldXNotSupported: "field '$1' not supported",
-    warnCommentXIgnored: "comment '$1' ignored",
-    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
-    warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
-    warnWriteToForeignHeap: "write to foreign heap",
-    warnUnsafeCode: "unsafe code: '$1'",
-    warnEachIdentIsTuple: "each identifier is a tuple",
-    warnShadowIdent: "shadowed identifier: '$1'",
-    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
-    warnProveField: "cannot prove that field '$1' is accessible",
-    warnProveIndex: "cannot prove index '$1' is valid",
-    warnGcUnsafe: "not GC-safe: '$1'",
-    warnGcUnsafe2: "$1",
-    warnUninit: "'$1' might not have been initialized",
-    warnGcMem: "'$1' uses GC'ed memory",
-    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
-    warnLockLevel: "$1",
-    warnResultShadowed: "Special variable 'result' is shadowed.",
-    warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
-    warnUser: "$1",
-    hintSuccess: "operation successful",
-    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
-    hintLineTooLong: "line too long",
-    hintXDeclaredButNotUsed: "'$1' is declared but not used",
-    hintConvToBaseNotNeeded: "conversion to base object is not needed",
-    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
-    hintExprAlwaysX: "expression evaluates always to '$1'",
-    hintQuitCalled: "quit() called",
-    hintProcessing: "$1",
-    hintCodeBegin: "generated code listing:",
-    hintCodeEnd: "end of listing",
-    hintConf: "used config file '$1'",
-    hintPath: "added path: '$1'",
-    hintConditionAlwaysTrue: "condition is always true: '$1'",
-    hintName: "name should be: '$1'",
-    hintPattern: "$1",
-    hintExecuting: "$1",
-    hintLinking: "",
-    hintDependency: "$1",
-    hintSource: "$1",
-    hintPerformance: "$1",
-    hintStackTrace: "$1",
-    hintGCStats: "$1",
-    hintUser: "$1",
-    hintUserRaw: "$1"]
-
-const
-  WarningsToStr* = ["CannotOpenFile", "OctalEscape",
-    "XIsNeverRead", "XmightNotBeenInit",
-    "Deprecated", "ConfigDeprecated",
-    "SmallLshouldNotBeUsed", "UnknownMagic",
-    "RedefinitionOfLabel", "UnknownSubstitutionX",
-    "LanguageXNotSupported", "FieldXNotSupported",
-    "CommentXIgnored",
-    "TypelessParam", "UseBase", "WriteToForeignHeap",
-    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
-    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
-    "GcMem", "Destructor", "LockLevel", "ResultShadowed",
-    "Spacing", "User"]
-
-  HintsToStr* = ["Success", "SuccessX", "LineTooLong",
-    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
-    "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
-    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
-    "Source", "Performance", "StackTrace", "GCStats",
-    "User", "UserRaw"]
-
-const
-  fatalMin* = errUnknown
-  fatalMax* = errInternal
-  errMin* = errUnknown
-  errMax* = errUser
-  warnMin* = warnCannotOpenFile
-  warnMax* = pred(hintSuccess)
-  hintMin* = hintSuccess
-  hintMax* = high(TMsgKind)
-
-static:
-  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
-  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
-
-type
-  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
-  TNoteKinds* = set[TNoteKind]
-
-const
-  NotesVerbosity*: array[0..3, TNoteKinds] = [
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintSuccessX, hintPath, hintConf,
-                                         hintProcessing, hintPattern,
-                                         hintDependency,
-                                         hintExecuting, hintLinking,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintPath,
-                                         hintDependency,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
-    {low(TNoteKind)..high(TNoteKind)}]
-
-const
-  errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
-  errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
-
-#[
-errStringLiteralExpected: "string literal expected",
-errIntLiteralExpected: "integer literal expected",
-errIdentifierExpected: "identifier expected, but found '$1'",
-errNewlineExpected: "newline expected, but found '$1'",
-errInvalidModuleName: "invalid module name: '$1'",
-errOnOrOffExpected: "'on' or 'off' expected",
-errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
-errInvalidPragma: "invalid pragma",
-errUnknownPragma: "unknown pragma: '$1'",
-errAtPopWithoutPush: "'pop' without a 'push' pragma",
-errEmptyAsm: "empty asm statement",
-errInvalidIndentation: "invalid indentation",
-
-errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
-errAttemptToRedefine: ,
-errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
-errStmtExpected: "statement expected",
-errInvalidLabel: "'$1' is no label",
-errInvalidCmdLineOption: "invalid command line option: '$1'",
-errCmdLineArgExpected: "argument for command line option expected: '$1'",
-errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
-errInvalidVarSubstitution: "invalid variable substitution in '$1'",
-errUnknownVar: "unknown variable: '$1'",
-errUnknownCcompiler: "unknown C compiler: '$1'",
-errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found",
-errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found",
-errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
-,
-errInvalidMultipleAsgn: "multiple assignment is not allowed",
-errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
-errUndeclaredField: "undeclared field: '$1'",
-errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
-errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
-errTypeExpected: "type expected",
-errSystemNeeds: "system module needs '$1'",
-errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
-errNotOverloadable: ,
-errInvalidArgForX: "invalid argument for '$1'",
-errStmtHasNoEffect: "statement has no effect",
-,
-errXExpectsArrayType: "'$1' expects an array type",
-errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
-errExprXAmbiguous: "expression '$1' ambiguous in this context",
-errConstantDivisionByZero: ,
-errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
-errOverOrUnderflow: ,
-errCannotEvalXBecauseIncompletelyDefined: ,
-errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
-errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
-errNilAccess: "attempt to access a nil address",
-errIndexOutOfBounds: "index out of bounds",
-errIndexTypesDoNotMatch: "index types do not match",
-errBracketsInvalidForType: "'[]' operator invalid for this type",
-errValueOutOfSetBounds: "value out of set bounds",
-errFieldNotInit: "field '$1' not initialized",
-errExprXCannotBeCalled: "expression '$1' cannot be called",
-errExprHasNoType: "expression has no type",
-errExprXHasNoType:,
-errCastNotInSafeMode: "'cast' not allowed in safe mode",
-errExprCannotBeCastToX: ,
-errCommaOrParRiExpected: "',' or ')' expected",
-errCurlyLeOrParLeExpected: "'{' or '(' expected",
-errSectionExpected: "section ('type', 'proc', etc.) expected",
-errRangeExpected: "range expected",
-errMagicOnlyInSystem: "'magic' only allowed in system module",
-errPowerOfTwoExpected: "power of two expected",
-errStringMayNotBeEmpty: "string literal may not be empty",
-errCallConvExpected: "calling convention expected",
-errProcOnlyOneCallConv: "a proc can only have one calling convention",
-errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
-errExprMustBeBool: "expression must be of type 'bool'",
-errConstExprExpected: "constant expression expected",
-errDuplicateCaseLabel: "duplicate case label",
-errRangeIsEmpty: "range is empty",
-,
-errSelectorMustBeOrdinal: "selector must be of an ordinal type",
-errOrdXMustNotBeNegative: "ord($1) must not be negative",
-errLenXinvalid: "len($1) must be less than 32768",
-errTypeXhasUnknownSize: "type '$1' has unknown size",
-errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
-errConstNeedsValue: "a constant needs a value",
-errResultCannotBeOpenArray: "the result type cannot be on open array",
-errSizeTooBig: "computing the type's size produced an overflow",
-errInheritanceOnlyWithEnums: "inheritance only works with an enum",
-errIllegalRecursionInTypeX:,
-errCannotInstantiateX: "cannot instantiate: '$1'",
-errTypeMismatch: "type mismatch: got <",
-errButExpected: "but expected one of: ",
-errButExpectedX: "but expected '$1'",
-errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
-errWrongNumberOfArguments: "wrong number of arguments",
-errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
-errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
-errXCannotBePassedToProcVar: ,
-,
-errImplOfXexpected: ,
-
-errIllegalConvFromXtoY: ,
-errCannotBindXTwice: "cannot bind parameter '$1' twice",
-errInvalidOrderInArrayConstructor: ,
-errInvalidOrderInEnumX: "invalid order in enum '$1'",
-errEnumXHasHoles: "enum '$1' has holes",
-errExceptExpected: "'except' or 'finally' expected",
-errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
-errOptionExpected: ,
-errXisNoLabel: "'$1' is not a label",
-errNotAllCasesCovered: "not all cases are covered",
-errUnknownSubstitionVar: "unknown substitution variable: '$1'",
-errComplexStmtRequiresInd: "complex statement requires indentation",
-errXisNotCallable: "'$1' is not callable",
-errNoPragmasAllowedForX: "no pragmas allowed for $1",
-,
-errInvalidParamKindX: "invalid param kind: '$1'",
-errDefaultArgumentInvalid: "default argument invalid",
-errNamedParamHasToBeIdent: "named parameter has to be an identifier",
-errNoReturnTypeForX: "no return type allowed for $1",
-errConvNeedsOneArg: "a type conversion needs exactly one argument",
-errInvalidPragmaX: ,
-errXNotAllowedHere: "$1 not allowed here",
-errXisNoType: "invalid type: '$1'",
-errCircumNeedsPointer: "'[]' needs a pointer or reference type",
-errInvalidExpression: "invalid expression",
-errInvalidExpressionX: "invalid expression: '$1'",
-errEnumHasNoValueX: "enum has no value '$1'",
-,
-errNoCommand: "no command given",
-errInvalidCommandX: "invalid command: '$1'",
-errXNeedsParamObjectType: ,
-errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
-errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
-errInstantiationFrom: "template/generic instantiation from here",
-errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
-errCommandExpectsFilename: "command expects a filename argument",
-errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
-errXExpected: "'$1' expected",
-,
-errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
-errInvalidSectionStart: "invalid section start",
-errGridTableNotImplemented: "grid table is not implemented",
-errGeneralParseError: "general parse error",
-errNewSectionExpected: "new section expected",
-errWhitespaceExpected: "whitespace expected, got '$1'",
-errXisNoValidIndexFile: "'$1' is no valid index file",
-errCannotRenderX: "cannot render reStructuredText element '$1'",
-errVarVarTypeNotAllowed: ,
-errInstantiateXExplicitly: "instantiate '$1' explicitly",
-errOnlyACallOpCanBeDelegator: ,
-errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
-errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
-                                   "because the parameter '$1' has a generic type",
-errDestructorNotGenericEnough: "Destructor signature is too specific. " &
-                               "A destructor must be associated will all instantiations of a generic type",
-errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
-                                "templates, macros and other inline iterators",
-errXExpectsTwoArguments: "'$1' expects two arguments",
-errXExpectsObjectTypes: "'$1' expects object types",
-errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
-errTooManyIterations: "interpretation requires too many iterations; " &
-  "if you are sure this is not a bug in your code edit " &
-  "compiler/vmdef.MaxLoopIterations and rebuild the compiler",
-errFieldXNotFound: "field '$1' cannot be found",
-errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
-errAssertionFailed: "assertion failed",
-errCannotGenerateCodeForX: "cannot generate code for '$1'",
-errXRequiresOneArgument: "$1 requires one parameter",
-errUnhandledExceptionX: "unhandled exception: $1",
-errCyclicTree: "macro returned a cyclic abstract syntax tree",
-errXisNoMacroOrTemplate: "'$1' is no macro or template",
-errXhasSideEffects: "'$1' can have side effects",
-errWrongSymbolX:,
-errIllegalCaptureX: "illegal capture '$1'",
-errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
-,
-]#
+import lineinfos
+export lineinfos
diff --git a/compiler/depends.nim b/compiler/depends.nim
index 732404232..d0a1139ef 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -14,46 +14,51 @@ import
 
 from modulegraphs import ModuleGraph
 
-proc generateDot*(project: string)
-
 type
   TGen = object of TPassContext
-    module*: PSym
+    module: PSym
     config: ConfigRef
+    graph: ModuleGraph
   PGen = ref TGen
 
-var gDotGraph: Rope # the generated DOT file; we need a global variable
+  Backend = ref object of RootRef
+    dotGraph: Rope
 
-proc addDependencyAux(importing, imported: string) =
-  addf(gDotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
+proc addDependencyAux(b: Backend; importing, imported: string) =
+  addf(b.dotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
   # s1 -> s2_4[label="[0-9]"];
 
 proc addDotDependency(c: PPassContext, n: PNode): PNode =
   result = n
-  var g = PGen(c)
+  let g = PGen(c)
+  let b = Backend(g.graph.backend)
   case n.kind
   of nkImportStmt:
     for i in countup(0, sonsLen(n) - 1):
       var imported = getModuleName(g.config, n.sons[i])
-      addDependencyAux(g.module.name.s, imported)
+      addDependencyAux(b, g.module.name.s, imported)
   of nkFromStmt, nkImportExceptStmt:
     var imported = getModuleName(g.config, n.sons[0])
-    addDependencyAux(g.module.name.s, imported)
+    addDependencyAux(b, g.module.name.s, imported)
   of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
     for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
   else:
     discard
 
-proc generateDot(project: string) =
-  writeRope("digraph $1 {$n$2}$n" % [
-      rope(changeFileExt(extractFilename(project), "")), gDotGraph],
+proc generateDot*(graph: ModuleGraph; project: string) =
+  let b = Backend(graph.backend)
+  discard writeRope("digraph $1 {$n$2}$n" % [
+      rope(changeFileExt(extractFilename(project), "")), b.dotGraph],
             changeFileExt(project, "dot"))
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   var g: PGen
   new(g)
   g.module = module
   g.config = graph.config
+  g.graph = graph
+  if graph.backend == nil:
+    graph.backend = Backend(dotGraph: nil)
   result = g
 
 const gendependPass* = makePass(open = myOpen, process = addDotDependency)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 31c735794..0395728c2 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -116,8 +116,8 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  strutils, options, dfa, lowerings, rodread, tables, modulegraphs,
-  configuration
+  strutils, options, dfa, lowerings, tables, modulegraphs,
+  lineinfos
 
 const
   InterestingSyms = {skVar, skResult, skLet}
@@ -132,10 +132,11 @@ type
     destroys, topLevelVars: PNode
     toDropBit: Table[int, PSym]
     graph: ModuleGraph
+    emptyNode: PNode
 
 proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
   # XXX why are temps fields in an object here?
-  let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info)
+  let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info)
   f.typ = typ
   rawAddField c.tmpObj, f
   result = rawDirectAccess(c.tmp, f)
@@ -243,17 +244,17 @@ proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
   genOp(t.destructor, "=destroy")
 
 proc addTopVar(c: var Con; v: PNode) =
-  c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
+  c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
 
 proc dropBit(c: var Con; s: PSym): PSym =
   result = c.toDropBit.getOrDefault(s.id)
   assert result != nil
 
 proc registerDropBit(c: var Con; s: PSym) =
-  let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info)
+  let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
   result.typ = getSysType(c.graph, s.info, tyBool)
   let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
-  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal)
+  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal)
   c.toDropBit[s.id] = result
   # generate:
   #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
@@ -322,13 +323,13 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   # generate: (let tmp = v; reset(v); tmp)
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-  var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info)
+  var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
   var v = newNodeI(nkLetSection, n.info)
   let tempAsNode = newSymNode(temp)
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = c.emptyNode
   vpart.sons[2] = n
   add(v, vpart)
 
@@ -427,13 +428,14 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
     echo "injecting into ", n
   var c: Con
   c.owner = owner
-  c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
+  c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info)
   c.tmpObj = createObj(g, owner, n.info)
   c.tmp.typ = c.tmpObj
   c.destroys = newNodeI(nkStmtList, n.info)
   c.topLevelVars = newNodeI(nkVarSection, n.info)
   c.toDropBit = initTable[int, PSym]()
   c.graph = g
+  c.emptyNode = newNodeI(nkEmpty, n.info)
   let cfg = constructCfg(owner, n)
   shallowCopy(c.g, cfg)
   c.jumpTargets = initIntSet()
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 0fd706178..013242f62 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -23,7 +23,7 @@
 ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
 ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
 
-import ast, astalgo, types, intsets, tables, msgs, options
+import ast, astalgo, types, intsets, tables, msgs, options, lineinfos
 
 type
   InstrKind* = enum
@@ -54,7 +54,7 @@ type
     blocks: seq[TBlock]
 
 proc debugInfo(info: TLineInfo): string =
-  result = info.toFilename & ":" & $info.line
+  result = $info.line #info.toFilename & ":" & $info.line
 
 proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
   # for debugging purposes
@@ -87,7 +87,8 @@ proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
   ## echos the ControlFlowGraph for debugging purposes.
   var buf = ""
   codeListing(c, buf, start, last)
-  echo buf
+  when declared(echo):
+    echo buf
 
 proc forkI(c: var Con; n: PNode): TPosition =
   result = TPosition(c.code.len)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 7ab2f0eee..d463dc3c0 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -16,7 +16,7 @@ import
   wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
   packages/docutils/rst, packages/docutils/rstgen, times,
   packages/docutils/highlite, sempass2, json, xmltree, cgi,
-  typesrenderer, astalgo, modulepaths, configuration
+  typesrenderer, astalgo, modulepaths, lineinfos
 
 type
   TSections = array[TSymKind, Rope]
@@ -30,6 +30,7 @@ type
     types: TStrTable
     isPureRst: bool
     conf*: ConfigRef
+    cache*: IdentCache
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
@@ -86,10 +87,11 @@ proc parseRst(text, filename: string,
   result = rstParse(text, filename, line, column, hasToc, rstOptions,
                     docgenFindFile, compilerMsgHandler)
 
-proc newDocumentor*(filename: string, conf: ConfigRef): PDoc =
+proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc =
   declareClosures()
   new(result)
   result.conf = conf
+  result.cache = cache
   initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
                    conf.configVars, filename, {roSupportRawDirective},
                    docgenFindFile, compilerMsgHandler)
@@ -190,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string =
   result = ""
   var dummyHasToc: bool
   if n.comment != nil:
-    renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+    renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                toLinenumber(n.info), toColumn(n.info),
                                dummyHasToc, d.options, d.conf), result)
 
@@ -307,13 +309,13 @@ when false:
       result = findDocComment(n.sons[i])
       if result != nil: return
 
-  proc extractDocComment*(s: PSym, d: PDoc = nil): string =
+  proc extractDocComment*(s: PSym, d: PDoc): string =
     let n = findDocComment(s.ast)
     result = ""
     if not n.isNil:
       if not d.isNil:
         var dummyHasToc: bool
-        renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+        renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                      toLinenumber(n.info), toColumn(n.info),
                                      dummyHasToc, d.options + {roSkipPounds}),
                        result)
@@ -349,18 +351,18 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
   else:
     result = ""
 
-proc getNameIdent(n: PNode): PIdent =
+proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
   case n.kind
-  of nkPostfix: result = getNameIdent(n.sons[1])
-  of nkPragmaExpr: result = getNameIdent(n.sons[0])
+  of nkPostfix: result = getNameIdent(cache, n.sons[1])
+  of nkPragmaExpr: result = getNameIdent(cache, n.sons[0])
   of nkSym: result = n.sym.name
   of nkIdent: result = n.ident
   of nkAccQuoted:
     var r = ""
-    for i in 0..<n.len: r.add(getNameIdent(n[i]).s)
-    result = getIdent(r)
+    for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
+    result = getIdent(cache, r)
   of nkOpenSymChoice, nkClosedSymChoice:
-    result = getNameIdent(n[0])
+    result = getNameIdent(cache, n[0])
   else:
     result = nil
 
@@ -502,7 +504,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
   let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
   if docItemSeeSrc.len > 0:
     let cwd = canonicalizePath(d.conf, getCurrentDir())
-    var path = n.info.toFullPath
+    var path = toFullPath(d.conf, n.info)
     if path.startsWith(cwd):
       path = path[cwd.len+1 .. ^1].replace('\\', '/')
     let gitUrl = getConfigVar(d.conf, "git.url")
@@ -586,21 +588,21 @@ proc generateDoc*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt: add(d.modDesc, genComment(d, n))
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -630,23 +632,23 @@ proc generateJson*(d: PDoc, n: PNode) =
       d.add %{ "comment": %stripped, "line": %n.info.line.int,
                "col": %n.info.col }
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef:
     d.add genJsonItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef:
     d.add genJsonItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -673,23 +675,23 @@ proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
       let stripped = n.comment.substr(2).strip
       r.add stripped
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef:
     r.add genTagsItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef:
     r.add genTagsItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -777,10 +779,16 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
 
 proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
+  var success = true
+  var filename: string
   if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
+    filename = "<stdout>"
   else:
-    writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning)
+    filename = getOutFile2(d.conf, filename, outExt, "htmldocs")
+    success = writeRope(content, filename)
+  if not success:
+    rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename)
 
 proc writeOutputJson*(d: PDoc, filename, outExt: string,
                       useWarning = false) =
@@ -798,18 +806,18 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
     else:
       discard "fixme: error report"
 
-proc commandDoc*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   generateDoc(d, ast)
   writeOutput(d, conf.projectFull, HtmlExt)
   generateIndex(d)
 
-proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
+proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
   var filen = addFileExt(filename, "txt")
-  var d = newDocumentor(filen, conf)
+  var d = newDocumentor(filen, cache, conf)
   d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
                           status: int; content: string) =
     var outp: string
@@ -841,17 +849,16 @@ proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
   writeOutput(d, filename, outExt)
   generateIndex(d)
 
-proc commandRst2Html*(conf: ConfigRef) =
-  commandRstAux(conf, conf.projectFull, HtmlExt)
+proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
+  commandRstAux(cache, conf, conf.projectFull, HtmlExt)
 
-proc commandRst2TeX*(conf: ConfigRef) =
-  splitter = "\\-"
-  commandRstAux(conf, conf.projectFull, TexExt)
+proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) =
+  commandRstAux(cache, conf, conf.projectFull, TexExt)
 
-proc commandJson*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandJson*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   generateJson(d, ast)
   let json = d.jArray
@@ -861,12 +868,14 @@ proc commandJson*(conf: ConfigRef) =
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, JsonExt)
-    writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false)
+    let filename = getOutFile(conf, conf.projectFull, JsonExt)
+    if not writeRope(content, filename):
+      rawMessage(conf, errCannotOpenFile, filename)
 
-proc commandTags*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandTags*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   var
     content: Rope
@@ -876,9 +885,11 @@ proc commandTags*(conf: ConfigRef) =
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, TagsExt)
-    writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false)
+    let filename = getOutFile(conf, conf.projectFull, TagsExt)
+    if not writeRope(content, filename):
+      rawMessage(conf, errCannotOpenFile, filename)
 
-proc commandBuildIndex*(conf: ConfigRef) =
+proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
   var content = mergeIndexes(conf.projectFull).rope
 
   let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
@@ -887,4 +898,6 @@ proc commandBuildIndex*(conf: ConfigRef) =
       ["Index".rope, nil, nil, rope(getDateStr()),
                    rope(getClockStr()), content, nil, nil, nil])
   # no analytics because context is not available
-  writeRope(code, getOutFile(conf, "theindex", HtmlExt))
+  let filename = getOutFile(conf, "theindex", HtmlExt)
+  if not writeRope(code, filename):
+    rawMessage(conf, errCannotOpenFile, filename)
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index d9a73e1cd..068c47bb3 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,7 +11,7 @@
 # semantic checking.
 
 import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
+  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -25,7 +25,7 @@ template closeImpl(body: untyped) {.dirty.} =
   var g = PGen(p)
   let useWarning = sfMainModule notin g.module.flags
   #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId
-  if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or
+  if (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
       sfMainModule in g.module.flags:
     body
     try:
@@ -35,11 +35,11 @@ template closeImpl(body: untyped) {.dirty.} =
 
 proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
-    writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
+    writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning)
 
 proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
-    writeOutputJson(g.doc, g.module.filename, ".json", useWarning)
+    writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning)
 
 proc processNode(c: PPassContext, n: PNode): PNode =
   result = n
@@ -51,11 +51,11 @@ proc processNodeJson(c: PPassContext, n: PNode): PNode =
   var g = PGen(c)
   generateJson(g.doc, n)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   var g: PGen
   new(g)
   g.module = module
-  var d = newDocumentor(module.filename, graph.config)
+  var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config)
   d.hasToc = true
   g.doc = d
   result = g
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index 0e3d0609d..e863c8995 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -442,7 +442,7 @@ proc callForeignFunction*(call: PNode): PNode =
   libffi.call(cif, fn, retVal, args)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, typ.sons[0], nil)
     result.info = call.info
@@ -484,7 +484,7 @@ proc callForeignFunction*(fn: PNode, fntyp: PType,
   libffi.call(cif, fn, retVal, cargs)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, fntyp.sons[0], nil)
     result.info = info
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 01c56ec9c..d6c630e79 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -11,7 +11,7 @@
 
 import
   strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
-  rodread
+  lineinfos
 
 type
   TemplCtx = object
@@ -104,7 +104,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     let default = s.typ.n.sons[i].sym.ast
     if default.isNil or default.kind == nkEmpty:
       localError(conf, n.info, errWrongNumberOfArguments)
-      addSon(result, ast.emptyNode)
+      addSon(result, newNodeI(nkEmpty, n.info))
     else:
       addSon(result, default.copyTree)
 
@@ -114,7 +114,6 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
 
 # to prevent endless recursion in template instantiation
 const evalTemplateLimit* = 1000
-var evalTemplateCounter* = 0 # XXX remove this global
 
 proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
   when true:
@@ -140,8 +139,8 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
 
 proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
                    conf: ConfigRef; fromHlo=false): PNode =
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > evalTemplateLimit:
+  inc(conf.evalTemplateCounter)
+  if conf.evalTemplateCounter > evalTemplateLimit:
     globalError(conf, n.info, errTemplateInstantiationTooNested)
     result = n
 
@@ -170,5 +169,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
       evalTemplateAux(body.sons[i], args, ctx, result)
   result.flags.incl nfFromTemplate
   result = wrapInComesFrom(n.info, tmpl, result)
-  dec(evalTemplateCounter)
+  dec(conf.evalTemplateCounter)
 
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 3f0e6f611..615b8c1e1 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -9,19 +9,14 @@
 
 # Module providing functions for calling the different external C compilers
 # Uses some hard-wired facts about each C/C++ compiler, plus options read
-# from a configuration file, to provide generalized procedures to compile
+# from a lineinfos file, to provide generalized procedures to compile
 # nim files.
 
 import
   ropes, os, strutils, osproc, platform, condsyms, options, msgs,
-  configuration, std / sha1, streams
-
-#from debuginfo import writeDebugInfo
+  lineinfos, std / sha1, streams
 
 type
-  TSystemCC* = enum
-    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
-    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
   TInfoCCProp* = enum         # properties of the C compiler:
     hasSwitchRange,           # CC allows ranges in switch statements (GNU C)
     hasComputedGoto,          # CC has computed goto (GNU C extension)
@@ -336,37 +331,8 @@ const
 
   hExt* = ".h"
 
-var
-  cCompiler* = ccGcc # the used compiler
-  gMixedMode*: bool  # true if some module triggered C++ codegen
-  cIncludes*: seq[string] = @[]   # directories to search for included files
-  cLibs*: seq[string] = @[]       # directories to search for lib files
-  cLinkedLibs*: seq[string] = @[] # libraries to link
-
-# implementation
-
-proc libNameTmpl(): string {.inline.} =
-  result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
-
-type
-  CfileFlag* {.pure.} = enum
-    Cached,    ## no need to recompile this time
-    External   ## file was introduced via .compile pragma
-
-  Cfile* = object
-    cname*, obj*: string
-    flags*: set[CFileFlag]
-  CfileList = seq[Cfile]
-
-var
-  externalToLink: seq[string] = @[] # files to link in addition to the file
-                                    # we compiled
-  linkOptionsCmd: string = ""
-  compileOptionsCmd: seq[string] = @[]
-  linkOptions: string = ""
-  compileOptions: string = ""
-  ccompilerpath: string = ""
-  toCompile: CfileList = @[]
+proc libNameTmpl(conf: ConfigRef): string {.inline.} =
+  result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
 
 proc nameToCC*(name: string): TSystemCC =
   ## Returns the kind of compiler referred to by `name`, or ccNone
@@ -389,10 +355,10 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
     else:
       suffix
 
-  if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
+  if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
       optCompileOnly notin conf.globalOptions:
-    let fullCCname = platform.CPU[targetCPU].name & '.' &
-                     platform.OS[targetOS].name & '.' &
+    let fullCCname = platform.CPU[conf.target.targetCPU].name & '.' &
+                     platform.OS[conf.target.targetOS].name & '.' &
                      CC[c].name & fullSuffix
     result = getConfigVar(conf, fullCCname)
     if result.len == 0:
@@ -402,40 +368,40 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
     result = getConfigVar(conf, CC[c].name & fullSuffix)
 
 proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
-  cCompiler = nameToCC(ccname)
-  if cCompiler == ccNone:
+  conf.cCompiler = nameToCC(ccname)
+  if conf.cCompiler == ccNone:
     localError(conf, info, "unknown C compiler: '$1'" % ccname)
-  compileOptions = getConfigVar(conf, cCompiler, ".options.always")
-  linkOptions = ""
-  ccompilerpath = getConfigVar(conf, cCompiler, ".path")
+  conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
+  conf.linkOptions = ""
+  conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
   for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
-  defineSymbol(conf.symbols, CC[cCompiler].name)
+  defineSymbol(conf.symbols, CC[conf.cCompiler].name)
 
 proc addOpt(dest: var string, src: string) =
   if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
   add(dest, src)
 
 proc addLinkOption*(conf: ConfigRef; option: string) =
-  addOpt(linkOptions, option)
+  addOpt(conf.linkOptions, option)
 
 proc addCompileOption*(conf: ConfigRef; option: string) =
-  if strutils.find(compileOptions, option, 0) < 0:
-    addOpt(compileOptions, option)
+  if strutils.find(conf.compileOptions, option, 0) < 0:
+    addOpt(conf.compileOptions, option)
 
 proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
-  addOpt(linkOptionsCmd, option)
+  addOpt(conf.linkOptionsCmd, option)
 
 proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
-  compileOptionsCmd.add(option)
+  conf.compileOptionsCmd.add(option)
 
 proc initVars*(conf: ConfigRef) =
   # we need to define the symbol here, because ``CC`` may have never been set!
   for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
-  defineSymbol(conf.symbols, CC[cCompiler].name)
-  addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always"))
+  defineSymbol(conf.symbols, CC[conf.cCompiler].name)
+  addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
   #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
-  if len(ccompilerpath) == 0:
-    ccompilerpath = getConfigVar(conf, cCompiler, ".path")
+  if len(conf.ccompilerpath) == 0:
+    conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
 
 proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
   result = completeGeneratedFilePath(conf, cfile, createSubDir)
@@ -445,21 +411,21 @@ proc toObjFile*(conf: ConfigRef; filename: string): string =
   #if filename.endsWith(".cpp"):
   #  result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
   #else:
-  result = changeFileExt(filename, CC[cCompiler].objExt)
+  result = changeFileExt(filename, CC[conf.cCompiler].objExt)
 
 proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
-  toCompile.add(cf)
+  conf.toCompile.add(cf)
 
 proc resetCompilationLists*(conf: ConfigRef) =
-  toCompile.setLen 0
+  conf.toCompile.setLen 0
   ## XXX: we must associate these with their originating module
   # when the module is loaded/unloaded it adds/removes its items
   # That's because we still need to hash check the external files
   # Maybe we can do that in checkDep on the other hand?
-  externalToLink.setLen 0
+  conf.externalToLink.setLen 0
 
 proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
-  externalToLink.insert(filename, 0)
+  conf.externalToLink.insert(filename, 0)
 
 proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
   rawMessage(conf, msg, cmd)
@@ -472,9 +438,12 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
 
 proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
   let (dir, name, ext) = splitFile(projectFile)
-  writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name,
-                                     platform.OS[targetOS].scriptExt))
-  copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
+  let filename = getNimcacheDir(conf) / addFileExt("compile_" & name,
+                                     platform.OS[conf.target.targetOS].scriptExt)
+  if writeRope(script, filename):
+    copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
+  else:
+    rawMessage(conf, errGenerated, "could not write to file: " & filename)
 
 proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
   result = getConfigVar(conf, c, ".options.speed")
@@ -499,8 +468,8 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
   result = conf.globalOptions * {optGenScript, optGenMapping} != {}
 
 proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
-  result = compileOptions
-  for option in compileOptionsCmd:
+  result = conf.compileOptions
+  for option in conf.compileOptionsCmd:
     if strutils.find(result, option, 0) < 0:
       addOpt(result, option)
 
@@ -508,15 +477,15 @@ proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
   if optCDebug in conf.globalOptions:
     let key = trunk & ".debug"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getDebug(conf, cCompiler))
+    else: addOpt(result, getDebug(conf, conf.cCompiler))
   if optOptimizeSpeed in conf.options:
     let key = trunk & ".speed"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getOptSpeed(conf, cCompiler))
+    else: addOpt(result, getOptSpeed(conf, conf.cCompiler))
   elif optOptimizeSize in conf.options:
     let key = trunk & ".size"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getOptSize(conf, cCompiler))
+    else: addOpt(result, getOptSize(conf, conf.cCompiler))
   let key = trunk & ".always"
   if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
 
@@ -524,15 +493,15 @@ proc getCompileOptions(conf: ConfigRef): string =
   result = cFileSpecificOptions(conf, "__dummy__")
 
 proc getLinkOptions(conf: ConfigRef): string =
-  result = linkOptions & " " & linkOptionsCmd & " "
-  for linkedLib in items(cLinkedLibs):
-    result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
-  for libDir in items(cLibs):
-    result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
+  result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
+  for linkedLib in items(conf.cLinkedLibs):
+    result.add(CC[conf.cCompiler].linkLibCmd % linkedLib.quoteShell)
+  for libDir in items(conf.cLibs):
+    result.add(join([CC[conf.cCompiler].linkDirCmd, libDir.quoteShell]))
 
 proc needsExeExt(conf: ConfigRef): bool {.inline.} =
-  result = (optGenScript in conf.globalOptions and targetOS == osWindows) or
-           (platform.hostOS == osWindows)
+  result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
+           (conf.target.hostOS == osWindows)
 
 proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
   result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
@@ -546,18 +515,18 @@ proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string
 
 proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
   result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
-           elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
+           elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
            else: getCompilerExe(conf, compiler, "")
 
 proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
-  var c = cCompiler
+  var c = conf.cCompiler
   var options = cFileSpecificOptions(conf, cfile.cname)
   var exe = getConfigVar(conf, c, ".exe")
   if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
 
   if needsExeExt(conf): exe = addFileExt(exe, "exe")
   if optGenDynLib in conf.globalOptions and
-      ospNeedsPIC in platform.OS[targetOS].props:
+      ospNeedsPIC in platform.OS[conf.target.targetOS].props:
     add(options, ' ' & CC[c].pic)
 
   var includeCmd, compilePattern: string
@@ -565,10 +534,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
     # compute include paths:
     includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
 
-    for includeDir in items(cIncludes):
+    for includeDir in items(conf.cIncludes):
       includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
 
-    compilePattern = joinPath(ccompilerpath, exe)
+    compilePattern = joinPath(conf.ccompilerpath, exe)
   else:
     includeCmd = ""
     compilePattern = getCompilerExe(conf, c, cfile.cname)
@@ -604,9 +573,9 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
 proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
   result = secureHash(
     $secureHashFile(cfile.cname) &
-    platform.OS[targetOS].name &
-    platform.CPU[targetCPU].name &
-    extccomp.CC[extccomp.cCompiler].name &
+    platform.OS[conf.target.targetOS].name &
+    platform.CPU[conf.target.targetCPU].name &
+    extccomp.CC[conf.cCompiler].name &
     getCompileCFileCmd(conf, cfile))
 
 proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
@@ -630,7 +599,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
 proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
   if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c):
     c.flags.incl CfileFlag.Cached
-  toCompile.add(c)
+  conf.toCompile.add(c)
 
 proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
   var c = Cfile(cname: filename,
@@ -650,7 +619,7 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var
       add(prettyCmds, "CC: " & name)
     if optGenScript in conf.globalOptions:
       add(script, compileCmd)
-      add(script, tnl)
+      add(script, "\n")
 
 proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
   if optGenStaticLib in conf.globalOptions:
@@ -660,24 +629,24 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
       if not libname.isAbsolute():
         libname = getCurrentDir() / libname
     else:
-      libname = (libNameTmpl() % splitFile(conf.projectName).name)
-    result = CC[cCompiler].buildLib % ["libfile", libname,
+      libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
+    result = CC[conf.cCompiler].buildLib % ["libfile", libname,
                                        "objfiles", objfiles]
   else:
-    var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe")
-    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler)
+    var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
+    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
     # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
     if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
     if noAbsolutePaths(conf): result = linkerExe
-    else: result = joinPath(ccompilerpath, linkerExe)
-    let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui
+    else: result = joinPath(conf.cCompilerpath, linkerExe)
+    let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui
                    else: ""
     var exefile, builddll: string
     if optGenDynLib in conf.globalOptions:
-      exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
-      builddll = CC[cCompiler].buildDll
+      exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name
+      builddll = CC[conf.cCompiler].buildDll
     else:
-      exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
+      exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt
       builddll = ""
     if conf.outFile.len > 0:
       exefile = conf.outFile.expandTilde
@@ -691,10 +660,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
         writeDebugInfo(exefile.changeFileExt("ndb"))
     exefile = quoteShell(exefile)
     let linkOptions = getLinkOptions(conf) & " " &
-                      getConfigVar(conf, cCompiler, ".options.linker")
-    var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl")
+                      getConfigVar(conf, conf.cCompiler, ".options.linker")
+    var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
     if linkTmpl.len == 0:
-      linkTmpl = CC[cCompiler].linkTmpl
+      linkTmpl = CC[conf.cCompiler].linkTmpl
     result = quoteShell(result % ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
         "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
@@ -765,19 +734,20 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
   var cmds: TStringSeq = @[]
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
-    echo prettyCmds[idx]
-  compileCFile(conf, toCompile, script, cmds, prettyCmds)
+    when declared(echo):
+      echo prettyCmds[idx]
+  compileCFile(conf, conf.toCompile, script, cmds, prettyCmds)
   if optCompileOnly notin conf.globalOptions:
     execCmdsInParallel(conf, cmds, prettyCb)
   if optNoLinking notin conf.globalOptions:
     # call the linker:
     var objfiles = ""
-    for it in externalToLink:
+    for it in conf.externalToLink:
       let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
       add(objfiles, ' ')
       add(objfiles, quoteShell(
-          addFileExt(objFile, CC[cCompiler].objExt)))
-    for x in toCompile:
+          addFileExt(objFile, CC[conf.cCompiler].objExt)))
+    for x in conf.toCompile:
       let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
       add(objfiles, ' ')
       add(objfiles, quoteShell(objFile))
@@ -789,7 +759,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
     linkCmd = ""
   if optGenScript in conf.globalOptions:
     add(script, linkCmd)
-    add(script, tnl)
+    add(script, "\n")
     generateScript(conf, projectfile, script)
 
 #from json import escapeJson
@@ -824,7 +794,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
     for it in llist:
       let objfile = if noAbsolutePaths(conf): it.extractFilename
                     else: it
-      let objstr = addFileExt(objfile, CC[cCompiler].objExt)
+      let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt)
       add(objfiles, ' ')
       add(objfiles, objstr)
       if pastStart: lit ",\L"
@@ -848,11 +818,11 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
   var f: File
   if open(f, jsonFile, fmWrite):
     lit "{\"compile\":[\L"
-    cfiles(conf, f, buf, toCompile, false)
+    cfiles(conf, f, buf, conf.toCompile, false)
     lit "],\L\"link\":[\L"
     var objfiles = ""
     # XXX add every file here that is to link
-    linkfiles(conf, f, buf, objfiles, toCompile, externalToLink)
+    linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink)
 
     lit "],\L\"linkcmd\": "
     str getLinkCmd(conf, projectfile, objfiles)
@@ -877,14 +847,16 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
       add(prettyCmds, "CC: " & name)
 
     let prettyCb = proc (idx: int) =
-      echo prettyCmds[idx]
+      when declared(echo):
+        echo prettyCmds[idx]
     execCmdsInParallel(conf, cmds, prettyCb)
 
     let linkCmd = data["linkcmd"]
     doAssert linkCmd.kind == JString
     execLinkCmd(conf, linkCmd.getStr)
   except:
-    echo getCurrentException().getStackTrace()
+    when declared(echo):
+      echo getCurrentException().getStackTrace()
     quit "error evaluating JSON file: " & jsonFile
 
 proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
@@ -894,16 +866,18 @@ proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
 proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
   if optGenMapping notin conf.globalOptions: return
   var code = rope("[C_Files]\n")
-  add(code, genMappingFiles(conf, toCompile))
+  add(code, genMappingFiles(conf, conf.toCompile))
   add(code, "\n[C_Compiler]\nFlags=")
   add(code, strutils.escape(getCompileOptions(conf)))
 
   add(code, "\n[Linker]\nFlags=")
   add(code, strutils.escape(getLinkOptions(conf) & " " &
-                            getConfigVar(conf, cCompiler, ".options.linker")))
+                            getConfigVar(conf, conf.cCompiler, ".options.linker")))
 
   add(code, "\n[Environment]\nlibpath=")
   add(code, strutils.escape(conf.libpath))
 
   addf(code, "\n[Symbols]$n$1", [symbolMapping])
-  writeRope(code, joinPath(conf.projectPath, "mapping.txt"))
+  let filename = joinPath(conf.projectPath, "mapping.txt")
+  if not writeRope(code, filename):
+    rawMessage(conf, errGenerated, "could not write to file: " & filename)
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 6c16a0b4e..09455ced7 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -11,7 +11,7 @@
 
 import
   llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
-  renderer, filters
+  renderer, filters, lineinfos
 
 type
   TParseState = enum
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index 44c7651bc..44ad46136 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -9,7 +9,8 @@
 
 ## Module that implements ``gorge`` for the compiler.
 
-import msgs, std / sha1, os, osproc, streams, strutils, options
+import msgs, std / sha1, os, osproc, streams, strutils, options,
+  lineinfos
 
 proc readOutput(p: Process): (string, int) =
   result[0] = ""
@@ -22,7 +23,7 @@ proc readOutput(p: Process): (string, int) =
   result[1] = p.waitForExit
 
 proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
-  let workingDir = parentDir(info.toFullPath)
+  let workingDir = parentDir(toFullPath(conf, info))
   if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
     let h = secureHash(cmd & "\t" & input & "\t" & cache)
     let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt")
diff --git a/compiler/guards.nim b/compiler/guards.nim
index d39ea799b..99bb51fce 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -10,7 +10,7 @@
 ## This module implements the 'implies' relation for guards.
 
 import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
-  saturate, modulegraphs, options, configuration
+  saturate, modulegraphs, options, lineinfos
 
 const
   someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@@ -131,8 +131,8 @@ proc neg(n: PNode; o: Operators): PNode =
         let eAsNode = newIntNode(nkIntLit, e.sym.position)
         if not inSet(n.sons[1], eAsNode): s.add eAsNode
       result.sons[1] = s
-    elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
-      result.sons[1] = complement(n.sons[1])
+    #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
+    #  result.sons[1] = complement(n.sons[1])
     else:
       # not ({2, 3, 4}.contains(x))   x != 2 and x != 3 and x != 4
       # XXX todo
@@ -208,14 +208,14 @@ proc zero(): PNode = nkIntLit.newIntNode(0)
 proc one(): PNode = nkIntLit.newIntNode(1)
 proc minusOne(): PNode = nkIntLit.newIntNode(-1)
 
-proc lowBound*(x: PNode): PNode =
-  result = nkIntLit.newIntNode(firstOrd(x.typ))
+proc lowBound*(conf: ConfigRef; x: PNode): PNode =
+  result = nkIntLit.newIntNode(firstOrd(conf, x.typ))
   result.info = x.info
 
-proc highBound*(x: PNode; o: Operators): PNode =
+proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
   let typ = x.typ.skipTypes(abstractInst)
   result = if typ.kind == tyArray:
-             nkIntLit.newIntNode(lastOrd(typ))
+             nkIntLit.newIntNode(lastOrd(conf, typ))
            elif typ.kind == tySequence and x.kind == nkSym and
                x.sym.kind == skConst:
              nkIntLit.newIntNode(x.sym.ast.len-1)
@@ -503,7 +503,7 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
     # fact:  x <= 4;  question x in {56}?
     # --> true if every value <= 4 is in the set {56}
     #
-    var value = newIntNode(c.kind, firstOrd(x.typ))
+    var value = newIntNode(c.kind, firstOrd(nil, x.typ))
     # don't iterate too often:
     if c.intVal - value.intVal < 1000:
       var i, pos, neg: int
@@ -520,7 +520,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
     # --> true iff every value >= 4 is in the set {56}
     #
     var value = newIntNode(c.kind, c.intVal)
-    let max = lastOrd(x.typ)
+    let max = lastOrd(nil, x.typ)
     # don't iterate too often:
     if max - value.intVal < 1000:
       var i, pos, neg: int
@@ -532,8 +532,8 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
       elif neg == i: result = impNo
 
 proc compareSets(a, b: PNode): TImplication =
-  if equalSets(a, b): result = impYes
-  elif intersectSets(a, b).len == 0: result = impNo
+  if equalSets(nil, a, b): result = impYes
+  elif intersectSets(nil, a, b).len == 0: result = impNo
 
 proc impliesIn(fact, loc, aSet: PNode): TImplication =
   case fact.sons[0].sym.magic
@@ -799,10 +799,10 @@ proc ple(m: TModel; a, b: PNode): TImplication =
 
   # use type information too:  x <= 4  iff  high(x) <= 4
   if b.isValue and a.typ != nil and a.typ.isOrdinalType:
-    if lastOrd(a.typ) <= b.intVal: return impYes
+    if lastOrd(nil, a.typ) <= b.intVal: return impYes
   # 3 <= x   iff  low(x) <= 3
   if a.isValue and b.typ != nil and b.typ.isOrdinalType:
-    if firstOrd(b.typ) <= a.intVal: return impYes
+    if firstOrd(nil, b.typ) <= a.intVal: return impYes
 
   # x <= x
   if sameTree(a, b): return impYes
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 8251e3179..bbbcb4e56 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -43,8 +43,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
       if not isNil(x):
         assert x.kind in {nkStmtList, nkCall}
         # better be safe than sorry, so check evalTemplateCounter too:
-        inc(evalTemplateCounter)
-        if evalTemplateCounter > evalTemplateLimit:
+        inc(c.config.evalTemplateCounter)
+        if c.config.evalTemplateCounter > evalTemplateLimit:
           globalError(c.config, n.info, "template instantiation too nested")
         # deactivate this pattern:
         c.patterns[i] = nil
@@ -54,7 +54,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
           result = flattenStmts(x)
         else:
           result = evalPattern(c, x, result)
-        dec(evalTemplateCounter)
+        dec(c.config.evalTemplateCounter)
         # activate this pattern again:
         c.patterns[i] = pattern
 
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 34cf5bf1e..0a2f2d5cf 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -30,12 +30,14 @@ type
     wordCounter: int
     idAnon*, idDelegator*, emptyIdent*: PIdent
 
-var
-  legacy: IdentCache
+when false:
+  var
+    legacy: IdentCache
 
 proc resetIdentCache*() =
-  for i in low(legacy.buckets)..high(legacy.buckets):
-    legacy.buckets[i] = nil
+  when false:
+    for i in low(legacy.buckets)..high(legacy.buckets):
+      legacy.buckets[i] = nil
 
 proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
   if a[0] != b[0]: return 1
@@ -111,25 +113,15 @@ proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
   result = getIdent(cstring(identifier), len(identifier), h)
 
 proc newIdentCache*(): IdentCache =
-  if legacy.isNil:
-    result = IdentCache()
-    result.idAnon = result.getIdent":anonymous"
-    result.wordCounter = 1
-    result.idDelegator = result.getIdent":delegator"
-    result.emptyIdent = result.getIdent("")
-    # initialize the keywords:
-    for s in countup(succ(low(specialWords)), high(specialWords)):
-      result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
-    legacy = result
-  else:
-    result = legacy
+  result = IdentCache()
+  result.idAnon = result.getIdent":anonymous"
+  result.wordCounter = 1
+  result.idDelegator = result.getIdent":delegator"
+  result.emptyIdent = result.getIdent("")
+  # initialize the keywords:
+  for s in countup(succ(low(specialWords)), high(specialWords)):
+    result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
 
 proc whichKeyword*(id: PIdent): TSpecialWord =
   if id.id < 0: result = wInvalid
   else: result = TSpecialWord(id.id)
-
-proc getIdent*(identifier: string): PIdent =
-  ## for backwards compatibility.
-  if legacy.isNil:
-    discard newIdentCache()
-  legacy.getIdent identifier
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 90e774a50..c013b93ab 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -10,8 +10,8 @@
 # This module implements the symbol importing mechanism.
 
 import
-  intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
-  semdata, passes, renderer, modulepaths, sigmatch, configuration
+  intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
+  semdata, passes, renderer, modulepaths, sigmatch, lineinfos
 
 proc evalImport*(c: PContext, n: PNode): PNode
 proc evalFrom*(c: PContext, n: PNode): PNode
@@ -20,7 +20,7 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet =
   assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
   result = initIntSet()
   for i in 1 ..< n.len:
-    let ident = lookups.considerQuotedIdent(c.config, n[i])
+    let ident = lookups.considerQuotedIdent(c, n[i])
     result.incl(ident.id)
 
 proc importPureEnumField*(c: PContext; s: PSym) =
@@ -69,12 +69,13 @@ proc rawImportSymbol(c: PContext, s: PSym) =
     if hasPattern(s): addPattern(c, s)
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
-  let ident = lookups.considerQuotedIdent(c.config, n)
+  let ident = lookups.considerQuotedIdent(c, n)
   let s = strTableGet(fromMod.tab, ident)
   if s == nil:
     errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
-    if s.kind == skStub: loadStub(s)
+    when false:
+      if s.kind == skStub: loadStub(s)
     if s.kind notin ExportableSymKinds:
       internalError(c.config, n.info, "importSymbol: 2")
     # for an enumeration we have to add all identifiers
@@ -132,7 +133,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
     result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
                                c.config.options)
 
-proc myImportModule(c: PContext, n: PNode): PSym =
+proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
   var f = checkModuleName(c.config, n)
   if f != InvalidFileIDX:
     let L = c.graph.importStack.len
@@ -143,10 +144,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
       var err = ""
       for i in countup(recursion, L-1):
         if i > recursion: err.add "\n"
-        err.add toFullPath(c.graph.importStack[i]) & " imports " &
-                toFullPath(c.graph.importStack[i+1])
+        err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
+                toFullPath(c.config, c.graph.importStack[i+1])
       c.recursiveDep = err
-    result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache))
+    result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f))
     #echo "set back to ", L
     c.graph.importStack.setLen(L)
     # we cannot perform this check reliably because of
@@ -161,9 +162,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
       else:
         message(c.config, n.info, warnDeprecated, result.name.s)
     suggestSym(c.config, n.info, result, c.graph.usageSym, false)
+    importStmtResult.add newStrNode(toFullPath(c.config, f), n.info)
 
-proc impMod(c: PContext; it: PNode) =
-  let m = myImportModule(c, it)
+proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
+  let m = myImportModule(c, it, importStmtResult)
   if m != nil:
     var emptySet: IntSet
     # ``addDecl`` needs to be done before ``importAllSymbols``!
@@ -172,7 +174,8 @@ proc impMod(c: PContext; it: PNode) =
     #importForwarded(c, m.ast, emptySet)
 
 proc evalImport(c: PContext, n: PNode): PNode =
-  result = n
+  #result = n
+  result = newNodeI(nkImportStmt, n.info)
   for i in countup(0, sonsLen(n) - 1):
     let it = n.sons[i]
     if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
@@ -184,14 +187,14 @@ proc evalImport(c: PContext, n: PNode): PNode =
       a.add sep # dummy entry, replaced in the loop
       for x in it[2]:
         a.sons[2] = x
-        impMod(c, a)
+        impMod(c, a, result)
     else:
-      impMod(c, it)
+      impMod(c, it, result)
 
 proc evalFrom(c: PContext, n: PNode): PNode =
-  result = n
+  result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0])
+  var m = myImportModule(c, n.sons[0], result)
   if m != nil:
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
@@ -200,9 +203,9 @@ proc evalFrom(c: PContext, n: PNode): PNode =
         importSymbol(c, n.sons[i], m)
 
 proc evalImportExcept*(c: PContext, n: PNode): PNode =
-  result = n
+  result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0])
+  var m = myImportModule(c, n.sons[0], result)
   if m != nil:
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
diff --git a/compiler/incremental.nim b/compiler/incremental.nim
new file mode 100644
index 000000000..378ba0665
--- /dev/null
+++ b/compiler/incremental.nim
@@ -0,0 +1,188 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Basic type definitions the module graph needs in order to support
+## incremental compilations.
+
+const nimIncremental* = defined(nimIncremental)
+
+import options, lineinfos
+
+when nimIncremental:
+  import ast, msgs, intsets, btrees, db_sqlite, std / sha1
+  from strutils import parseInt
+
+  type
+    Writer* = object
+      sstack*: seq[PSym]          # a stack of symbols to process
+      tstack*: seq[PType]         # a stack of types to process
+      tmarks*, smarks*: IntSet
+      forwardedSyms*: seq[PSym]
+
+    Reader* = object
+      syms*: BTree[int, PSym]
+      types*: BTree[int, PType]
+
+    IncrementalCtx* = object
+      db*: DbConn
+      w*: Writer
+      r*: Reader
+
+  proc init*(incr: var IncrementalCtx) =
+    incr.w.sstack = @[]
+    incr.w.tstack = @[]
+    incr.w.tmarks = initIntSet()
+    incr.w.smarks = initIntSet()
+    incr.w.forwardedSyms = @[]
+    incr.r.syms = initBTree[int, PSym]()
+    incr.r.types = initBTree[int, PType]()
+
+
+  proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: string): string =
+    result = msgs.getHash(conf, fileIdx)
+    if result.len == 0:
+      result = $secureHashFile(fullpath)
+      msgs.setHash(conf, fileIdx, result)
+
+  proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int =
+    if fileIdx == FileIndex(-1): return -1
+    let fullpath = toFullPath(conf, fileIdx)
+    let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
+      fullpath)
+    let id = row[0]
+    let fullhash = hashFileCached(conf, fileIdx, fullpath)
+    if id.len == 0:
+      result = int incr.db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
+        fullpath, fullhash)
+    else:
+      if row[1] != fullhash:
+        incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
+      result = parseInt(id)
+
+  proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex =
+    if dbId == -1: return FileIndex(-1)
+    let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId)
+    doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
+    result = fileInfoIdx(conf, fullpath)
+
+
+  proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
+                     module, fileIdx: FileIndex;
+                     isIncludeFile: bool) =
+    if conf.symbolFiles != v2Sf: return
+
+    let a = toDbFileId(incr, conf, module)
+    let b = toDbFileId(incr, conf, fileIdx)
+
+    incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
+      a, b, ord(isIncludeFile))
+
+  # --------------- Database model ---------------------------------------------
+
+  proc createDb*(db: DbConn) =
+    db.exec(sql"""
+      create table if not exists controlblock(
+        idgen integer not null
+      );
+    """)
+
+    db.exec(sql"""
+      create table if not exists filenames(
+        id integer primary key,
+        fullpath varchar(8000) not null,
+        fullHash varchar(256) not null
+      );
+    """)
+    db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
+
+    db.exec(sql"""
+      create table if not exists modules(
+        id integer primary key,
+        fullpath varchar(8000) not null,
+        interfHash varchar(256) not null,
+        fullHash varchar(256) not null,
+
+        created timestamp not null default (DATETIME('now'))
+      );""")
+    db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
+
+    db.exec(sql"""
+      create table if not exists deps(
+        id integer primary key,
+        module integer not null,
+        dependency integer not null,
+        isIncludeFile integer not null,
+        foreign key (module) references filenames(id),
+        foreign key (dependency) references filenames(id)
+      );""")
+    db.exec(sql"""create index if not exists DepsIx on deps(module);""")
+
+    db.exec(sql"""
+      create table if not exists types(
+        id integer primary key,
+        nimid integer not null,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index TypeByModuleIdx on types(module);"
+    db.exec sql"create index TypeByNimIdIdx on types(nimid);"
+
+    db.exec(sql"""
+      create table if not exists syms(
+        id integer primary key,
+        nimid integer not null,
+        module integer not null,
+        name varchar(256) not null,
+        data blob not null,
+        exported int not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index if not exists SymNameIx on syms(name);"
+    db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
+    db.exec sql"create index SymByModuleIdx on syms(module);"
+    db.exec sql"create index SymByNimIdIdx on syms(nimid);"
+
+
+    db.exec(sql"""
+      create table if not exists toplevelstmts(
+        id integer primary key,
+        position integer not null,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
+    db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
+
+    db.exec(sql"""
+      create table if not exists statics(
+        id integer primary key,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
+    db.exec sql"insert into controlblock(idgen) values (0)"
+
+
+else:
+  type
+    IncrementalCtx* = object
+
+  template init*(incr: IncrementalCtx) = discard
+
+  template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
+                     module, fileIdx: FileIndex;
+                     isIncludeFile: bool) =
+    discard
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 25b554f7b..ef54841ae 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -31,8 +31,8 @@ implements the required case distinction.
 import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
-  times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
-  intsets, cgmeth, lowerings, sighashes, configuration
+  times, ropes, math, passes, ccgutils, wordrecg, renderer,
+  intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils
 
 from modulegraphs import ModuleGraph
 
@@ -72,7 +72,7 @@ type
                              # has been used (i.e. the label should be emitted)
     isLoop: bool             # whether it's a 'block' or 'while'
 
-  TGlobals = object
+  PGlobals = ref object of RootObj
     typeInfo, constants, code: Rope
     forwarded: seq[PSym]
     generatedSyms: IntSet
@@ -80,7 +80,6 @@ type
     classes: seq[(PType, Rope)]
     unique: int    # for temp identifier generation
 
-  PGlobals = ref TGlobals
   PProc = ref TProc
   TProc = object
     procDef: PNode
@@ -537,7 +536,7 @@ proc genLineDir(p: PProc, n: PNode) =
   let line = toLinenumber(n.info)
   if optLineDir in p.options:
     lineF(p, "// line $2 \"$1\"$n",
-         [rope(toFilename(n.info)), rope(line)])
+         [rope(toFilename(p.config, n.info)), rope(line)])
   if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
       ((p.prc == nil) or sfPure notin p.prc.flags):
     useMagic(p, "endb")
@@ -606,11 +605,11 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   var length = sonsLen(n)
   var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
   if catchBranchesExist:
-    add(p.body, "++excHandler;" & tnl)
+    add(p.body, "++excHandler;\L")
   var tmpFramePtr = rope"F"
   if optStackTrace notin p.options:
     tmpFramePtr = p.getTemp(true)
-    line(p, tmpFramePtr & " = framePtr;" & tnl)
+    line(p, tmpFramePtr & " = framePtr;\L")
   lineF(p, "try {$n", [])
   var a: TCompRes
   gen(p, n.sons[0], a)
@@ -648,15 +647,15 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   if catchBranchesExist:
     if not generalCatchBranchExists:
       useMagic(p, "reraiseException")
-      line(p, "else {" & tnl)
-      line(p, "\treraiseException();" & tnl)
-      line(p, "}" & tnl)
+      line(p, "else {\L")
+      line(p, "\treraiseException();\L")
+      line(p, "}\L")
     addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
-  line(p, "} finally {" & tnl)
+  line(p, "} finally {\L")
   line(p, "framePtr = $1;$n" % [tmpFramePtr])
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
-  line(p, "}" & tnl)
+  line(p, "}\L")
 
 proc genRaiseStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -669,7 +668,7 @@ proc genRaiseStmt(p: PProc, n: PNode) =
              [a.rdLoc, makeJSString(typ.sym.name.s)])
   else:
     useMagic(p, "reraiseException")
-    line(p, "reraiseException();" & tnl)
+    line(p, "reraiseException();\L")
 
 proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   var
@@ -775,7 +774,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
       var r: TCompRes
       gen(p, it, r)
       p.body.add(r.rdLoc)
-  p.body.add tnl
+  p.body.add "\L"
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
   var cond, stmt: TCompRes
@@ -798,7 +797,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
       p.nested: gen(p, it.sons[0], stmt)
     moveInto(p, stmt, r)
     lineF(p, "}$n", [])
-  line(p, repeat('}', toClose) & tnl)
+  line(p, repeat('}', toClose) & "\L")
 
 proc generateHeader(p: PProc, typ: PType): Rope =
   result = nil
@@ -969,7 +968,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
   r.address = a.res
   var typ = skipTypes(m.sons[0].typ, abstractPtrs)
-  if typ.kind == tyArray: first = firstOrd(typ.sons[0])
+  if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0])
   else: first = 0
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
@@ -1366,7 +1365,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   of tyBool:
     result = putToSeq("false", indirect)
   of tyArray:
-    let length = int(lengthOrd(t))
+    let length = int(lengthOrd(p.config, t))
     let e = elemType(t)
     let jsTyp = arrayTypeForElemType(e)
     if not jsTyp.isNil:
@@ -1680,7 +1679,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
-  of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
+  of mSizeOf: r.res = rope(getSize(p.config, n.sons[1].typ))
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
   of mLengthStr:
@@ -1901,13 +1900,13 @@ proc frameCreate(p: PProc; procname, filename: Rope): Rope =
   result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
 
 proc frameDestroy(p: PProc): Rope =
-  result = p.indentLine rope(("framePtr = F.prev;") & tnl)
+  result = p.indentLine rope(("framePtr = F.prev;") & "\L")
 
 proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
     result = frameCreate(p,
               makeJSString(prc.owner.name.s & '.' & prc.name.s),
-              makeJSString(toFilename(prc.info)))
+              makeJSString(toFilename(p.config, prc.info)))
   else:
     result = nil
   if p.beforeRetNeeded:
@@ -1926,7 +1925,7 @@ proc optionaLine(p: Rope): Rope =
   if p == nil:
     return nil
   else:
-    return p & tnl
+    return p & "\L"
 
 proc genProc(oldProc: PProc, prc: PSym): Rope =
   var
@@ -1968,7 +1967,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
               optionaLine(genProcBody(p, prc)),
               optionaLine(p.indentLine(returnStmt))]
   else:
-    result = ~tnl
+    result = ~"\L"
 
     if optHotCodeReloading in p.config.options:
       # Here, we introduce thunks that create the equivalent of a jump table
@@ -2156,7 +2155,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
@@ -2170,14 +2169,14 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     discard "XXX to implement for better stack traces"
   else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
 
-var globals: PGlobals # XXX global variable here
-
-proc newModule(module: PSym): BModule =
+proc newModule(g: ModuleGraph; module: PSym): BModule =
   new(result)
   result.module = module
   result.sigConflicts = initCountTable[SigHash]()
-  if globals == nil:
-    globals = newGlobals()
+  if g.backend == nil:
+    g.backend = newGlobals()
+  result.graph = g
+  result.config = g.config
 
 proc genHeader(): Rope =
   result = (
@@ -2200,7 +2199,7 @@ proc genModule(p: PProc, n: PNode) =
   if optStackTrace in p.options:
     add(p.body, frameCreate(p,
         makeJSString("module " & p.module.module.name.s),
-        makeJSString(toFilename(p.module.module.info))))
+        makeJSString(toFilename(p.config, p.module.module.info))))
   genStmt(p, n)
   if optStackTrace in p.options:
     add(p.body, frameDestroy(p))
@@ -2210,6 +2209,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   let m = BModule(b)
   if passes.skipCodegen(m.config, n): return n
   if m.module == nil: internalError(m.config, n.info, "myProcess")
+  let globals = PGlobals(m.graph.backend)
   var p = newProc(globals, m, nil, m.module.options)
   p.unique = globals.unique
   genModule(p, n)
@@ -2217,6 +2217,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   add(p.g.code, p.body)
 
 proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
+  let globals = PGlobals(graph.backend)
   for prc in globals.forwarded:
     if not globals.generatedSyms.containsOrIncl(prc.id):
       var p = newProc(globals, m, nil, m.module.options)
@@ -2261,8 +2262,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   var m = BModule(b)
   if passes.skipCodegen(m.config, n): return n
   if sfMainModule in m.module.flags:
+    let globals = PGlobals(graph.backend)
     let ext = "js"
-    let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position)
+    let f = if globals.classes.len == 0: toFilename(m.config, FileIndex m.module.position)
             else: "nimsystem"
     let code = wholeCode(graph, m)
     let outfile =
@@ -2275,15 +2277,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
     for obj, content in items(globals.classes):
       genClass(m.config, obj, content, ext)
 
-proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext =
-  internalError(graph.config, "symbol files are not possible with the JS code generator")
-  result = nil
-
-proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
-  var r = newModule(s)
-  r.graph = graph
-  r.config = graph.config
-  result = r
+proc myOpen(graph: ModuleGraph; s: PSym): PPassContext =
+  result = newModule(graph, s)
 
-const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
+const JSgenPass* = makePass(myOpen, myProcess, myClose)
 
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index f9e4246eb..d86b09a03 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -25,7 +25,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
     else:
       s = nil
       for i in countup(0, length - 1):
-        if i > 0: add(s, ", " & tnl)
+        if i > 0: add(s, ", \L")
         add(s, genObjectFields(p, typ, n.sons[i]))
       result = ("{kind: 2, len: $1, offset: 0, " &
           "typ: null, name: null, sons: [$2]}") % [rope(length), s]
@@ -56,15 +56,15 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
           else:
             add(u, rope(getOrdValue(b.sons[j])))
       of nkElse:
-        u = rope(lengthOrd(field.typ))
+        u = rope(lengthOrd(p.config, field.typ))
       else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
-      if result != nil: add(result, ", " & tnl)
+      if result != nil: add(result, ", \L")
       addf(result, "[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
     result = ("{kind: 3, offset: \"$1\", len: $3, " &
         "typ: $2, name: $4, sons: [$5]}") % [
         mangleName(p.module, field), s,
-        rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
+        rope(lengthOrd(p.config, field.typ)), makeJSString(field.name.s), result]
   else: internalError(p.config, n.info, "genObjectFields")
 
 proc objHasTypeField(t: PType): bool {.inline.} =
@@ -85,7 +85,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
 proc genTupleFields(p: PProc, typ: PType): Rope =
   var s: Rope = nil
   for i in 0 ..< typ.len:
-    if i > 0: add(s, ", " & tnl)
+    if i > 0: add(s, ", \L")
     s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
            "typ: $2, name: \"Field$1\", sons: null}",
            [i.rope, genTypeInfo(p, typ.sons[i])])
@@ -106,7 +106,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
   for i in countup(0, length - 1):
     if (typ.n.sons[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
     let field = typ.n.sons[i].sym
-    if i > 0: add(s, ", " & tnl)
+    if i > 0: add(s, ", \L")
     let extName = if field.ast == nil: field.name.s else: field.ast.strVal
     addf(s, "\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
          [rope(field.position), name, makeJSString(extName)])
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 773e5e29c..6fb273164 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -11,8 +11,8 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
-  idents, renderer, types, magicsys, rodread, lowerings, tables,
-  modulegraphs
+  idents, renderer, types, magicsys, lowerings, tables,
+  modulegraphs, lineinfos
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -137,7 +137,7 @@ proc createStateType(g: ModuleGraph; iter: PSym): PType =
   rawAddSon(result, intType)
 
 proc createStateField(g: ModuleGraph; iter: PSym): PSym =
-  result = newSym(skField, getIdent(":state"), iter, iter.info, {})
+  result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info, {})
   result.typ = createStateType(g, iter)
 
 proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
@@ -146,12 +146,12 @@ proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
   result = createObj(g, owner, info, final=false)
   rawAddField(result, createStateField(g, owner))
 
-proc getIterResult(iter: PSym): PSym =
+proc getIterResult(iter: PSym; cache: IdentCache): PSym =
   if resultPos < iter.ast.len:
     result = iter.ast.sons[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent":result", iter, iter.info, {})
+    result = newSym(skResult, getIdent(cache, ":result"), iter, iter.info, {})
     result.typ = iter.typ.sons[0]
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
@@ -245,7 +245,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   var env: PNode
   if owner.isIterator:
     let it = getHiddenParam(g, owner)
-    addUniqueField(it.typ.sons[0], hp)
+    addUniqueField(it.typ.sons[0], hp, g.cache)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
     let e = newSym(skLet, iter.name, owner, n.info)
@@ -262,7 +262,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
 proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.lastSon
-  addField(obj, s)
+  addField(obj, s, g.cache)
 
   var access = newSymNode(envParam)
   assert obj.kind == tyObject
@@ -279,7 +279,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
   if illegalCapture(s):
     localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
-      [s.name.s, typeToString(s.typ), $s.info])
+      [s.name.s, typeToString(s.typ), g.config$s.info])
   elif owner.typ.callConv notin {ccClosure, ccDefault}:
     localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
       [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
@@ -326,7 +326,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   if refObj == fieldType:
     localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
 
-  let upIdent = getIdent(upName)
+  let upIdent = getIdent(c.graph.cache, upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
     if upField.typ != fieldType:
@@ -367,7 +367,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
   let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
   let t = c.getEnvTypeForOwner(owner, info)
   if cp == nil:
-    cp = newSym(skParam, getIdent(paramName), fn, fn.info)
+    cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = t
     addHiddenParam(fn, cp)
@@ -400,7 +400,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
           if not c.capturedVars.containsOrIncl(s.id):
             let obj = getHiddenParam(c.graph, owner).typ.lastSon
             #let obj = c.getEnvTypeForOwner(s.owner).lastSon
-            addField(obj, s)
+            addField(obj, s, c.graph.cache)
       # but always return because the rest of the proc is only relevant when
       # ow != owner:
       return
@@ -425,7 +425,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
         let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
         #getHiddenParam(owner).typ.lastSon
-        addField(obj, s)
+        addField(obj, s, c.graph.cache)
       # create required upFields:
       var w = owner.skipGenericOwner
       if isInnerProc(w) or owner.isIterator:
@@ -482,14 +482,14 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
       let field = getFieldFromObj(obj, s)
       if field != nil:
         return rawIndirectAccess(access, field, n.info)
-      let upField = lookupInRecord(obj.n, getIdent(upName))
+      let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
   localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
-proc newEnvVar(owner: PSym; typ: PType): PNode =
-  var v = newSym(skVar, getIdent(envName), owner, owner.info)
+proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode =
+  var v = newSym(skVar, getIdent(cache, envName), owner, owner.info)
   incl(v.flags, sfShadowed)
   v.typ = typ
   result = newSymNode(v)
@@ -510,14 +510,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
       localError d.graph.config, owner.info, "internal error: could not determine closure type"
-    result = newEnvVar(owner, envVarType)
+    result = newEnvVar(d.graph.cache, owner, envVarType)
     c.envVars[owner.id] = result
 
 proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
   let p = getHiddenParam(g, owner)
   result = p.newSymNode
   if owner.isIterator:
-    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
+    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName))
     if upField == nil:
       localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
@@ -546,7 +546,7 @@ proc rawClosureCreation(owner: PSym;
         # add ``env.param = param``
         result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
 
-  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
+  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let up = getUpViaParam(d.graph, owner)
     if up != nil and upField.typ == up.typ:
@@ -562,13 +562,13 @@ proc closureCreationForIter(iter: PNode;
                             d: DetectionPass; c: var LiftingPass): PNode =
   result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
   let owner = iter.sym.skipGenericOwner
-  var v = newSym(skVar, getIdent(envName), owner, iter.info)
+  var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
   incl(v.flags, sfShadowed)
   v.typ = getHiddenParam(d.graph, iter.sym).typ
   var vnode: PNode
   if owner.isIterator:
     let it = getHiddenParam(d.graph, owner)
-    addUniqueField(it.typ.sons[0], v)
+    addUniqueField(it.typ.sons[0], v, d.graph.cache)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
     vnode = v.newSymNode
@@ -577,7 +577,7 @@ proc closureCreationForIter(iter: PNode;
     result.add(vs)
   result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
 
-  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
+  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let u = setupEnvVar(owner, d, c)
     if u.typ == upField.typ:
@@ -625,11 +625,11 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
   if n.sons[0].kind != nkEmpty:
     var a = newNodeI(nkAsgn, n.sons[0].info)
     var retVal = liftCapturedVars(n.sons[0], owner, d, c)
-    addSon(a, newSymNode(getIterResult(owner)))
+    addSon(a, newSymNode(getIterResult(owner, d.graph.cache)))
     addSon(a, retVal)
     retStmt.add(a)
   else:
-    retStmt.add(emptyNode)
+    retStmt.add(newNodeI(nkEmpty, n.info))
 
   var stateLabelStmt = newNodeI(nkState, n.info)
   stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
@@ -700,7 +700,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
       if access.typ == wanted:
         return makeClosure(d.graph, s, access, n.info)
       let obj = access.typ.sons[0]
-      let upField = lookupInRecord(obj.n, getIdent(upName))
+      let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
       if upField == nil:
         localError(d.graph.config, n.info, "internal error: no environment found")
         return n
@@ -923,7 +923,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
       body[i].sym.kind = skLet
     addSon(vpart, body[i])
 
-  addSon(vpart, ast.emptyNode) # no explicit type
+  addSon(vpart, newNodeI(nkEmpty, body.info)) # no explicit type
   if not env.isNil:
     call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
   addSon(vpart, call)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 591561987..6e5506b09 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -17,7 +17,7 @@
 
 import
   hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg, configuration
+  wordrecg, lineinfos
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -273,10 +273,10 @@ template tokenBegin(tok, pos) {.dirty.} =
 template tokenEnd(tok, pos) {.dirty.} =
   when defined(nimsuggest):
     let colB = getColNumber(L, pos)+1
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.InToken
-      gTrackPos.col = colA.int16
+      L.config.m.trackPos.col = colA.int16
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -284,10 +284,10 @@ template tokenEnd(tok, pos) {.dirty.} =
 template tokenEndIgnore(tok, pos) =
   when defined(nimsuggest):
     let colB = getColNumber(L, pos)
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
-      gTrackPos.fileIndex = trackPosInvalidFileIdx
-      gTrackPos.line = 0'u16
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+      L.config.m.trackPos.fileIndex = trackPosInvalidFileIdx
+      L.config.m.trackPos.line = 0'u16
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -298,11 +298,11 @@ template tokenEndPrevious(tok, pos) =
     # to the token that came before that, but only if we haven't detected
     # the cursor in a string literal or comment:
     let colB = getColNumber(L, pos)
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.BeforeToken
-      gTrackPos = L.previousToken
-      gTrackPosAttached = true
+      L.config.m.trackPos = L.previousToken
+      L.config.m.trackPosAttached = true
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -630,14 +630,14 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
     if L.config.oldNewlines:
       if tok.tokType == tkCharLit:
         lexMessage(L, errGenerated, "\\n not allowed in character literal")
-      add(tok.literal, tnl)
+      add(tok.literal, L.config.target.tnl)
     else:
       add(tok.literal, '\L')
     inc(L.bufpos)
   of 'p', 'P':
     if tok.tokType == tkCharLit:
       lexMessage(L, errGenerated, "\\p not allowed in character literal")
-    add(tok.literal, tnl)
+    add(tok.literal, L.config.target.tnl)
     inc(L.bufpos)
   of 'r', 'R', 'c', 'C':
     add(tok.literal, CR)
@@ -714,11 +714,6 @@ proc handleCRLF(L: var TLexer, pos: int): int =
     if col > MaxLineLength:
       lexMessagePos(L, hintLineTooLong, pos)
 
-    if optEmbedOrigSrc in L.config.globalOptions:
-      let lineStart = cast[ByteAddress](L.buf) + L.lineStart
-      let line = newString(cast[cstring](lineStart), col)
-      addSourceLine(L.fileIdx, line)
-
   case L.buf[pos]
   of CR:
     registerLine()
@@ -1121,9 +1116,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       else:
         tok.tokType = tkParLe
         when defined(nimsuggest):
-          if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
-                    tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon:
-            gTrackPos.col = tok.col.int16
+          if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col < L.config.m.trackPos.col and
+                    tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideCon:
+            L.config.m.trackPos.col = tok.col.int16
     of ')':
       tok.tokType = tkParRi
       inc(L.bufpos)
@@ -1142,11 +1137,11 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       inc(L.bufpos)
     of '.':
       when defined(nimsuggest):
-        if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
-            tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug:
+        if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and
+            tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug:
           tok.tokType = tkDot
           L.cursor = CursorPosition.InToken
-          gTrackPos.col = tok.col.int16
+          L.config.m.trackPos.col = tok.col.int16
           inc(L.bufpos)
           atTokenEnd()
           return
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 4603d357b..ae789cd88 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, strutils, options, ast, astalgo, msgs,
-  idents, renderer, types, lowerings
+  idents, renderer, types, lowerings, lineinfos
 
 from pragmas import getPragmaVal
 from wordrecg import wLiftLocals
@@ -20,13 +20,14 @@ type
   Ctx = object
     partialParam: PSym
     objType: PType
+    cache: IdentCache
 
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and
     sfGlobal notin s.flags
 
 proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
-  let field = addUniqueField(c.objType, s)
+  let field = addUniqueField(c.objType, s, c.cache)
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = c.objType
   add(deref, newSymNode(c.partialParam, info))
@@ -52,7 +53,7 @@ proc lookupParam(params, dest: PNode): PSym =
     if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
       return params[i].sym
 
-proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef): PNode =
   let liftDest = getPragmaVal(prc.ast, wLiftLocals)
   if liftDest == nil: return n
   let partialParam = lookupParam(prc.typ.n, liftDest)
@@ -64,7 +65,7 @@ proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
   if objType.kind != tyObject or tfPartial notin objType.flags:
     localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
     return n
-  var c = Ctx(partialParam: partialParam, objType: objType)
+  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache)
   let w = newTree(nkStmtList, n)
   liftLocals(w, 0, c)
   result = w[0]
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
new file mode 100644
index 000000000..cad1fe6aa
--- /dev/null
+++ b/compiler/lineinfos.nim
@@ -0,0 +1,267 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains the ``TMsgKind`` enum as well as the
+## ``TLineInfo`` object.
+
+import ropes, tables
+
+const
+  explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
+
+type
+  TMsgKind* = enum
+    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
+    errXExpected,
+    errGridTableNotImplemented,
+    errGeneralParseError,
+    errNewSectionExpected,
+    errInvalidDirectiveX,
+    errGenerated,
+    errUser,
+    warnCannotOpenFile,
+    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
+    warnDeprecated, warnConfigDeprecated,
+    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
+    warnUnknownSubstitutionX, warnLanguageXNotSupported,
+    warnFieldXNotSupported, warnCommentXIgnored,
+    warnTypelessParam,
+    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
+    warnEachIdentIsTuple, warnShadowIdent,
+    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
+    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
+    warnInconsistentSpacing, warnUser,
+    hintSuccess, hintSuccessX,
+    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
+    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
+    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
+    hintConditionAlwaysTrue, hintName, hintPattern,
+    hintExecuting, hintLinking, hintDependency,
+    hintSource, hintPerformance, hintStackTrace, hintGCStats,
+    hintGlobalVar,
+    hintUser, hintUserRaw
+
+const
+  MsgKindToStr*: array[TMsgKind, string] = [
+    errUnknown: "unknown error",
+    errInternal: "internal error: $1",
+    errIllFormedAstX: "illformed AST: $1",
+    errCannotOpenFile: "cannot open '$1'",
+    errXExpected: "'$1' expected",
+    errGridTableNotImplemented: "grid table is not implemented",
+    errGeneralParseError: "general parse error",
+    errNewSectionExpected: "new section expected",
+    errInvalidDirectiveX: "invalid directive: '$1'",
+    errGenerated: "$1",
+    errUser: "$1",
+    warnCannotOpenFile: "cannot open '$1'",
+    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
+    warnXIsNeverRead: "'$1' is never read",
+    warnXmightNotBeenInit: "'$1' might not have been initialized",
+    warnDeprecated: "$1 is deprecated",
+    warnConfigDeprecated: "config file '$1' is deprecated",
+    warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
+    warnUnknownMagic: "unknown magic '$1' might crash the compiler",
+    warnRedefinitionOfLabel: "redefinition of label '$1'",
+    warnUnknownSubstitutionX: "unknown substitution '$1'",
+    warnLanguageXNotSupported: "language '$1' not supported",
+    warnFieldXNotSupported: "field '$1' not supported",
+    warnCommentXIgnored: "comment '$1' ignored",
+    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+    warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
+    warnWriteToForeignHeap: "write to foreign heap",
+    warnUnsafeCode: "unsafe code: '$1'",
+    warnEachIdentIsTuple: "each identifier is a tuple",
+    warnShadowIdent: "shadowed identifier: '$1'",
+    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
+    warnProveField: "cannot prove that field '$1' is accessible",
+    warnProveIndex: "cannot prove index '$1' is valid",
+    warnGcUnsafe: "not GC-safe: '$1'",
+    warnGcUnsafe2: "$1",
+    warnUninit: "'$1' might not have been initialized",
+    warnGcMem: "'$1' uses GC'ed memory",
+    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
+    warnLockLevel: "$1",
+    warnResultShadowed: "Special variable 'result' is shadowed.",
+    warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
+    warnUser: "$1",
+    hintSuccess: "operation successful",
+    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+    hintLineTooLong: "line too long",
+    hintXDeclaredButNotUsed: "'$1' is declared but not used",
+    hintConvToBaseNotNeeded: "conversion to base object is not needed",
+    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
+    hintExprAlwaysX: "expression evaluates always to '$1'",
+    hintQuitCalled: "quit() called",
+    hintProcessing: "$1",
+    hintCodeBegin: "generated code listing:",
+    hintCodeEnd: "end of listing",
+    hintConf: "used config file '$1'",
+    hintPath: "added path: '$1'",
+    hintConditionAlwaysTrue: "condition is always true: '$1'",
+    hintName: "name should be: '$1'",
+    hintPattern: "$1",
+    hintExecuting: "$1",
+    hintLinking: "",
+    hintDependency: "$1",
+    hintSource: "$1",
+    hintPerformance: "$1",
+    hintStackTrace: "$1",
+    hintGCStats: "$1",
+    hintGlobalVar: "global variable declared here",
+    hintUser: "$1",
+    hintUserRaw: "$1"]
+
+const
+  WarningsToStr* = ["CannotOpenFile", "OctalEscape",
+    "XIsNeverRead", "XmightNotBeenInit",
+    "Deprecated", "ConfigDeprecated",
+    "SmallLshouldNotBeUsed", "UnknownMagic",
+    "RedefinitionOfLabel", "UnknownSubstitutionX",
+    "LanguageXNotSupported", "FieldXNotSupported",
+    "CommentXIgnored",
+    "TypelessParam", "UseBase", "WriteToForeignHeap",
+    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
+    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
+    "GcMem", "Destructor", "LockLevel", "ResultShadowed",
+    "Spacing", "User"]
+
+  HintsToStr* = ["Success", "SuccessX", "LineTooLong",
+    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
+    "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
+    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
+    "Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
+    "User", "UserRaw"]
+
+const
+  fatalMin* = errUnknown
+  fatalMax* = errInternal
+  errMin* = errUnknown
+  errMax* = errUser
+  warnMin* = warnCannotOpenFile
+  warnMax* = pred(hintSuccess)
+  hintMin* = hintSuccess
+  hintMax* = high(TMsgKind)
+
+static:
+  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
+  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
+
+type
+  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
+  TNoteKinds* = set[TNoteKind]
+
+const
+  NotesVerbosity*: array[0..3, TNoteKinds] = [
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintSuccessX, hintPath, hintConf,
+                                         hintProcessing, hintPattern,
+                                         hintDependency,
+                                         hintExecuting, hintLinking,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGlobalVar, hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintPath,
+                                         hintDependency,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGlobalVar, hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
+    {low(TNoteKind)..high(TNoteKind)}]
+
+const
+  errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
+  errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
+
+type
+  TFileInfo* = object
+    fullPath*: string          # This is a canonical full filesystem path
+    projPath*: string          # This is relative to the project's root
+    shortName*: string         # short name of the module
+    quotedName*: Rope          # cached quoted short name for codegen
+                               # purposes
+    quotedFullName*: Rope      # cached quoted full name for codegen
+                               # purposes
+
+    lines*: seq[string]        # the source code of the module
+                               #   used for better error messages and
+                               #   embedding the original source in the
+                               #   generated code
+    dirtyfile*: string         # the file that is actually read into memory
+                               # and parsed; usually "" but is used
+                               # for 'nimsuggest'
+    hash*: string              # the checksum of the file
+    dirty*: bool               # for 'nimfix' / 'nimpretty' like tooling
+    when defined(nimpretty):
+      fullContent*: string
+  FileIndex* = distinct int32
+  TLineInfo* = object          # This is designed to be as small as possible,
+                               # because it is used
+                               # in syntax nodes. We save space here by using
+                               # two int16 and an int32.
+                               # On 64 bit and on 32 bit systems this is
+                               # only 8 bytes.
+    line*: uint16
+    col*: int16
+    fileIndex*: FileIndex
+    when defined(nimpretty):
+      offsetA*, offsetB*: int
+      commentOffsetA*, commentOffsetB*: int
+
+  TErrorOutput* = enum
+    eStdOut
+    eStdErr
+
+  TErrorOutputs* = set[TErrorOutput]
+
+  ERecoverableError* = object of ValueError
+  ESuggestDone* = object of Exception
+
+proc `==`*(a, b: FileIndex): bool {.borrow.}
+
+const
+  InvalidFileIDX* = FileIndex(-1)
+
+proc unknownLineInfo*(): TLineInfo =
+  result.line = uint16(0)
+  result.col = int16(-1)
+  result.fileIndex = InvalidFileIDX
+
+type
+  Severity* {.pure.} = enum ## VS Code only supports these three
+    Hint, Warning, Error
+
+const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
+                                   # are produced within comments and string literals
+
+type
+  MsgConfig* = object ## does not need to be stored in the incremental cache
+    trackPos*: TLineInfo
+    trackPosAttached*: bool ## whether the tracking position was attached to
+                            ## some close token.
+
+    errorOutputs*: TErrorOutputs
+    msgContext*: seq[TLineInfo]
+    lastError*: TLineInfo
+    filenameToIndexTbl*: Table[string, FileIndex]
+    fileInfos*: seq[TFileInfo]
+    systemFileIdx*: FileIndex
+
+
+proc initMsgConfig*(): MsgConfig =
+  result.msgContext = @[]
+  result.lastError = unknownLineInfo()
+  result.filenameToIndexTbl = initTable[string, FileIndex]()
+  result.fileInfos = @[]
+  result.errorOutputs = {eStdOut, eStdErr}
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index b6d63d0bd..87694988a 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -10,8 +10,8 @@
 # This module implements lookup helpers.
 
 import
-  intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
-  renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils
+  intsets, ast, astalgo, idents, semdata, types, msgs, options,
+  renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -22,13 +22,13 @@ proc noidentError(conf: ConfigRef; n, origin: PNode) =
   m.add "identifier expected, but found '" & n.renderTree & "'"
   localError(conf, n.info, m)
 
-proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIdent =
+proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
   ## Retrieve a PIdent from a PNode, taking into account accent nodes.
   ## ``origin`` can be nil. If it is not nil, it is used for a better
   ## error message.
   template handleError(n, origin: PNode) =
-    noidentError(conf, n, origin)
-    result = getIdent"<Error>"
+    noidentError(c.config, n, origin)
+    result = getIdent(c.cache, "<Error>")
 
   case n.kind
   of nkIdent: result = n.ident
@@ -36,7 +36,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden
   of nkAccQuoted:
     case n.len
     of 0: handleError(n, origin)
-    of 1: result = considerQuotedIdent(conf, n.sons[0], origin)
+    of 1: result = considerQuotedIdent(c, n.sons[0], origin)
     else:
       var id = ""
       for i in 0..<n.len:
@@ -46,7 +46,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden
         of nkSym: id.add(x.sym.name.s)
         of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
         else: handleError(n, origin)
-      result = getIdent(id)
+      result = getIdent(c.cache, id)
   of nkOpenSymChoice, nkClosedSymChoice:
     if n[0].kind == nkSym:
       result = n.sons[0].sym.name
@@ -86,7 +86,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
   else:
     result = s.owner
     if conf.cmd == cmdPretty:
-      prettybase.replaceDeprecated(n.info, s, result)
+      prettybase.replaceDeprecated(conf, n.info, s, result)
     else:
       message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
               s.name.s)
@@ -100,15 +100,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym =
     if result != nil: return
   result = nil
 
-proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
-  var i = 0
-  for scope in walkScopes(c.currentScope):
-    echo "scope ", i
-    for h in 0 .. high(scope.symbols.data):
-      if scope.symbols.data[h] != nil:
-        echo scope.symbols.data[h].name.s
-    if i == limit: break
-    inc i
+when declared(echo):
+  proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
+    var i = 0
+    for scope in walkScopes(c.currentScope):
+      echo "scope ", i
+      for h in 0 .. high(scope.symbols.data):
+        if scope.symbols.data[h] != nil:
+          echo scope.symbols.data[h].name.s
+      if i == limit: break
+      inc i
 
 proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
   for scope in walkScopes(c.currentScope):
@@ -125,9 +126,9 @@ proc errorSym*(c: PContext, n: PNode): PSym =
   # ensure that 'considerQuotedIdent' can't fail:
   if m.kind == nkDotExpr: m = m.sons[1]
   let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
-      considerQuotedIdent(c.config, m)
+      considerQuotedIdent(c, m)
     else:
-      getIdent("err:" & renderTree(m))
+      getIdent(c.cache, "err:" & renderTree(m))
   result = newSym(skError, ident, getCurrOwner(c), n.info, {})
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
@@ -139,7 +140,7 @@ type
   TOverloadIterMode* = enum
     oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice,
     oimSymChoiceLocalLookup
-  TOverloadIter*{.final.} = object
+  TOverloadIter* = object
     it*: TIdentIter
     m*: PSym
     mode*: TOverloadIterMode
@@ -147,10 +148,10 @@ type
     scope*: PScope
     inSymChoice: IntSet
 
-proc getSymRepr*(s: PSym): string =
+proc getSymRepr*(conf: ConfigRef; s: PSym): string =
   case s.kind
   of skProc, skFunc, skMethod, skConverter, skIterator:
-    result = getProcHeader(s)
+    result = getProcHeader(conf, s)
   else:
     result = s.name.s
 
@@ -164,7 +165,8 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
       # too many 'implementation of X' errors are annoying
       # and slow 'suggest' down:
       if missingImpls == 0:
-        localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s))
+        localError(c.config, s.info, "implementation of '$1' expected" %
+            getSymRepr(c.config, s))
       inc missingImpls
     elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
       if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
@@ -172,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
         # maybe they can be made skGenericParam as well.
         if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
            s.typ.kind != tyGenericParam:
-          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s))
+          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
     s = nextIter(it, scope.symbols)
 
 proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) =
@@ -275,7 +277,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
   of nkSym:
     result = n.sym
   of nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     result = searchInScopes(c, ident).skipAlias(n, c.config)
     if result == nil:
       fixSpelling(n, ident, searchInScopes)
@@ -286,7 +288,8 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     return
   if contains(c.ambiguousSymbols, result.id):
     errorUseQualifier(c, n.info, result)
-  if result.kind == skStub: loadStub(result)
+  when false:
+    if result.kind == skStub: loadStub(result)
 
 type
   TLookupFlag* = enum
@@ -296,7 +299,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
   const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     if checkModule in flags:
       result = searchInScopes(c, ident).skipAlias(n, c.config)
     else:
@@ -322,7 +325,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
       if n.sons[1].kind == nkIdent:
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c.config, n.sons[1])
+        ident = considerQuotedIdent(c, n.sons[1])
       if ident != nil:
         if m == c.module:
           result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
@@ -341,12 +344,13 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
         result = errorSym(c, n.sons[1])
   else:
     result = nil
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     o.scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
@@ -367,7 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       if n.sons[1].kind == nkIdent:
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c.config, n.sons[1], n)
+        ident = considerQuotedIdent(c, n.sons[1], n)
       if ident != nil:
         if o.m == c.module:
           # a module may access its private members:
@@ -390,7 +394,8 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     o.inSymChoice = initIntSet()
     incl(o.inSymChoice, result.id)
   else: discard
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc lastOverloadScope*(o: TOverloadIter): int =
   case o.mode
@@ -441,7 +446,8 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       result = firstIdentExcluding(o.it, o.scope.symbols,
                                    n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
 
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
               flags: TSymFlags = {}): PSym =
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 13336f00e..24a4f186e 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,7 +12,8 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs
+import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
+  lineinfos
 from trees import getMagic
 
 proc newDeref*(n: PNode): PNode {.inline.} =
@@ -30,8 +31,8 @@ proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
 proc addVar*(father, v: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
+  vpart.sons[2] = vpart[1]
   addSon(father, vpart)
 
 proc newAsgnStmt(le, ri: PNode): PNode =
@@ -49,7 +50,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
@@ -73,17 +74,17 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
 proc newTryFinally*(body, final: PNode): PNode =
   result = newTree(nkTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options)
+  var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options)
   var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, value.info)
   vpart.sons[2] = value
   addSon(v, vpart)
   result.add(v)
@@ -92,10 +93,10 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
   for i in 0 .. lhs.len-1:
     result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
 
-proc lowerSwap*(n: PNode; owner: PSym): PNode =
+proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
-  var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options)
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options)
   temp.typ = n.sons[1].typ
   incl(temp.flags, sfFromGeneric)
 
@@ -104,7 +105,7 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
 
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = n[1]
   addSon(v, vpart)
 
@@ -120,7 +121,7 @@ proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType
   else:
     rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
-  let s = newSym(skType, getIdent("Env_" & info.toFilename),
+  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)),
                   owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
@@ -171,10 +172,10 @@ proc lookupInRecord(n: PNode, id: int): PSym =
     if n.sym.id == -abs(id): result = n.sym
   else: discard
 
-proc addField*(obj: PType; s: PSym) =
+proc addField*(obj: PType; s: PSym; cache: IdentCache) =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
                      s.options)
   field.id = -s.id
   let t = skipIntLit(s.typ)
@@ -183,10 +184,10 @@ proc addField*(obj: PType; s: PSym) =
   field.position = sonsLen(obj.n)
   addSon(obj.n, newSymNode(field))
 
-proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} =
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} =
   result = lookupInRecord(obj.n, s.id)
   if result == nil:
-    var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
                        s.options)
     field.id = -s.id
     let t = skipIntLit(s.typ)
@@ -227,13 +228,13 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
-proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
+proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.skipTypes(abstractInst).sons[0]
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
-  let bb = getIdent(b)
+  let bb = getIdent(cache, b)
   while true:
     assert t.kind == tyObject
     field = getSymFromList(t.n, bb)
@@ -337,15 +338,15 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
 
 proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
                  v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info,
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
                   owner.options)
   result.typ = typ
   incl(result.flags, sfFromGeneric)
 
   var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
   vpart.sons[0] = newSymNode(result)
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
+  vpart.sons[2] = if varInit.isNil: v else: vpart[1]
   varSection.add vpart
   if varInit != nil:
     if useShallowCopy and typeNeedsNoDeepCopy(typ):
@@ -410,7 +411,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     # generate:
     #   fv.owner = threadParam
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      "owner", fv.info), threadParam.newSymNode)
+      "owner", fv.info, g.cache), threadParam.newSymNode)
 
   body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
   if spawnKind == srByVar:
@@ -421,12 +422,12 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
       localError(g.config, f.info, "cannot create a flowVar of type: " &
         typeToString(fv.typ.sons[1]))
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      if fk == fvGC: "data" else: "blob", fv.info), call)
+      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
     if fk == fvGC:
       let incRefCall = newNodeI(nkCall, fv.info, 2)
       incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
       incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
-                                          "data", fv.info)
+                                          "data", fv.info, g.cache)
       body.add incRefCall
     if barrier == nil:
       # by now 'fv' is shared and thus might have beeen overwritten! we need
@@ -438,7 +439,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
-  params.add emptyNode
+  params.add newNodeI(nkEmpty, f.info)
   params.add threadParam.newSymNode
   params.add argsParam.newSymNode
 
@@ -452,14 +453,18 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   t.n.add argsParam.newSymNode
 
   let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
-  result = newSym(skProc, getIdent(name), argsParam.owner, f.info,
+  result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
                   argsParam.options)
-  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+  let emptyNode = newNodeI(nkEmpty, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body = body,
+      params = params, name = newSymNode(result), pattern = emptyNode,
+      genericParams = emptyNode, pragmas = emptyNode,
+      exceptions = emptyNode)
   result.typ = t
 
 proc createCastExpr(argsParam: PSym; objType: PType): PNode =
   result = newNodeI(nkCast, argsParam.info)
-  result.add emptyNode
+  result.add newNodeI(nkEmpty, argsParam.info)
   result.add newSymNode(argsParam)
   result.typ = newType(tyPtr, objType.owner)
   result.typ.rawAddSon(objType)
@@ -468,7 +473,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
+  let tmpName = getIdent(g.cache, genPrefix)
   for i in 1 ..< n.len:
     # we pick n's type here, which hopefully is 'tyArray' and not
     # 'tyOpenArray':
@@ -481,7 +486,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
     var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
 
     let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
@@ -511,7 +516,7 @@ proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
 
 proc genHigh*(g: ModuleGraph; n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind == tyArray:
-    result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar)))
+    result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar)))
   else:
     result = newNodeI(nkCall, n.info, 2)
     result.typ = getSysType(g, n.info, tyInt)
@@ -522,7 +527,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
+  let tmpName = getIdent(g.cache, genPrefix)
   # we need to copy the foreign scratch object fields into local variables
   # for correctness: These are called 'threadLocal' here.
   for i in 1 ..< n.len:
@@ -543,17 +548,17 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
       slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
       var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
       fieldB.typ = getSysType(g, n.info, tyInt)
-      objType.addField(fieldB)
+      objType.addField(fieldB, g.cache)
 
       if getMagic(n) == mSlice:
         let a = genAddrOf(n[1])
         field.typ = a.typ
-        objType.addField(field)
+        objType.addField(field, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
 
         var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
         fieldA.typ = getSysType(g, n.info, tyInt)
-        objType.addField(fieldA)
+        objType.addField(fieldA, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
 
@@ -564,7 +569,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
       else:
         let a = genAddrOf(n)
         field.typ = a.typ
-        objType.addField(field)
+        objType.addField(field, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
 
@@ -577,12 +582,12 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                                     useShallowCopy=true)
       slice.sons[3] = threadLocal.newSymNode
       call.add slice
-    elif (let size = computeSize(argType); size < 0 or size > 16) and
+    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
         n.getRoot != nil:
       # it is more efficient to pass a pointer instead:
       let a = genAddrOf(n)
       field.typ = a.typ
-      objType.addField(field)
+      objType.addField(field, g.cache)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
       let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
@@ -591,7 +596,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
     else:
       # boring case
       field.typ = argType
-      objType.addField(field)
+      objType.addField(field, g.cache)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
       let threadLocal = addLocalVar(g, varSection, varInit,
                                     objType.owner, field.typ,
@@ -623,8 +628,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
     if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
       localError(g.config, n.info, "'spawn' takes a GC safe call expression")
   var
-    threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options)
-    argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options)
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
   block:
     let ptrType = getSysType(g, n.info, tyPointer)
     threadParam.typ = ptrType
@@ -635,7 +640,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType)
 
-  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options)
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
   block:
     scratchObj.typ = objType
     incl(scratchObj.flags, sfFromGeneric)
@@ -653,9 +658,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
                                                skFunc, skMethod, skConverter}):
     # for indirect calls we pass the function pointer in the scratchObj
     var argType = n[0].typ.skipTypes(abstractInst)
-    var field = newSym(skField, getIdent"fn", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
     fn = indirectAccess(castExpr, field, n.info)
   elif fn.kind == nkSym and fn.sym.kind == skIterator:
@@ -677,17 +682,17 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   if barrier != nil:
     let typ = newType(tyPtr, owner)
     typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
-    var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
     field.typ = typ
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
     barrierAsExpr = indirectAccess(castExpr, field, n.info)
 
   var fvField, fvAsExpr: PNode = nil
   if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
     field.typ = retType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     fvField = newDotExpr(scratchObj, field)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     # create flowVar:
@@ -696,10 +701,10 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
       result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
 
   elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
     field.typ = newType(tyPtr, objType.owner)
     field.typ.rawAddSon(retType)
-    objType.addField(field)
+    objType.addField(field, g.cache)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
 
diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim
new file mode 100644
index 000000000..d23040763
--- /dev/null
+++ b/compiler/macrocacheimpl.nim
@@ -0,0 +1,79 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements helpers for the macro cache.
+
+import lineinfos, ast, modulegraphs, vmdef, magicsys
+
+proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("inc", info)
+  recorded.add newStrNode(key, info)
+  recorded.add newIntNode(nkIntLit, by)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("put", info)
+  recorded.add newStrNode(key, info)
+  recorded.add newStrNode(k, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("add", info)
+  recorded.add newStrNode(key, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("incl", info)
+  recorded.add newStrNode(key, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+when false:
+  proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode =
+    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c))
+
+  proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode =
+    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b))
+
+  template nodeFrom(s: string): PNode =
+    var res = newStrNode(s, info)
+    res.typ = getSysType(g, info, tyString)
+    res
+
+  template nodeFrom(i: BiggestInt): PNode =
+    var res = newIntNode(i, info)
+    res.typ = getSysType(g, info, tyInt)
+    res
+
+  template nodeFrom(n: PNode): PNode = copyTree(n)
+
+  template record(call) =
+    g.recordStmt(g, c.module, call)
+
+  proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
+    let g = c.graph
+    record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by)
+
+  proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
+    let g = c.graph
+    record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val)
+
+  proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+    let g = c.graph
+    record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val)
+
+  proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+    let g = c.graph
+    record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index b5577d961..d40b9d732 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,8 +10,8 @@
 # Built-in types and compilerprocs are registered here.
 
 import
-  ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread,
-  modulegraphs
+  ast, astalgo, hashes, msgs, platform, nversion, times, idents,
+  modulegraphs, lineinfos
 
 export createMagic
 
@@ -26,30 +26,18 @@ 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(name))
+  result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
-    result = newSym(skError, getIdent(name), g.systemModule, g.systemModule.info, {})
+    result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {})
     result.typ = newType(tyError, g.systemModule)
-  if result.kind == skStub: loadStub(result)
   if result.kind == skAlias: result = result.owner
 
-when false:
-  proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-    result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
-    result.magic = m
-
-when false:
-  let
-    opNot* = createMagic("not", mNot)
-    opContains* = createMagic("contains", mInSet)
-
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
   var ti: TIdentIter
-  let id = getIdent(name)
+  let id = getIdent(g.cache, name)
   var r = initIdentIter(ti, g.systemModule.tab, id)
   while r != nil:
-    if r.kind == skStub: loadStub(r)
     if r.magic == m:
       # prefer the tyInt variant:
       if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r
@@ -87,7 +75,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
     of tyString: result = sysTypeFromName("string")
     of tyCString: result = sysTypeFromName("cstring")
     of tyPointer: result = sysTypeFromName("pointer")
-    of tyNil: result = newSysType(g, tyNil, ptrSize)
+    of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize)
     else: internalError(g.config, "request for typekind: " & $kind)
     g.sysTypes[kind] = result
   if result.kind != kind:
@@ -139,7 +127,7 @@ proc addSonSkipIntLit*(father, son: PType) =
 
 proc setIntLitType*(g: ModuleGraph; result: PNode) =
   let i = result.intVal
-  case platform.intSize
+  case g.config.target.intSize
   of 8: result.typ = getIntLitType(g, result)
   of 4:
     if i >= low(int32) and i <= high(int32):
@@ -167,15 +155,8 @@ proc setIntLitType*(g: ModuleGraph; result: PNode) =
     internalError(g.config, result.info, "invalid int size")
 
 proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
-  let ident = getIdent(name)
+  let ident = getIdent(g.cache, name)
   result = strTableGet(g.compilerprocs, ident)
-  when false:
-    if result == nil:
-      result = strTableGet(g.rodCompilerprocs, ident)
-      if result != nil:
-        strTableAdd(g.compilerprocs, result)
-        if result.kind == skStub: loadStub(result)
-        if result.kind == skAlias: result = result.owner
 
 proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
   strTableAdd(g.compilerprocs, s)
@@ -187,9 +168,9 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
     strTableAdd(g.exposed, s)
   else:
     localError(g.config, s.info,
-      "symbol conflicts with other .exportNims symbol at: " & $conflict.info)
+      "symbol conflicts with other .exportNims symbol at: " & g.config$conflict.info)
 
 proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
-  strTableGet(g.exposed, getIdent(name))
+  strTableGet(g.exposed, getIdent(g.cache, name))
 
 proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
diff --git a/compiler/main.nim b/compiler/main.nim
index e3f00db9e..cd05ded62 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -9,74 +9,68 @@
 
 # implements the command dispatcher and several commands
 
+when not defined(nimcore):
+  {.error: "nimcore MUST be defined for Nim's core tooling".}
+
 import
   llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
-  os, condsyms, rodread, rodwrite, times,
+  os, condsyms, times,
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
-  modulegraphs, tables, rod, configuration
+  docgen2, parser, modules, ccgutils, sigmatch, ropes,
+  modulegraphs, tables, rod, lineinfos
 
 from magicsys import resetSysTypes
 
-proc rodPass(g: ModuleGraph) =
-  if g.config.symbolFiles in {enabledSf, writeOnlySf}:
-    registerPass(rodwritePass)
-
-proc codegenPass =
-  registerPass cgenPass
+proc codegenPass(g: ModuleGraph) =
+  registerPass g, cgenPass
 
-proc semanticPasses =
-  registerPass verbosePass
-  registerPass semPass
+proc semanticPasses(g: ModuleGraph) =
+  registerPass g, verbosePass
+  registerPass g, semPass
 
 proc writeDepsFile(g: ModuleGraph; project: string) =
   let f = open(changeFileExt(project, "deps"), fmWrite)
   for m in g.modules:
     if m != nil:
-      f.writeLine(toFullPath(m.position.FileIndex))
+      f.writeLine(toFullPath(g.config, m.position.FileIndex))
   for k in g.inclToMod.keys:
     if g.getModule(k).isNil:  # don't repeat includes which are also modules
-      f.writeLine(k.toFullPath)
+      f.writeLine(toFullPath(g.config, k))
   f.close()
 
-proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
-  semanticPasses()
-  registerPass(gendependPass)
-  #registerPass(cleanupPass)
-  compileProject(graph, cache)
+proc commandGenDepend(graph: ModuleGraph) =
+  semanticPasses(graph)
+  registerPass(graph, gendependPass)
+  compileProject(graph)
   let project = graph.config.projectFull
   writeDepsFile(graph, project)
-  generateDot(project)
+  generateDot(graph, project)
   execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") &
       ' ' & changeFileExt(project, "dot"))
 
-proc commandCheck(graph: ModuleGraph; cache: IdentCache) =
+proc commandCheck(graph: ModuleGraph) =
   graph.config.errorMax = high(int)  # do not stop after first error
   defineSymbol(graph.config.symbols, "nimcheck")
-  semanticPasses()            # use an empty backend for semantic checking only
-  rodPass(graph)
-  compileProject(graph, cache)
+  semanticPasses(graph)  # use an empty backend for semantic checking only
+  compileProject(graph)
 
-proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) =
+proc commandDoc2(graph: ModuleGraph; json: bool) =
   graph.config.errorMax = high(int)  # do not stop after first error
-  semanticPasses()
-  if json: registerPass(docgen2JsonPass)
-  else: registerPass(docgen2Pass)
-  #registerPass(cleanupPass())
-  compileProject(graph, cache)
+  semanticPasses(graph)
+  if json: registerPass(graph, docgen2JsonPass)
+  else: registerPass(graph, docgen2Pass)
+  compileProject(graph)
   finishDoc2Pass(graph.config.projectName)
 
-proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
+proc commandCompileToC(graph: ModuleGraph) =
   let conf = graph.config
   extccomp.initVars(conf)
-  semanticPasses()
-  registerPass(cgenPass)
-  rodPass(graph)
-  #registerPass(cleanupPass())
+  semanticPasses(graph)
+  registerPass(graph, cgenPass)
 
-  compileProject(graph, cache)
+  compileProject(graph)
   cgenWriteModules(graph.backend, conf)
   if conf.cmd != cmdRun:
     let proj = changeFileExt(conf.projectFull, "")
@@ -85,53 +79,51 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
     if optGenScript in graph.config.globalOptions:
       writeDepsFile(graph, toGeneratedFile(conf, proj, ""))
 
-proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
+proc commandJsonScript(graph: ModuleGraph) =
   let proj = changeFileExt(graph.config.projectFull, "")
   extccomp.runJsonBuildInstructions(graph.config, proj)
 
-proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
+proc commandCompileToJS(graph: ModuleGraph) =
   #incl(gGlobalOptions, optSafeCode)
-  setTarget(osJS, cpuJS)
+  setTarget(graph.config.target, osJS, cpuJS)
   #initDefines()
   defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
   defineSymbol(graph.config.symbols, "js")
-  semanticPasses()
-  registerPass(JSgenPass)
-  compileProject(graph, cache)
+  semanticPasses(graph)
+  registerPass(graph, JSgenPass)
+  compileProject(graph)
 
-proc interactivePasses(graph: ModuleGraph; cache: IdentCache) =
-  #incl(gGlobalOptions, optSafeCode)
-  #setTarget(osNimrodVM, cpuNimrodVM)
+proc interactivePasses(graph: ModuleGraph) =
   initDefines(graph.config.symbols)
   defineSymbol(graph.config.symbols, "nimscript")
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  registerPass(verbosePass)
-  registerPass(semPass)
-  registerPass(evalPass)
+  registerPass(graph, verbosePass)
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
 
-proc commandInteractive(graph: ModuleGraph; cache: IdentCache) =
+proc commandInteractive(graph: ModuleGraph) =
   graph.config.errorMax = high(int)  # do not stop after first error
-  interactivePasses(graph, cache)
-  compileSystemModule(graph, cache)
+  interactivePasses(graph)
+  compileSystemModule(graph)
   if graph.config.commandArgs.len > 0:
-    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {})
+    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
-    processModule(graph, m, llStreamOpenStdIn(), nil, cache)
+    processModule(graph, m, llStreamOpenStdIn())
 
 const evalPasses = [verbosePass, semPass, evalPass]
 
-proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache) =
-  carryPasses(graph, nodes, module, cache, evalPasses)
+proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
+  carryPasses(graph, nodes, module, evalPasses)
 
-proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) =
+proc commandEval(graph: ModuleGraph; exp: string) =
   if graph.systemModule == nil:
-    interactivePasses(graph, cache)
-    compileSystemModule(graph, cache)
+    interactivePasses(graph)
+    compileSystemModule(graph)
   let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
-  evalNim(graph, echoExp.parseString(cache, graph.config),
-    makeStdinModule(graph), cache)
+  evalNim(graph, echoExp.parseString(graph.cache, graph.config),
+    makeStdinModule(graph))
 
 proc commandScan(cache: IdentCache, config: ConfigRef) =
   var f = addFileExt(mainCommandArg(config), NimExt)
@@ -153,12 +145,13 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
 const
   PrintRopeCacheStats = false
 
-proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
+proc mainCommand*(graph: ModuleGraph) =
   let conf = graph.config
+  let cache = graph.cache
 
-  setupModuleCache()
+  setupModuleCache(graph)
   # In "nim serve" scenario, each command must reset the registered passes
-  clearPasses()
+  clearPasses(graph)
   conf.lastCmdTime = epochTime()
   conf.searchPaths.add(conf.libpath)
   setId(100)
@@ -166,69 +159,69 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
   of "c", "cc", "compile", "compiletoc":
     # compile means compileToC currently
     conf.cmd = cmdCompileToC
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "cpp", "compiletocpp":
     conf.cmd = cmdCompileToCpp
     defineSymbol(graph.config.symbols, "cpp")
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "objc", "compiletooc":
     conf.cmd = cmdCompileToOC
     defineSymbol(graph.config.symbols, "objc")
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "run":
     conf.cmd = cmdRun
     when hasTinyCBackend:
       extccomp.setCC("tcc")
-      commandCompileToC(graph, cache)
+      commandCompileToC(graph)
     else:
       rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
   of "js", "compiletojs":
     conf.cmd = cmdCompileToJS
-    commandCompileToJS(graph, cache)
+    commandCompileToJS(graph)
   of "doc0":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
-    commandDoc(conf)
+    commandDoc(cache, conf)
   of "doc2", "doc":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, cache, false)
+    commandDoc2(graph, false)
   of "rst2html":
     conf.cmd = cmdRst2html
     loadConfigs(DocConfig, cache, conf)
-    commandRst2Html(conf)
+    commandRst2Html(cache, conf)
   of "rst2tex":
     conf.cmd = cmdRst2tex
     loadConfigs(DocTexConfig, cache, conf)
-    commandRst2TeX(conf)
+    commandRst2TeX(cache, conf)
   of "jsondoc0":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     wantMainModule(conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandJson(conf)
+    commandJson(cache, conf)
   of "jsondoc2", "jsondoc":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     wantMainModule(conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, cache, true)
+    commandDoc2(graph, true)
   of "ctags":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandTags(conf)
+    commandTags(cache, conf)
   of "buildindex":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
-    commandBuildIndex(conf)
+    commandBuildIndex(cache, conf)
   of "gendepend":
     conf.cmd = cmdGenDepend
-    commandGenDepend(graph, cache)
+    commandGenDepend(graph)
   of "dump":
     conf.cmd = cmdDump
     if getConfigVar(conf, "dump.format") == "json":
@@ -257,11 +250,11 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
       for it in conf.searchPaths: msgWriteln(conf, it)
   of "check":
     conf.cmd = cmdCheck
-    commandCheck(graph, cache)
+    commandCheck(graph)
   of "parse":
     conf.cmd = cmdParse
     wantMainModule(conf)
-    discard parseFile(FileIndex conf.projectMainIdx, cache, conf)
+    discard parseFile(conf.projectMainIdx, cache, conf)
   of "scan":
     conf.cmd = cmdScan
     wantMainModule(conf)
@@ -269,15 +262,15 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
     msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
   of "secret":
     conf.cmd = cmdInteractive
-    commandInteractive(graph, cache)
+    commandInteractive(graph)
   of "e":
-    commandEval(graph, cache, mainCommandArg(conf))
+    commandEval(graph, mainCommandArg(conf))
   of "nop", "help":
     # prevent the "success" message:
     conf.cmd = cmdDump
   of "jsonscript":
     conf.cmd = cmdJsonScript
-    commandJsonScript(graph, cache)
+    commandJsonScript(graph)
   else:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
@@ -302,5 +295,3 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
                                        ffDecimal, 3)
 
   resetAttributes(conf)
-
-#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 460d0b4a5..334cd1ae6 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -9,7 +9,7 @@
 
 ## 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 ROD file mechanism is not yet integrated here.
+## or stored in a Sqlite database.
 ##
 ## 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
@@ -25,7 +25,8 @@
 ## - Its dependent module stays the same.
 ##
 
-import ast, intsets, tables, options, rod, msgs, hashes, idents
+import ast, intsets, tables, options, lineinfos, hashes, idents,
+  incremental, btrees
 
 type
   ModuleGraph* = ref object
@@ -40,16 +41,27 @@ type
                                   # module dependencies.
     backend*: RootRef # minor hack so that a backend can extend this easily
     config*: ConfigRef
+    cache*: IdentCache
+    vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
+                 # be clarified in later compiler implementations.
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
-    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
+    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] # needs serialization!
     systemModule*: PSym
     sysTypes*: array[TTypeKind, PType]
     compilerprocs*: TStrTable
     exposed*: TStrTable
     intTypeCache*: array[-5..64, PType]
     opContains*, opNot*: PSym
+    emptyNode*: PNode
+    incr*: IncrementalCtx
+    importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
+    includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
+    recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.}
+    cacheSeqs*: Table[string, PNode] # state that is shared to suppor the 'macrocache' API
+    cacheCounters*: Table[string, BiggestInt]
+    cacheTables*: Table[string, BTree[string, PNode]]
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
@@ -59,26 +71,31 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
   result = doStopCompile != nil and doStopCompile()
 
 proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(name), nil, unknownLineInfo(), {})
+  result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
   result.magic = m
 
-proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
+proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result = ModuleGraph()
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.modules = @[]
   result.importStack = @[]
   result.inclToMod = initTable[FileIndex, FileIndex]()
-  if config.isNil:
-    result.config = newConfigRef()
-  else:
-    result.config = config
+  result.config = config
+  result.cache = cache
   result.owners = @[]
   result.methods = @[]
   initStrTable(result.compilerprocs)
   initStrTable(result.exposed)
   result.opNot = createMagic(result, "not", mNot)
   result.opContains = createMagic(result, "contains", mInSet)
+  result.emptyNode = newNode(nkEmpty)
+  init(result.incr)
+  result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} =
+    discard
+  result.cacheSeqs = initTable[string, PNode]()
+  result.cacheCounters = initTable[string, BiggestInt]()
+  result.cacheTables = initTable[string, BTree[string, PNode]]()
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
@@ -100,15 +117,15 @@ proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
 proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
   assert m.position == m.info.fileIndex.int32
-  addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
+  addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
   if suggestMode:
     deps.incl m.position.dependsOn(dep.int)
     # we compute the transitive closure later when quering the graph lazily.
-    # this improve efficiency quite a lot:
+    # this improves efficiency quite a lot:
     #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
-  addModuleDep(module, includeFile, isIncludeFile = true)
+  addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
   discard hasKeyOrPut(inclToMod, includeFile, module)
 
 proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index daa55c2ba..87c7f3541 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, renderer, strutils, msgs, options, idents, os
+import ast, renderer, strutils, msgs, options, idents, os, lineinfos
 
 import nimblecmd
 
@@ -120,7 +120,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
     try:
-      result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir)
+      result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
     except ValueError:
       localError(conf, n.info, "invalid path: " & n.strVal)
       result = n.strVal
@@ -131,7 +131,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   of nkInfix:
     let n0 = n[0]
     let n1 = n[1]
-    if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
+    if n0.kind == nkIdent and n0.ident.s == "as":
       # XXX hack ahead:
       n.kind = nkImportAs
       n.sons[0] = n.sons[1]
@@ -177,7 +177,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
 proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
   # This returns the full canonical path for a given module import
   let modulename = getModuleName(conf, n)
-  let fullPath = findModule(conf, modulename, n.info.toFullPath)
+  let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
   if fullPath.len == 0:
     if doLocalError:
       let m = if modulename.len > 0: modulename else: $n
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 5d1eba1f2..04b1506f4 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,9 +10,9 @@
 ## Implements the module handling, including the caching of modules.
 
 import
-  ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
+  ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
   idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
-  configuration
+  lineinfos
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
   magicsys.resetSysTypes(g)
@@ -23,8 +23,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   new(result)
   result.id = -1             # for better error checking
   result.kind = skModule
-  let filename = fileIdx.toFullPath
-  result.name = getIdent(splitFile(filename).name)
+  let filename = toFullPath(graph.config, fileIdx)
+  result.name = getIdent(graph.cache, splitFile(filename).name)
   if not isNimIdentifier(result.name.s):
     rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
 
@@ -32,17 +32,19 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   let
     pck = getPackageName(graph.config, filename)
     pck2 = if pck.len > 0: pck else: "unknown"
-    pack = getIdent(pck2)
+    pack = getIdent(graph.cache, pck2)
   var packSym = graph.packageSyms.strTableGet(pack)
   if packSym == nil:
-    packSym = newSym(skPackage, getIdent(pck2), nil, result.info)
+    packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
     initStrTable(packSym.tab)
     graph.packageSyms.strTableAdd(packSym)
 
   result.owner = packSym
   result.position = int fileIdx
 
-  growCache graph.modules, int fileIdx
+  if int(fileIdx) >= graph.modules.len:
+    setLen(graph.modules, int(fileIdx) + 1)
+  #growCache graph.modules, int fileIdx
   graph.modules[result.position] = result
 
   incl(result.flags, sfUsed)
@@ -50,95 +52,75 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   strTableAdd(result.tab, result) # a module knows itself
   let existing = strTableGet(packSym.tab, result.name)
   if existing != nil and existing.info.fileIndex != result.info.fileIndex:
-    localError(graph.config, result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
+    localError(graph.config, result.info,
+      "module names need to be unique per Nimble package; module clashes with " &
+        toFullPath(graph.config, existing.info.fileIndex))
   # strTableIncl() for error corrections:
   discard strTableIncl(packSym.tab, result)
 
-proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
+proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
   result = graph.getModule(fileIdx)
   if result == nil:
-    #growCache gMemCacheData, fileIdx
-    #gMemCacheData[fileIdx].needsRecompile = Probing
     result = newModule(graph, fileIdx)
-    var rd: PRodReader
     result.flags = result.flags + flags
     if sfMainModule in result.flags:
-      gMainPackageId = result.owner.id
-
-    when false:
-      if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
-        rd = handleSymbolFile(result, cache)
-        if result.id < 0:
-          internalError("handleSymbolFile should have set the module's ID")
-          return
-      else:
-        discard
-    result.id = getModuleId(fileIdx, toFullPath(fileIdx))
+      graph.config.mainPackageId = result.owner.id
+
+    result.id = getModuleId(graph, fileIdx, toFullPath(graph.config, fileIdx))
     discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
-      rd, cache)
-    #if optCaasEnabled in gGlobalOptions:
-    #  gMemCacheData[fileIdx].needsRecompile = Recompiled
-    #  if validFile: doHash fileIdx
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
   elif graph.isDirty(result):
     result.flags.excl sfDirty
     # reset module fields:
     initStrTable(result.tab)
     result.ast = nil
     discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
-      nil, cache)
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
     graph.markClientsDirty(fileIdx)
-    when false:
-      if checkDepMem(fileIdx) == Yes:
-        result = compileModule(fileIdx, cache, flags)
-      else:
-        result = gCompiledModules[fileIdx]
-
-proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                   cache: IdentCache): PSym {.procvar.} =
+
+proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym {.procvar.} =
   # this is called by the semantic checking phase
   assert graph.config != nil
-  result = compileModule(graph, fileIdx, cache, {})
+  result = compileModule(graph, fileIdx, {})
   graph.addDep(s, fileIdx)
   #if sfSystemModule in result.flags:
   #  localError(result.info, errAttemptToRedefine, result.name.s)
   # restore the notes for outer module:
   graph.config.notes =
-      if s.owner.id == gMainPackageId: graph.config.mainPackageNotes
-      else: graph.config.foreignPackageNotes
+    if s.owner.id == graph.config.mainPackageId: graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                    cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache, graph.config)
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+  result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(s.position.FileIndex, fileIdx)
 
-proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
+proc compileSystemModule*(graph: ModuleGraph) =
   if graph.systemModule == nil:
-    systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
-    discard graph.compileModule(systemFileIdx, cache, {sfSystemModule})
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
+    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
 
 proc wantMainModule*(conf: ConfigRef) =
   if conf.projectFull.len == 0:
     fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename")
-  conf.projectMainIdx = int32 fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
+  conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
 
-passes.gIncludeFile = includeModule
-passes.gImportModule = importModule
+proc connectCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = includeModule
+  graph.importModuleCallback = importModule
 
-proc compileProject*(graph: ModuleGraph; cache: IdentCache;
-                     projectFileIdx = InvalidFileIDX) =
+proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
+  connectCallbacks(graph)
   let conf = graph.config
   wantMainModule(conf)
   let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim")
-  let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx
+  let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx
   graph.importStack.add projectFile
   if projectFile == systemFileIdx:
-    discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
+    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
   else:
-    graph.compileSystemModule(cache)
-    discard graph.compileModule(projectFile, cache, {sfMainModule})
+    graph.compileSystemModule()
+    discard graph.compileModule(projectFile, {sfMainModule})
 
 proc makeModule*(graph: ModuleGraph; filename: string): PSym =
   result = graph.newModule(fileInfoIdx(graph.config, filename))
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 533d3a57f..62948e81e 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -9,83 +9,28 @@
 
 import
   options, strutils, os, tables, ropes, platform, terminal, macros,
-  configuration
+  lineinfos
 
-#type
-#  MsgConfig* = ref object of RootObj
-
-type
-  TFileInfo* = object
-    fullPath: string           # This is a canonical full filesystem path
-    projPath*: string          # This is relative to the project's root
-    shortName*: string         # short name of the module
-    quotedName*: Rope          # cached quoted short name for codegen
-                               # purposes
-    quotedFullName*: Rope      # cached quoted full name for codegen
-                               # purposes
-
-    lines*: seq[Rope]          # the source code of the module
-                               #   used for better error messages and
-                               #   embedding the original source in the
-                               #   generated code
-    dirtyfile: string          # the file that is actually read into memory
-                               # and parsed; usually 'nil' but is used
-                               # for 'nimsuggest'
-    hash*: string              # the checksum of the file
-    when defined(nimpretty):
-      fullContent*: string
-  FileIndex* = distinct int32
-  TLineInfo* = object          # This is designed to be as small as possible,
-                               # because it is used
-                               # in syntax nodes. We save space here by using
-                               # two int16 and an int32.
-                               # On 64 bit and on 32 bit systems this is
-                               # only 8 bytes.
-    line*: uint16
-    col*: int16
-    fileIndex*: FileIndex
-    when defined(nimpretty):
-      offsetA*, offsetB*: int
-      commentOffsetA*, commentOffsetB*: int
-
-  TErrorOutput* = enum
-    eStdOut
-    eStdErr
-
-  TErrorOutputs* = set[TErrorOutput]
-
-  ERecoverableError* = object of ValueError
-  ESuggestDone* = object of Exception
-
-proc `==`*(a, b: FileIndex): bool {.borrow.}
-
-
-const
-  InvalidFileIDX* = FileIndex(-1)
-
-var
-  filenameToIndexTbl = initTable[string, FileIndex]()
-  fileInfos*: seq[TFileInfo] = @[]
-  systemFileIdx*: FileIndex
-
-proc toCChar*(c: char): string =
+proc toCChar*(c: char; result: var string) =
   case c
-  of '\0'..'\x1F', '\x7F'..'\xFF': result = '\\' & toOctal(c)
-  of '\'', '\"', '\\', '?': result = '\\' & c
-  else: result = $(c)
+  of '\0'..'\x1F', '\x7F'..'\xFF':
+    result.add '\\'
+    result.add toOctal(c)
+  of '\'', '\"', '\\', '?':
+    result.add '\\'
+    result.add c
+  else:
+    result.add c
 
 proc makeCString*(s: string): Rope =
-  const
-    MaxLineLength = 64
+  const MaxLineLength = 64
   result = nil
   var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
   add(res, "\"")
   for i in countup(0, len(s) - 1):
     if (i + 1) mod MaxLineLength == 0:
-      add(res, '\"')
-      add(res, tnl)
-      add(res, '\"')
-    add(res, toCChar(s[i]))
+      add(res, "\"\L\"")
+    toCChar(s[i], res)
   add(res, '\"')
   add(result, rope(res))
 
@@ -110,8 +55,8 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
         result.fullContent = ""
 
 when defined(nimpretty):
-  proc fileSection*(fid: FileIndex; a, b: int): string =
-    substr(fileInfos[fid.int].fullContent, a, b)
+  proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
+    substr(conf.m.fileInfos[fid.int].fullContent, a, b)
 
 proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
   var
@@ -120,7 +65,7 @@ proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
     canon = canonicalizePath(conf, filename)
   except:
     canon = filename
-  result = filenameToIndexTbl.hasKey(canon)
+  result = conf.m.filenameToIndexTbl.hasKey(canon)
 
 proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
   var
@@ -136,14 +81,14 @@ proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): Fil
     # This flag indicates that we are working with such a path here
     pseudoPath = true
 
-  if filenameToIndexTbl.hasKey(canon):
-    result = filenameToIndexTbl[canon]
+  if conf.m.filenameToIndexTbl.hasKey(canon):
+    result = conf.m.filenameToIndexTbl[canon]
   else:
     isKnownFile = false
-    result = fileInfos.len.FileIndex
-    fileInfos.add(newFileInfo(canon, if pseudoPath: filename
-                                     else: shortenDir(conf, canon)))
-    filenameToIndexTbl[canon] = result
+    result = conf.m.fileInfos.len.FileIndex
+    conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename
+                                            else: shortenDir(conf, canon)))
+    conf.m.filenameToIndexTbl[canon] = result
 
 proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
   var dummy: bool
@@ -157,34 +102,9 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
 proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
   result = newLineInfo(fileInfoIdx(conf, filename), line, col)
 
-when false:
-  fileInfos.add(newFileInfo("", "command line"))
-  var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
-
-  fileInfos.add(newFileInfo("", "compilation artifact"))
-  var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
-
 proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
   raise newException(ERecoverableError, msg)
 
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope
-
-proc unknownLineInfo*(): TLineInfo =
-  result.line = uint16(0)
-  result.col = int16(-1)
-  result.fileIndex = InvalidFileIDX
-
-type
-  Severity* {.pure.} = enum ## VS Code only supports these three
-    Hint, Warning, Error
-
-var
-  msgContext: seq[TLineInfo] = @[]
-  lastError = unknownLineInfo()
-
-  errorOutputs* = {eStdOut, eStdErr}
-  writelnHook*: proc (output: string) {.closure.}
-  structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.}
 
 proc concat(strings: openarray[string]): string =
   var totalLen = 0
@@ -192,13 +112,13 @@ proc concat(strings: openarray[string]): string =
   result = newStringOfCap totalLen
   for s in strings: result.add s
 
-proc suggestWriteln*(s: string) =
-  if eStdOut in errorOutputs:
-    if isNil(writelnHook):
+proc suggestWriteln*(conf: ConfigRef; s: string) =
+  if eStdOut in conf.m.errorOutputs:
+    if isNil(conf.writelnHook):
       writeLine(stdout, s)
       flushFile(stdout)
     else:
-      writelnHook(s)
+      conf.writelnHook(s)
 
 proc msgQuit*(x: int8) = quit x
 proc msgQuit*(x: string) = quit x
@@ -219,61 +139,61 @@ const
   HintTitle    = "Hint: "
   HintColor    = fgGreen
 
-proc getInfoContextLen*(): int = return msgContext.len
-proc setInfoContextLen*(L: int) = setLen(msgContext, L)
+proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
+proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
 
-proc pushInfoContext*(info: TLineInfo) =
-  msgContext.add(info)
+proc pushInfoContext*(conf: ConfigRef; info: TLineInfo) =
+  conf.m.msgContext.add(info)
 
-proc popInfoContext*() =
-  setLen(msgContext, len(msgContext) - 1)
+proc popInfoContext*(conf: ConfigRef) =
+  setLen(conf.m.msgContext, len(conf.m.msgContext) - 1)
 
-proc getInfoContext*(index: int): TLineInfo =
-  let L = msgContext.len
+proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
+  let L = conf.m.msgContext.len
   let i = if index < 0: L + index else: index
   if i >=% L: result = unknownLineInfo()
-  else: result = msgContext[i]
+  else: result = conf.m.msgContext[i]
 
-template toFilename*(fileIdx: FileIndex): string =
-  (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath)
+template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
+  (if fileIdx.int32 < 0: "???" else: conf.m.fileInfos[fileIdx.int32].projPath)
 
-proc toFullPath*(fileIdx: FileIndex): string =
+proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
   if fileIdx.int32 < 0: result = "???"
-  else: result = fileInfos[fileIdx.int32].fullPath
+  else: result = conf.m.fileInfos[fileIdx.int32].fullPath
 
-proc setDirtyFile*(fileIdx: FileIndex; filename: string) =
+proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) =
   assert fileIdx.int32 >= 0
-  fileInfos[fileIdx.int32].dirtyFile = filename
+  conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
 
-proc setHash*(fileIdx: FileIndex; hash: string) =
+proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
   assert fileIdx.int32 >= 0
-  shallowCopy(fileInfos[fileIdx.int32].hash, hash)
+  shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
 
-proc getHash*(fileIdx: FileIndex): string =
+proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
   assert fileIdx.int32 >= 0
-  shallowCopy(result, fileInfos[fileIdx.int32].hash)
+  shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
 
-proc toFullPathConsiderDirty*(fileIdx: FileIndex): string =
+proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string =
   if fileIdx.int32 < 0:
     result = "???"
-  elif not fileInfos[fileIdx.int32].dirtyFile.isNil:
-    result = fileInfos[fileIdx.int32].dirtyFile
+  elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil:
+    result = conf.m.fileInfos[fileIdx.int32].dirtyFile
   else:
-    result = fileInfos[fileIdx.int32].fullPath
+    result = conf.m.fileInfos[fileIdx.int32].fullPath
 
-template toFilename*(info: TLineInfo): string =
-  info.fileIndex.toFilename
+template toFilename*(conf: ConfigRef; info: TLineInfo): string =
+  toFilename(conf, info.fileIndex)
 
-template toFullPath*(info: TLineInfo): string =
-  info.fileIndex.toFullPath
+template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
+  toFullPath(conf, info.fileIndex)
 
 proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
   if info.fileIndex.int32 < 0:
     result = "???"
   elif optListFullPaths in conf.globalOptions:
-    result = fileInfos[info.fileIndex.int32].fullPath
+    result = conf.m.fileInfos[info.fileIndex.int32].fullPath
   else:
-    result = fileInfos[info.fileIndex.int32].projPath
+    result = conf.m.fileInfos[info.fileIndex.int32].projPath
 
 proc toLinenumber*(info: TLineInfo): int {.inline.} =
   result = int info.line
@@ -281,23 +201,19 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} =
 proc toColumn*(info: TLineInfo): int {.inline.} =
   result = info.col
 
-proc toFileLine*(info: TLineInfo): string {.inline.} =
-  result = info.toFilename & ":" & $info.line
+proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
+  result = toFilename(conf, info) & ":" & $info.line
 
-proc toFileLineCol*(info: TLineInfo): string {.inline.} =
-  result = info.toFilename & "(" & $info.line & ", " & $info.col & ")"
+proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
+  result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
 
-proc `$`*(info: TLineInfo): string = toFileLineCol(info)
+proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
 
-proc `??`* (info: TLineInfo, filename: string): bool =
-  # only for debugging purposes
-  result = filename in info.toFilename
+proc `$`*(info: TLineInfo): string {.error.} = discard
 
-const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
-                                   # are produced within comments and string literals
-var gTrackPos*: TLineInfo
-var gTrackPosAttached*: bool ## whether the tracking position was attached to some
-                             ## close token.
+proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
+  # only for debugging purposes
+  result = filename in toFilename(conf, info)
 
 type
   MsgFlag* = enum  ## flags altering msgWriteln behavior
@@ -315,14 +231,14 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
   ## support.
   #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
 
-  if not isNil(writelnHook) and msgSkipHook notin flags:
-    writelnHook(s)
+  if not isNil(conf.writelnHook) and msgSkipHook notin flags:
+    conf.writelnHook(s)
   elif optStdout in conf.globalOptions or msgStdout in flags:
-    if eStdOut in errorOutputs:
+    if eStdOut in conf.m.errorOutputs:
       writeLine(stdout, s)
       flushFile(stdout)
   else:
-    if eStdErr in errorOutputs:
+    if eStdErr in conf.m.errorOutputs:
       writeLine(stderr, s)
       # On Windows stderr is fully-buffered when piped, regardless of C std.
       when defined(windows):
@@ -353,17 +269,17 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
     result.add(arg)
 
 template callWritelnHook(args: varargs[string, `$`]) =
-  writelnHook concat(args)
+  conf.writelnHook concat(args)
 
 template styledMsgWriteln*(args: varargs[typed]) =
-  if not isNil(writelnHook):
+  if not isNil(conf.writelnHook):
     callIgnoringStyle(callWritelnHook, nil, args)
   elif optStdout in conf.globalOptions:
-    if eStdOut in errorOutputs:
+    if eStdOut in conf.m.errorOutputs:
       callIgnoringStyle(writeLine, stdout, args)
       flushFile(stdout)
   else:
-    if eStdErr in errorOutputs:
+    if eStdErr in conf.m.errorOutputs:
       if optUseColors in conf.globalOptions:
         callStyledWriteLineStderr(args)
       else:
@@ -394,7 +310,7 @@ proc log*(s: string) {.procvar.} =
 
 proc quit(conf: ConfigRef; msg: TMsgKind) =
   if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
-    if stackTraceAvailable() and isNil(writelnHook):
+    if stackTraceAvailable() and isNil(conf.writelnHook):
       writeStackTrace()
     else:
       styledMsgWriteln(fgRed, "No stack traceback available\n" &
@@ -425,19 +341,19 @@ proc exactEquals*(a, b: TLineInfo): bool =
 proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
   const instantiationFrom = "template/generic instantiation from here"
   var info = lastinfo
-  for i in countup(0, len(msgContext) - 1):
-    if msgContext[i] != lastinfo and msgContext[i] != info:
-      if structuredErrorHook != nil:
-        structuredErrorHook(msgContext[i], instantiationFrom,
-                            Severity.Error)
+  for i in 0 ..< len(conf.m.msgContext):
+    if conf.m.msgContext[i] != lastinfo and conf.m.msgContext[i] != info:
+      if conf.structuredErrorHook != nil:
+        conf.structuredErrorHook(conf, conf.m.msgContext[i], instantiationFrom,
+                                  Severity.Error)
       else:
         styledMsgWriteln(styleBright,
-                         PosFormat % [toMsgFilename(conf, msgContext[i]),
-                                      coordToStr(msgContext[i].line.int),
-                                      coordToStr(msgContext[i].col+1)],
+                         PosFormat % [toMsgFilename(conf, conf.m.msgContext[i]),
+                                      coordToStr(conf.m.msgContext[i].line.int),
+                                      coordToStr(conf.m.msgContext[i].col+1)],
                          resetStyle,
                          instantiationFrom)
-    info = msgContext[i]
+    info = conf.m.msgContext[i]
 
 proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
   msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
@@ -473,8 +389,9 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
     inc(conf.hintCounter)
   let s = msgKindToString(msg) % args
 
-  if structuredErrorHook != nil:
-    structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev)
+  if conf.structuredErrorHook != nil:
+    conf.structuredErrorHook(conf, unknownLineInfo(),
+      s & (if kind != nil: KindFormat % kind else: ""), sev)
 
   if not ignoreMsgBecauseOfIdeTools(conf, msg):
     if kind != nil:
@@ -491,6 +408,24 @@ proc resetAttributes*(conf: ConfigRef) =
   if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}:
     terminal.resetAttributes(stderr)
 
+proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
+  conf.m.fileInfos[fileIdx.int32].lines.add line
+
+proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
+  if i.fileIndex.int32 < 0: return ""
+
+  if not optPreserveOrigSource(conf) and conf.m.fileInfos[i.fileIndex.int32].lines.len == 0:
+    try:
+      for line in lines(toFullPath(conf, i)):
+        addSourceLine conf, i.fileIndex, line.string
+    except IOError:
+      discard
+  assert i.fileIndex.int32 < conf.m.fileInfos.len
+  # can happen if the error points to EOF:
+  if i.line.int > conf.m.fileInfos[i.fileIndex.int32].lines.len: return ""
+
+  result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
+
 proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
   const indent = "  "
   msgWriteln(conf, indent & $sourceLine(conf, info))
@@ -523,7 +458,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
     # we try to filter error messages so that not two error message
     # in the same file and line are produced:
     #ignoreMsg = lastError == info and eh != doAbort
-    lastError = info
+    conf.m.lastError = info
   of warnMin..warnMax:
     sev = Severity.Warning
     ignoreMsg = optWarns notin conf.options or msg notin conf.notes
@@ -547,8 +482,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
   let s = getMessageStr(msg, arg)
 
   if not ignoreMsg:
-    if structuredErrorHook != nil:
-      structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev)
+    if conf.structuredErrorHook != nil:
+      conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev)
     if not ignoreMsgBecauseOfIdeTools(conf, msg):
       if kind != nil:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
@@ -562,7 +497,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
 proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
   # this fixes bug #7080 so that it is at least obvious 'fatal'
   # was executed.
-  errorOutputs = {eStdOut, eStdErr}
+  conf.m.errorOutputs = {eStdOut, eStdErr}
   liMessage(conf, info, msg, arg, doAbort)
 
 proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
@@ -584,12 +519,12 @@ proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
   liMessage(conf, info, msg, arg, doNothing)
 
 proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
-  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
   writeContext(conf, info)
   liMessage(conf, info, errInternal, errMsg, doAbort)
 
 proc internalError*(conf: ConfigRef; errMsg: string) =
-  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
   writeContext(conf, unknownLineInfo())
   rawMessage(conf, errInternal, errMsg)
 
@@ -600,44 +535,19 @@ template assertNotNil*(conf: ConfigRef; e): untyped =
 template internalAssert*(conf: ConfigRef, e: bool) =
   if not e: internalError(conf, $instantiationInfo())
 
-proc addSourceLine*(fileIdx: FileIndex, line: string) =
-  fileInfos[fileIdx.int32].lines.add line.rope
-
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope =
-  if i.fileIndex.int32 < 0: return nil
-
-  if not optPreserveOrigSource(conf) and fileInfos[i.fileIndex.int32].lines.len == 0:
-    try:
-      for line in lines(i.toFullPath):
-        addSourceLine i.fileIndex, line.string
-    except IOError:
-      discard
-  assert i.fileIndex.int32 < fileInfos.len
-  # can happen if the error points to EOF:
-  if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
-
-  result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
-
 proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
   assert i.fileIndex.int32 >= 0
   if optExcessiveStackTrace in conf.globalOptions:
-    result = fileInfos[i.fileIndex.int32].quotedFullName
+    result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
   else:
-    result = fileInfos[i.fileIndex.int32].quotedName
-
-ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
-  case err
-  of rInvalidFormatStr:
-    internalError(newPartialConfigRef(), "ropes: invalid format string: " & msg)
-  of rCannotOpenFile:
-    rawMessage(newPartialConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
+    result = conf.m.fileInfos[i.fileIndex.int32].quotedName
 
 proc listWarnings*(conf: ConfigRef) =
   msgWriteln(conf, "Warnings:")
   for warn in warnMin..warnMax:
     msgWriteln(conf, "  [$1] $2" % [
       if warn in conf.notes: "x" else: " ",
-      configuration.WarningsToStr[ord(warn) - ord(warnMin)]
+      lineinfos.WarningsToStr[ord(warn) - ord(warnMin)]
     ])
 
 proc listHints*(conf: ConfigRef) =
@@ -645,5 +555,5 @@ proc listHints*(conf: ConfigRef) =
   for hint in hintMin..hintMax:
     msgWriteln(conf, "  [$1] $2" % [
       if hint in conf.notes: "x" else: " ",
-      configuration.HintsToStr[ord(hint) - ord(hintMin)]
+      lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
     ])
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
index a7ca02193..9708c388d 100644
--- a/compiler/ndi.nim
+++ b/compiler/ndi.nim
@@ -10,7 +10,7 @@
 ## This module implements the generation of ``.ndi`` files for better debugging
 ## support of Nim code. "ndi" stands for "Nim debug info".
 
-import ast, msgs, ropes
+import ast, msgs, ropes, options
 
 type
   NdiFile* = object
@@ -18,19 +18,19 @@ type
     f: File
     buf: string
 
-proc doWrite(f: var NdiFile; s: PSym) =
+proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
   f.buf.setLen 0
   f.buf.add s.info.line.int
   f.buf.add "\t"
   f.buf.add s.info.col.int
   f.f.write(s.name.s, "\t")
   f.f.writeRope(s.loc.r)
-  f.f.writeLine("\t", s.info.toFullPath, "\t", f.buf)
+  f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
 
-template writeMangledName*(f: NdiFile; s: PSym) =
-  if f.enabled: doWrite(f, s)
+template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
+  if f.enabled: doWrite(f, s, conf)
 
-proc open*(f: var NdiFile; filename: string) =
+proc open*(f: var NdiFile; filename: string; conf: ConfigRef) =
   f.enabled = filename.len > 0
   if f.enabled:
     f.f = open(filename, fmWrite, 8000)
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 853ae7e00..9cdb9c6c9 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -5,6 +5,8 @@ path:"llvm"
 path:"$projectPath/.."
 
 define:booting
+define:nimcore
+define:nimIncremental
 #import:"$projectpath/testability"
 
 @if windows:
@@ -13,6 +15,5 @@ define:booting
 
 define:useStdoutAsStdmsg
 
-cs:partial
 #define:useNodeIds
 #gc:markAndSweep
diff --git a/compiler/nim.nim b/compiler/nim.nim
index d3e00017f..90049bdfb 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -20,8 +20,8 @@ when defined(i386) and defined(windows) and defined(vcc):
 
 import
   commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
-  extccomp, strutils, os, osproc, platform, main, parseopt, service,
-  nodejs, scriptconfig, idents, modulegraphs, configuration
+  extccomp, strutils, os, osproc, platform, main, parseopt,
+  nodejs, scriptconfig, idents, modulegraphs, lineinfos
 
 when hasTinyCBackend:
   import tccgen
@@ -37,6 +37,25 @@ proc prependCurDir(f: string): string =
   else:
     result = f
 
+proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
+  var p = parseopt.initOptParser(cmd)
+  var argsCount = 0
+  while true:
+    parseopt.next(p)
+    case p.kind
+    of cmdEnd: break
+    of cmdLongoption, cmdShortOption:
+      if p.key == " ":
+        p.key = "-"
+        if processArgument(pass, p, argsCount, config): break
+      else:
+        processSwitch(pass, p, config)
+    of cmdArgument:
+      if processArgument(pass, p, argsCount, config): break
+  if pass == passCmd2:
+    if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
+      rawMessage(config, errGenerated, errArgsNeedRunOption)
+
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   condsyms.initDefines(conf.symbols)
   if paramCount() == 0:
@@ -60,7 +79,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
       conf.projectName = p.name
     else:
       conf.projectPath = canonicalizePath(conf, getCurrentDir())
-    loadConfigs(DefaultConfig, conf) # load all config files
+    loadConfigs(DefaultConfig, cache, conf) # load all config files
     let scriptFile = conf.projectFull.changeFileExt("nims")
     if fileExists(scriptFile):
       runNimScript(cache, scriptFile, freshDefines=false, conf)
@@ -75,7 +94,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     processCmdLine(passCmd2, "", conf)
     if conf.command == "":
       rawMessage(conf, errGenerated, "command missing")
-    mainCommand(newModuleGraph(conf), cache)
+    mainCommand(newModuleGraph(cache, conf))
     if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
     if conf.errorCounter == 0:
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 8305b01f5..9a23535bf 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -10,7 +10,7 @@
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
 import parseutils, strutils, strtabs, os, options, msgs, sequtils,
-  configuration
+  lineinfos
 
 proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
   if not conf.searchPaths.contains(path):
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 6cb5bab0f..1bc2c27e3 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -11,7 +11,7 @@
 
 import
   llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
-  options, idents, wordrecg, strtabs, configuration
+  options, idents, wordrecg, strtabs, lineinfos
 
 # ---------------- configuration file parser -----------------------------
 # we use Nim's scanner here to save space and work
@@ -253,7 +253,3 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
       if not fileExists(projectConfig):
         projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
       readConfigFile(projectConfig, cache, conf)
-
-proc loadConfigs*(cfg: string; conf: ConfigRef) =
-  # for backwards compatibility only.
-  loadConfigs(cfg, newIdentCache(), conf)
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index ff91861d0..308560010 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2018 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,27 +9,112 @@
 
 ## exposes the Nim VM to clients.
 import
-  ast, modules, passes, passaux, condsyms,
-  options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents
-
-proc execute*(program: string) =
-  passes.gIncludeFile = includeModule
-  passes.gImportModule = importModule
-  initDefines()
-  loadConfigs(DefaultConfig)
-
-  initDefines()
-  defineSymbol("nimrodvm")
-  defineSymbol("nimscript")
-  when hasFFI: defineSymbol("nimffi")
-  registerPass(verbosePass)
-  registerPass(semPass)
-  registerPass(evalPass)
-
-  searchPaths.add options.libpath
-  var graph = newModuleGraph()
+  ast, astalgo, modules, passes, condsyms,
+  options, sem, semdata, llstream, vm, vmdef,
+  modulegraphs, idents, os
+
+type
+  Interpreter* = ref object ## Use Nim as an interpreter with this object
+    mainModule: PSym
+    graph: ModuleGraph
+    scriptName: string
+
+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:
+    yield s
+    s = nextIter(it, i.mainModule.tab)
+
+proc selectUniqueSymbol*(i: Interpreter; name: string;
+                         symKinds: set[TSymKind] = {skLet, skVar}): PSym =
+  ## Can be used to access a unique symbol of ``name`` and
+  ## the given ``symKinds`` filter.
+  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)
+  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)
+
+proc selectRoutine*(i: Interpreter; name: string): PSym =
+  ## Selects a declared rountine (proc/func/etc) from the main module.
+  ## The routine needs to have the export marker ``*``. The only matching
+  ## routine is returned and ``nil`` if it is overloaded.
+  result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
+                                        skMethod, skProc, skConverter})
+
+proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode =
+  assert i != nil
+  result = vm.execProc(PCtx i.graph.vm, routine, args)
+
+proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
+  result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
+
+proc implementRoutine*(i: Interpreter; pkg, module, name: string;
+                       impl: proc (a: VmArgs) {.closure, gcsafe.}) =
+  assert i != nil
+  let vm = PCtx(i.graph.vm)
+  vm.registerCallback(pkg & "." & module & "." & name, impl)
+
+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)
+  i.mainModule.ast = nil
+
+  let s = if scriptStream != nil: scriptStream
+          else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
+  processModule(i.graph, i.mainModule, s, nil, i.graph.cache)
+
+proc findNimStdLib*(): string =
+  ## Tries to find a path to a valid "system.nim" file.
+  ## Returns "" on failure.
+  try:
+    let nimexe = os.findExe("nim")
+    if nimexe.len == 0: return ""
+    result = nimexe.splitPath()[0] /../ "lib"
+    if not fileExists(result / "system.nim"):
+      when defined(unix):
+        result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
+        if not fileExists(result / "system.nim"): return ""
+  except OSError, ValueError:
+    return ""
+
+proc createInterpreter*(scriptName: string;
+                        searchPaths: openArray[string];
+                        flags: TSandboxFlags = {}): Interpreter =
+  var conf = newConfigRef()
   var cache = newIdentCache()
-  var m = makeStdinModule(graph)
+  var graph = newModuleGraph(cache, conf)
+  connectCallbacks(graph)
+  initDefines(conf.symbols)
+  defineSymbol(conf.symbols, "nimscript")
+  defineSymbol(conf.symbols, "nimconfig")
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
+
+  for p in searchPaths:
+    conf.searchPaths.add(p)
+    if conf.libpath.len == 0: conf.libpath = p
+
+  var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  compileSystemModule(graph,cache)
-  processModule(graph,m, llStreamOpen(program), nil, cache)
+  var vm = newCtx(m, cache, graph)
+  vm.mode = emRepl
+  vm.features = flags
+  graph.vm = vm
+  graph.compileSystemModule(cache)
+  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
+
+proc destroyInterpreter*(i: Interpreter) =
+  ## destructor.
+  discard "currently nothing to do."
diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim
index 96429ad53..f3fff781f 100644
--- a/compiler/nimfix/pretty.nim
+++ b/compiler/nimfix/pretty.nim
@@ -14,7 +14,7 @@ import
   strutils, os, intsets, strtabs
 
 import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents,
-  configuration]
+  lineinfos]
 import prettybase
 
 type
@@ -27,19 +27,19 @@ var
 
 proc overwriteFiles*(conf: ConfigRef) =
   let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
-  for i in 0 .. high(gSourceFiles):
-    if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
-        (not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex):
-      let newFile = if gOverWrite: gSourceFiles[i].fullpath
-                    else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
+  for i in 0 .. high(conf.m.fileInfos):
+    if conf.m.fileInfos[i].dirty and
+        (not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx):
+      let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
+                    else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
       try:
         var f = open(newFile, fmWrite)
-        for line in gSourceFiles[i].lines:
+        for line in conf.m.fileInfos[i].lines:
           if doStrip:
             f.write line.strip(leading = false, trailing = true)
           else:
             f.write line
-          f.write(gSourceFiles[i].newline)
+          f.write(conf.m.fileInfos[i], "\L")
         f.close
       except IOError:
         rawMessage(conf, errGenerated, "cannot open file: " & newFile)
@@ -93,10 +93,8 @@ proc beautifyName(s: string, k: TSymKind): string =
       result.add s[i]
     inc i
 
-proc replaceInFile(info: TLineInfo; newName: string) =
-  loadFile(info)
-
-  let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1]
+proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) =
+  let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1]
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
@@ -108,35 +106,35 @@ proc replaceInFile(info: TLineInfo; newName: string) =
   if differ(line, first, last, newName):
     # last-first+1 != newName.len or
     var x = line.substr(0, first-1) & newName & line.substr(last+1)
-    system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
-    gSourceFiles[info.fileIndex.int].dirty = true
+    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x)
+    conf.m.fileInfos[info.fileIndex.int].dirty = true
 
-proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
+proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
   let beau = beautifyName(s, k)
   if s != beau:
     if gStyleCheck == StyleCheck.Auto:
-      sym.name = getIdent(beau)
-      replaceInFile(info, beau)
+      sym.name = getIdent(cache, beau)
+      replaceInFile(conf, info, beau)
     else:
       message(conf, info, hintName, beau)
 
-proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
+proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) =
   # operators stay as they are:
   if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
   if k in {skType, skGenericParam} and sfAnon in s.flags: return
   if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
-    checkStyle(conf, info, s.name.s, k, s)
+    checkStyle(conf, cache, info, s.name.s, k, s)
 
 template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
   when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k)
+    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, cache, info, s, k)
 
 template styleCheckDef*(info: TLineInfo; s: PSym) =
   styleCheckDef(info, s, s.kind)
 template styleCheckDef*(s: PSym) =
   styleCheckDef(s.info, s, s.kind)
 
-proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
+proc styleCheckUseImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym) =
   if info.fileIndex.int < 0: return
   # we simply convert it to what it looks like in the definition
   # for consistency
@@ -147,9 +145,9 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
   if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
   let newName = s.name.s
 
-  replaceInFile(info, newName)
+  replaceInFile(conf, info, newName)
   #if newName == "File": writeStackTrace()
 
 template styleCheckUse*(info: TLineInfo; s: PSym) =
   when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)
+    if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, cache, info, s)
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index c32dbe623..d1ad41a6e 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -8,48 +8,9 @@
 #
 
 import strutils, lexbase, streams
-import ".." / [ast, msgs, idents]
+import ".." / [ast, msgs, lineinfos, idents, options]
 from os import splitFile
 
-type
-  TSourceFile* = object
-    lines*: seq[string]
-    dirty*, isNimfixFile*: bool
-    fullpath*, newline*: string
-    fileIdx*: FileIndex
-
-var
-  gSourceFiles*: seq[TSourceFile] = @[]
-
-proc loadFile*(info: TLineInfo) =
-  let i = info.fileIndex.int
-  if i >= gSourceFiles.len:
-    gSourceFiles.setLen(i+1)
-  if gSourceFiles[i].lines.isNil:
-    gSourceFiles[i].fileIdx = info.fileIndex
-    gSourceFiles[i].lines = @[]
-    let path = info.toFullPath
-    gSourceFiles[i].fullpath = path
-    gSourceFiles[i].isNimfixFile = path.splitFile.ext == ".nimfix"
-    # we want to die here for IOError:
-    for line in lines(path):
-      gSourceFiles[i].lines.add(line)
-    # extract line ending of the file:
-    var lex: BaseLexer
-    open(lex, newFileStream(path, fmRead))
-    var pos = lex.bufpos
-    while true:
-      case lex.buf[pos]
-      of '\c':
-        gSourceFiles[i].newline = "\c\L"
-        break
-      of '\L', '\0':
-        gSourceFiles[i].newline = "\L"
-        break
-      else: discard
-      inc pos
-    close(lex)
-
 const
   Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
 
@@ -61,10 +22,8 @@ proc differ*(line: string, a, b: int, x: string): bool =
   let y = line[a..b]
   result = cmpIgnoreStyle(y, x) == 0 and y != x
 
-proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
-  loadFile(info)
-
-  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
+proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
+  let line = sourceLine(conf, info)
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
@@ -75,20 +34,18 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
   let last = first+identLen(line, first)-1
   if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
     var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
-    system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
-    gSourceFiles[info.fileIndex.int32].dirty = true
+    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
+    conf.m.fileInfos[info.fileIndex.int32].dirty = true
     #if newSym.s == "File": writeStackTrace()
 
-proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
-  replaceDeprecated(info, oldSym.name, newSym.name)
-
-proc replaceComment*(info: TLineInfo) =
-  loadFile(info)
+proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
+  replaceDeprecated(conf, info, oldSym.name, newSym.name)
 
-  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
+proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
+  let line = sourceLine(conf, info)
   var first = info.col.int
   if line[first] != '#': inc first
 
   var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
-  system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
-  gSourceFiles[info.fileIndex.int32].dirty = true
+  system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
+  conf.m.fileInfos[info.fileIndex.int32].dirty = true
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index 6cb675ed8..b00353e20 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -10,7 +10,8 @@
 # this unit handles Nim sets; it implements symbolic sets
 
 import
-  ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
+  ast, astalgo, trees, nversion, lineinfos, platform, bitsets, types, renderer,
+  options
 
 proc inSet*(s: PNode, elem: PNode): bool =
   assert s.kind == nkCurly
@@ -58,10 +59,10 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
         return true
   result = false
 
-proc toBitSet*(s: PNode, b: var TBitSet) =
+proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) =
   var first, j: BiggestInt
-  first = firstOrd(s.typ.sons[0])
-  bitSetInit(b, int(getSize(s.typ)))
+  first = firstOrd(conf, s.typ.sons[0])
+  bitSetInit(b, int(getSize(conf, s.typ)))
   for i in countup(0, sonsLen(s) - 1):
     if s.sons[i].kind == nkRange:
       j = getOrdValue(s.sons[i].sons[0])
@@ -71,13 +72,13 @@ proc toBitSet*(s: PNode, b: var TBitSet) =
     else:
       bitSetIncl(b, getOrdValue(s.sons[i]) - first)
 
-proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
+proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
   var
     a, b, e, first: BiggestInt # a, b are interval borders
     elemType: PType
     n: PNode
   elemType = settype.sons[0]
-  first = firstOrd(elemType)
+  first = firstOrd(conf, elemType)
   result = newNodeI(nkCurly, info)
   result.typ = settype
   result.info = info
@@ -107,42 +108,42 @@ proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
 
 template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   op(x, y)
-  result = toTreeSet(x, a.typ, a.info)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc unionSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
-proc diffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
-proc intersectSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
-proc symdiffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
+proc unionSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
+proc diffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
+proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
+proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
 
-proc containsSets*(a, b: PNode): bool =
+proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   result = bitSetContains(x, y)
 
-proc equalSets*(a, b: PNode): bool =
+proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   result = bitSetEquals(x, y)
 
-proc complement*(a: PNode): PNode =
+proc complement*(conf: ConfigRef; a: PNode): PNode =
   var x: TBitSet
-  toBitSet(a, x)
+  toBitSet(conf, a, x)
   for i in countup(0, high(x)): x[i] = not x[i]
-  result = toTreeSet(x, a.typ, a.info)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc deduplicate*(a: PNode): PNode =
+proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
   var x: TBitSet
-  toBitSet(a, x)
-  result = toTreeSet(x, a.typ, a.info)
+  toBitSet(conf, a, x)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc cardSet*(a: PNode): BiggestInt =
+proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
   var x: TBitSet
-  toBitSet(a, x)
+  toBitSet(conf, a, x)
   result = bitSetCard(x)
 
 proc setHasRange*(s: PNode): bool =
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index caa818d79..4b8cf7100 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -15,6 +15,6 @@ const
   VersionAsString* = system.NimVersion
   RodFileVersion* = "1223"       # modify this if the rod-format changes!
 
-  NimCompilerApiVersion* = 1 ## Check for the existance of this before accessing it
+  NimCompilerApiVersion* = 2 ## Check for the existance of this before accessing it
                              ## as older versions of the compiler API do not
                              ## declare this.
diff --git a/compiler/options.nim b/compiler/options.nim
index 9779f2020..7ee8f8d4c 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,8 @@
 #
 
 import
-  os, strutils, strtabs, osproc, sets, configuration, platform
+  os, strutils, strtabs, osproc, sets, lineinfos, platform,
+  prefixmatches
 
 from terminal import isatty
 
@@ -73,6 +74,7 @@ type                          # please make sure we have under 32 options
     optNoCppExceptions        # use C exception handling even with CPP
     optExcessiveStackTrace    # fully qualified module filenames
     optWholeProject           # for 'doc2': output any dependency
+    optMixedMode              # true if some module triggered C++ codegen
     optListFullPaths
     optNoNimblePath
     optDynlibOverrideAll
@@ -119,22 +121,62 @@ type
     notnil
 
   SymbolFilesOption* = enum
-    disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
-
-  ConfigRef* = ref object ## eventually all global configuration should be moved here
+    disabledSf, writeOnlySf, readOnlySf, v2Sf
+
+  TSystemCC* = enum
+    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
+    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
+
+  CfileFlag* {.pure.} = enum
+    Cached,    ## no need to recompile this time
+    External   ## file was introduced via .compile pragma
+
+  Cfile* = object
+    cname*, obj*: string
+    flags*: set[CFileFlag]
+  CfileList* = seq[Cfile]
+
+  Suggest* = ref object
+    section*: IdeCmd
+    qualifiedPath*: seq[string]
+    name*: ptr string         # not used beyond sorting purposes; name is also
+                              # part of 'qualifiedPath'
+    filePath*: string
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    doc*: string           # Not escaped (yet)
+    forth*: string               # type
+    quality*: range[0..100]   # matching quality
+    isGlobal*: bool # is a global variable
+    contextFits*: bool # type/non-type context matches
+    prefix*: PrefixMatch
+    symkind*: byte
+    scope*, localUsages*, globalUsages*: int # more usages is better
+    tokenLen*: int
+    version*: int
+  Suggestions* = seq[Suggest]
+
+  ConfigRef* = ref object ## every global configuration
+                          ## fields marked with '*' are subject to
+                          ## the incremental compilation mechanisms
+                          ## (+) means "part of the dependency"
+    target*: Target       # (+)
     linesCompiled*: int  # all lines that have been compiled
-    options*: TOptions
-    globalOptions*: TGlobalOptions
+    options*: TOptions    # (+)
+    globalOptions*: TGlobalOptions # (+)
+    m*: MsgConfig
+    evalTemplateCounter*: int
+    evalMacroCounter*: int
     exitcode*: int8
     cmd*: TCommands  # the command
-    selectedGC*: TGCMode       # the selected GC
+    selectedGC*: TGCMode       # the selected GC (+)
     verbosity*: int            # how verbose the compiler is
     numberOfProcessors*: int   # number of processors
     evalExpr*: string          # expression for idetools --eval
     lastCmdTime*: float        # when caas is enabled, we measure each command
     symbolFiles*: SymbolFilesOption
 
-    cppDefines*: HashSet[string]
+    cppDefines*: HashSet[string] # (*)
     headerFile*: string
     features*: set[Feature]
     arguments*: string ## the arguments to be passed to the program that
@@ -142,11 +184,13 @@ type
     helpWritten*: bool
     ideCmd*: IdeCmd
     oldNewlines*: bool
+    cCompiler*: TSystemCC
     enableNotes*: TNoteKinds
     disableNotes*: TNoteKinds
     foreignPackageNotes*: TNoteKinds
     notes*: TNoteKinds
     mainPackageNotes*: TNoteKinds
+    mainPackageId*: int
     errorCounter*: int
     hintCounter*: int
     warnCounter*: int
@@ -164,7 +208,7 @@ type
     projectPath*: string # holds a path like /home/alice/projects/nim/compiler/
     projectFull*: string # projectPath/projectName
     projectIsStdin*: bool # whether we're compiling from stdin
-    projectMainIdx*: int32 # the canonical path id of the main module
+    projectMainIdx*: FileIndex # the canonical path id of the main module
     command*: string # the main command (e.g. cc, check, scan, etc)
     commandArgs*: seq[string] # any arguments after the main command
     keepComments*: bool # whether the parser needs to keep comments
@@ -173,6 +217,40 @@ type
     docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
     # The string uses the formatting variables `path` and `line`.
 
+     # the used compiler
+    cIncludes*: seq[string]  # directories to search for included files
+    cLibs*: seq[string]      # directories to search for lib files
+    cLinkedLibs*: seq[string]  # libraries to link
+
+    externalToLink*: seq[string]  # files to link in addition to the file
+                                  # we compiled (*)
+    linkOptionsCmd*: string
+    compileOptionsCmd*: seq[string]
+    linkOptions*: string          # (*)
+    compileOptions*: string       # (*)
+    ccompilerpath*: string
+    toCompile*: CfileList         # (*)
+    suggestionResultHook*: proc (result: Suggest) {.closure.}
+    suggestVersion*: int
+    suggestMaxResults*: int
+    lastLineInfo*: TLineInfo
+    writelnHook*: proc (output: string) {.closure.}
+    structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
+                                severity: Severity) {.closure.}
+
+template depConfigFields*(fn) {.dirty.} =
+  fn(target)
+  fn(options)
+  fn(globalOptions)
+  fn(selectedGC)
+
+template serializeConfigFields(fn) {.dirty.} =
+  fn(cppDefines)
+  fn(externalToLink)
+  fn(linkOptions)
+  fn(compileOptions)
+  fn(toCompile)
+
 const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
 
 const
@@ -195,9 +273,11 @@ template newPackageCache*(): untyped =
 proc newConfigRef*(): ConfigRef =
   result = ConfigRef(
     selectedGC: gcRefc,
+    cCompiler: ccGcc,
     verbosity: 1,
     options: DefaultOptions,
     globalOptions: DefaultGlobalOptions,
+    m: initMsgConfig(),
     evalExpr: "",
     cppDefines: initSet[string](),
     headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
@@ -215,15 +295,28 @@ proc newConfigRef*(): ConfigRef =
     projectPath: "", # holds a path like /home/alice/projects/nim/compiler/
     projectFull: "", # projectPath/projectName
     projectIsStdin: false, # whether we're compiling from stdin
-    projectMainIdx: 0'i32, # the canonical path id of the main module
+    projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
     command: "", # the main command (e.g. cc, check, scan, etc)
     commandArgs: @[], # any arguments after the main command
     keepComments: true, # whether the parser needs to keep comments
     implicitImports: @[], # modules that are to be implicitly imported
     implicitIncludes: @[], # modules that are to be implicitly included
     docSeeSrcUrl: "",
-    arguments: ""
+    cIncludes: @[],   # directories to search for included files
+    cLibs: @[],       # directories to search for lib files
+    cLinkedLibs: @[],  # libraries to link
+
+    externalToLink: @[],
+    linkOptionsCmd: "",
+    compileOptionsCmd: @[],
+    linkOptions: "",
+    compileOptions: "",
+    ccompilerpath: "",
+    toCompile: @[],
+    arguments: "",
+    suggestMaxResults: 10_000
   )
+  setTargetFromSystem(result.target)
   # enable colors by default on terminals
   if terminal.isatty(stderr):
     incl(result.globalOptions, optUseColors)
@@ -245,39 +338,39 @@ proc cppDefine*(c: ConfigRef; define: string) =
 proc isDefined*(conf: ConfigRef; symbol: string): bool =
   if conf.symbols.hasKey(symbol):
     result = conf.symbols[symbol] != "false"
-  elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
+  elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
     result = true
-  elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
+  elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
     result = true
   else:
     case symbol.normalize
-    of "x86": result = targetCPU == cpuI386
-    of "itanium": result = targetCPU == cpuIa64
-    of "x8664": result = targetCPU == cpuAmd64
+    of "x86": result = conf.target.targetCPU == cpuI386
+    of "itanium": result = conf.target.targetCPU == cpuIa64
+    of "x8664": result = conf.target.targetCPU == cpuAmd64
     of "posix", "unix":
-      result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
+      result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
                             osQnx, osAtari, osAix,
                             osHaiku, osVxWorks, osSolaris, osNetbsd,
                             osFreebsd, osOpenbsd, osDragonfly, osMacosx,
                             osAndroid}
     of "linux":
-      result = targetOS in {osLinux, osAndroid}
+      result = conf.target.targetOS in {osLinux, osAndroid}
     of "bsd":
-      result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
+      result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
     of "emulatedthreadvars":
-      result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
-    of "msdos": result = targetOS == osDos
-    of "mswindows", "win32": result = targetOS == osWindows
-    of "macintosh": result = targetOS in {osMacos, osMacosx}
-    of "sunos": result = targetOS == osSolaris
-    of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
-    of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
-    of "cpu8": result = CPU[targetCPU].bit == 8
-    of "cpu16": result = CPU[targetCPU].bit == 16
-    of "cpu32": result = CPU[targetCPU].bit == 32
-    of "cpu64": result = CPU[targetCPU].bit == 64
+      result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
+    of "msdos": result = conf.target.targetOS == osDos
+    of "mswindows", "win32": result = conf.target.targetOS == osWindows
+    of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
+    of "sunos": result = conf.target.targetOS == osSolaris
+    of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
+    of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
+    of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
+    of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
+    of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
+    of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
     of "nimrawsetjmp":
-      result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
+      result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
                             osDragonfly, osMacosx}
     else: discard
 
@@ -285,7 +378,7 @@ proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc,
 proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
 
 template compilationCachePresent*(conf: ConfigRef): untyped =
-  conf.symbolFiles in {enabledSf, writeOnlySf}
+  conf.symbolFiles in {v2Sf, writeOnlySf}
 #  {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
 
 template optPreserveOrigSource*(conf: ConfigRef): untyped =
diff --git a/compiler/parser.nim b/compiler/parser.nim
index fbc57ebb6..aedee8538 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -27,7 +27,7 @@ when isMainModule:
   outp.close
 
 import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration
+  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos
 
 type
   TParser* = object            # A TParser object represents a file that
@@ -40,6 +40,7 @@ type
     tok*: TToken               # The current token
     inPragma*: int             # Pragma level
     inSemiStmtList*: int
+    emptyNode: PNode
 
   SymbolMode = enum
     smNormal, smAllowNil, smAfterDot
@@ -93,6 +94,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
+  p.emptyNode = newNode(nkEmpty)
 
 proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef;
@@ -131,7 +133,7 @@ proc rawSkipComment(p: var TParser, node: PNode) =
       if node.comment == nil: node.comment = ""
       when defined(nimpretty):
         if p.tok.commentOffsetB > p.tok.commentOffsetA:
-          add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
+          add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
         else:
           add node.comment, p.tok.literal
       else:
@@ -333,7 +335,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
       getTok(p)
     else:
       parMessage(p, errIdentifierExpected, p.tok)
-      result = ast.emptyNode
+      result = p.emptyNode
   of tkAccent:
     result = newNodeP(nkAccQuoted, p)
     getTok(p)
@@ -364,7 +366,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     # But: this really sucks for idetools and keywords, so we don't do it
     # if it is a keyword:
     #if not isKeyword(p.tok.tokType): getTok(p)
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc colonOrEquals(p: var TParser, a: PNode): PNode =
   if p.tok.tokType == tkColon:
@@ -703,7 +705,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
   else:
     parMessage(p, errExprExpected, p.tok)
     getTok(p)  # we must consume a token here to prevend endless loops!
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc namedParams(p: var TParser, callee: PNode,
                  kind: TNodeKind, endTok: TTokType): PNode =
@@ -1015,7 +1017,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   #| paramListColon = paramList? (':' optInd typeDesc)?
   var a: PNode
   result = newNodeP(nkFormalParams, p)
-  addSon(result, ast.emptyNode) # return type
+  addSon(result, p.emptyNode) # return type
   let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
   if hasParLe:
     getTok(p)
@@ -1047,13 +1049,13 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
     result.sons[0] = parseTypeDesc(p)
   elif not retColon and not hasParle:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc optPragmas(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
     result = parsePragma(p)
   else:
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   #| doBlock = 'do' paramListArrow pragmas? colcom stmt
@@ -1062,7 +1064,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   colcom(p, result)
   result = parseStmt(p)
   if params.kind != nkEmpty:
-    result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
+    result = newProcNode(nkDo, info,
+      body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
 
 proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
@@ -1075,9 +1079,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   if p.tok.tokType == tkEquals and isExpr:
     getTok(p)
     skipComment(p, result)
-    result = newProcNode(kind, info, parseStmt(p),
-                         params = params,
-                         pragmas = pragmas)
+    result = newProcNode(kind, info, body = parseStmt(p),
+      params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
   else:
     result = newNodeI(nkProcTy, info)
     if hasSignature:
@@ -1244,8 +1248,8 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
   if p.tok.indent >= 0: return
 
   var
-    openingParams = emptyNode
-    openingPragmas = emptyNode
+    openingParams = p.emptyNode
+    openingPragmas = p.emptyNode
 
   if p.tok.tokType == tkDo:
     getTok(p)
@@ -1264,8 +1268,12 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
 
       stmtList.flags.incl nfBlockArg
       if openingParams.kind != nkEmpty:
-        result.add newProcNode(nkDo, stmtList.info, stmtList,
-                               params = openingParams, pragmas = openingPragmas)
+        result.add newProcNode(nkDo, stmtList.info, body = stmtList,
+                               params = openingParams,
+                               name = p.emptyNode, pattern = p.emptyNode,
+                               genericParams = p.emptyNode,
+                               pragmas = openingPragmas,
+                               exceptions = p.emptyNode)
       else:
         result.add stmtList
 
@@ -1424,10 +1432,10 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
   getTok(p)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
     # NL terminates:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   else:
     var e = parseExpr(p)
     e = postExprBlocks(p, e)
@@ -1568,7 +1576,7 @@ proc parseBlock(p: var TParser): PNode =
   #| blockExpr = 'block' symbol? colcom stmt
   result = newNodeP(nkBlockStmt, p)
   getTokNoInd(p)
-  if p.tok.tokType == tkColon: addSon(result, ast.emptyNode)
+  if p.tok.tokType == tkColon: addSon(result, p.emptyNode)
   else: addSon(result, parseSymbol(p))
   colcom(p, result)
   addSon(result, parseStmt(p))
@@ -1586,7 +1594,7 @@ proc parseAsm(p: var TParser): PNode =
   result = newNodeP(nkAsmStmt, p)
   getTokNoInd(p)
   if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   case p.tok.tokType
   of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
   of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
@@ -1594,7 +1602,7 @@ proc parseAsm(p: var TParser): PNode =
                             newStrNodeP(nkTripleStrLit, p.tok.literal, p))
   else:
     parMessage(p, "the 'asm' statement takes a string literal")
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
     return
   getTok(p)
 
@@ -1625,13 +1633,13 @@ proc parseGenericParam(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
 
 proc parseGenericParamList(p: var TParser): PNode =
   #| genericParamList = '[' optInd
@@ -1667,22 +1675,22 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
   optInd(p, result)
   addSon(result, identVis(p))
   if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   if p.tok.tokType == tkBracketLe and p.validInd:
     result.add(p.parseGenericParamList)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   addSon(result, p.parseParamList)
   if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   # empty exception tracking:
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals and p.validInd:
     getTok(p)
     skipComment(p, result)
     addSon(result, parseStmt(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)
 
 proc newCommentStmt(p: var TParser): PNode =
@@ -1732,7 +1740,7 @@ proc parseConstant(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseTypeDesc(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   eat(p, tkEquals)
   optInd(p, result)
   addSon(result, parseExpr(p))
@@ -1742,7 +1750,7 @@ proc parseEnum(p: var TParser): PNode =
   #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
   getTok(p)
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   optInd(p, result)
   flexComment(p, result)
   # progress guaranteed
@@ -1813,7 +1821,7 @@ proc parseObjectCase(p: var TParser): PNode =
   addSon(a, identWithPragma(p))
   eat(p, tkColon)
   addSon(a, parseTypeDesc(p))
-  addSon(a, ast.emptyNode)
+  addSon(a, p.emptyNode)
   addSon(result, a)
   if p.tok.tokType == tkColon: getTok(p)
   flexComment(p, result)
@@ -1872,7 +1880,7 @@ proc parseObjectPart(p: var TParser): PNode =
       result = newNodeP(nkNilLit, p)
       getTok(p)
     else:
-      result = ast.emptyNode
+      result = p.emptyNode
 
 proc parseObject(p: var TParser): PNode =
   #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
@@ -1881,19 +1889,19 @@ proc parseObject(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
     addSon(a, parseTypeDesc(p))
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
     return
   addSon(result, parseObjectPart(p))
 
@@ -1928,7 +1936,7 @@ proc parseTypeClass(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
@@ -1939,12 +1947,12 @@ proc parseTypeClass(p: var TParser): PNode =
       getTok(p)
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
   else:
     addSon(result, parseStmt(p))
 
@@ -1957,14 +1965,14 @@ proc parseTypeDef(p: var TParser): PNode =
   if p.tok.tokType == tkBracketLe and p.validInd:
     addSon(result, parseGenericParamList(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
     addSon(result, parseTypeDefAux(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)    # special extension!
 
 proc parseVarTuple(p: var TParser): PNode =
@@ -1979,7 +1987,7 @@ proc parseVarTuple(p: var TParser): PNode =
     if p.tok.tokType != tkComma: break
     getTok(p)
     skipComment(p, a)
-  addSon(result, ast.emptyNode)         # no type desc
+  addSon(result, p.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
   eat(p, tkEquals)
@@ -2040,7 +2048,7 @@ proc simpleStmt(p: var TParser): PNode =
   of tkComment: result = newCommentStmt(p)
   else:
     if isExprStart(p): result = parseExprStmt(p)
-    else: result = ast.emptyNode
+    else: result = p.emptyNode
   if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
 
 proc complexOrSimpleStmt(p: var TParser): PNode =
@@ -2136,7 +2144,7 @@ proc parseStmt(p: var TParser): PNode =
     of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
        tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
       parMessage(p, "complex statement requires indentation")
-      result = ast.emptyNode
+      result = p.emptyNode
     else:
       if p.inSemiStmtList > 0:
         result = simpleStmt(p)
@@ -2173,7 +2181,7 @@ proc parseAll(p: var TParser): PNode =
 proc parseTopLevelStmt(p: var TParser): PNode =
   ## Implements an iterator which, when called repeatedly, returns the next
   ## top-level statement or emptyNode if end of stream.
-  result = ast.emptyNode
+  result = p.emptyNode
   # progress guaranteed
   while true:
     if p.tok.indent != 0:
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 568fb4c23..eabce8822 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,7 +10,7 @@
 ## implements some little helper passes
 
 import
-  strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration
+  strutils, ast, astalgo, passes, idents, msgs, options, idgen, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -18,7 +18,7 @@ type
   VerboseRef = ref object of TPassContext
     config: ConfigRef
 
-proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
+proc verboseOpen(graph: ModuleGraph; s: PSym): PPassContext =
   #MessageOut('compiling ' + s.name.s);
   result = VerboseRef(config: graph.config)
   rawMessage(graph.config, hintProcessing, s.name.s)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 8f9f57f3d..45c726f2a 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,24 +13,20 @@
 import
   strutils, options, ast, astalgo, llstream, msgs, platform, os,
   condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
-  nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod,
-  configuration
+  nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
+  lineinfos
 
 
 type
   TPassContext* = object of RootObj # the pass's context
-    rd*: PRodReader  # != nil if created by "openCached"
 
   PPassContext* = ref TPassContext
 
-  TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.}
-  TPassOpenCached* =
-    proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.}
+  TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.}
   TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
   TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
 
-  TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
-                 process: TPassProcess, close: TPassClose,
+  TPass* = tuple[open: TPassOpen, process: TPassProcess, close: TPassClose,
                  isFrontend: bool]
 
   TPassData* = tuple[input: PNode, closeOutput: PNode]
@@ -41,23 +37,14 @@ type
 # This mechanism used to be used for the instantiation of generics.
 
 proc makePass*(open: TPassOpen = nil,
-               openCached: TPassOpenCached = nil,
                process: TPassProcess = nil,
                close: TPassClose = nil,
                isFrontend = false): TPass =
   result.open = open
-  result.openCached = openCached
   result.close = close
   result.process = process
   result.isFrontend = isFrontend
 
-# the semantic checker needs these:
-var
-  gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.}
-  gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.}
-
-# implementation
-
 proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
   # can be used by codegen passes to determine whether they should do
   # something with `n`. Currently, this ignores `n` and uses the global
@@ -74,44 +61,34 @@ var
   gPasses: array[0..maxPasses - 1, TPass]
   gPassesLen*: int
 
-proc clearPasses* =
+proc clearPasses*(g: ModuleGraph) =
   gPassesLen = 0
 
-proc registerPass*(p: TPass) =
+proc registerPass*(g: ModuleGraph; p: TPass) =
   gPasses[gPassesLen] = p
   inc(gPassesLen)
 
-proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache;
+proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
                 m: TPassData): TPassData =
-  var c = p.open(g, module, cache)
+  var c = p.open(g, module)
   result.input = p.process(c, m.input)
   result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
                        else: m.closeOutput
 
 proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
-                  cache: IdentCache; passes: TPasses) =
+                  passes: TPasses) =
   var passdata: TPassData
   passdata.input = nodes
   for pass in passes:
-    passdata = carryPass(g, pass, module, cache, passdata)
+    passdata = carryPass(g, pass, module, passdata)
 
 proc openPasses(g: ModuleGraph; a: var TPassContextArray;
-                module: PSym; cache: IdentCache) =
+                module: PSym) =
   for i in countup(0, gPassesLen - 1):
     if not isNil(gPasses[i].open):
-      a[i] = gPasses[i].open(g, module, cache)
+      a[i] = gPasses[i].open(g, module)
     else: a[i] = nil
 
-proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym,
-                      rd: PRodReader) =
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached):
-      a[i] = gPasses[i].openCached(g, module, rd)
-      if a[i] != nil:
-        a[i].rd = rd
-    else:
-      a[i] = nil
-
 proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
   for i in countup(0, gPassesLen - 1):
@@ -127,19 +104,6 @@ proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
       if isNil(m): return false
   result = true
 
-proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
-  # this implements the code transformation pipeline
-  var m = n
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
-
-proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
-  var m: PNode = nil
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
-      m = gPasses[i].close(graph, a[i], m)
-    a[i] = nil                # free the memory here
-
 proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
   let fullPath = findModule(conf, module, relativeTo)
   if fullPath.len == 0:
@@ -151,7 +115,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi
                       a: var TPassContextArray; m: PSym) =
   # XXX fixme this should actually be relative to the config file!
   let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1)
-  let relativeTo = m.info.toFullPath
+  let relativeTo = toFullPath(conf, m.info)
   for module in items(implicits):
     # implicit imports should not lead to a module importing itself
     if m.position != resolveMod(conf, module, relativeTo).int32:
@@ -161,8 +125,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi
       importStmt.addSon str
       if not processTopLevelStmt(importStmt, a): break
 
-proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
-                    rd: PRodReader; cache: IdentCache): bool {.discardable.} =
+proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
   var
     p: TParsers
@@ -173,24 +136,17 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     # new module caching mechanism:
     for i in 0..<gPassesLen:
       if not isNil(gPasses[i].open) and not gPasses[i].isFrontend:
-        a[i] = gPasses[i].open(graph, module, cache)
+        a[i] = gPasses[i].open(graph, module)
       else:
         a[i] = nil
 
-    var stmtIndex = 0
-    var doContinue = true
-    while doContinue:
-      let n = loadNode(module, stmtIndex)
-      if n == nil or graph.stopCompile(): break
-      #if n.kind == nkImportStmt:
-      #  echo "yes and it's ", n
-      inc stmtIndex
+    if not graph.stopCompile():
+      let n = loadNode(graph, module)
       var m = n
       for i in 0..<gPassesLen:
         if not isNil(gPasses[i].process) and not gPasses[i].isFrontend:
           m = gPasses[i].process(a[i], m)
           if isNil(m):
-            doContinue = false
             break
 
     var m: PNode = nil
@@ -198,10 +154,10 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
       if not isNil(gPasses[i].close) and not gPasses[i].isFrontend:
         m = gPasses[i].close(graph, a[i], m)
       a[i] = nil
-  elif rd == nil:
-    openPasses(graph, a, module, cache)
+  else:
+    openPasses(graph, a, module)
     if stream == nil:
-      let filename = fileIdx.toFullPathConsiderDirty
+      let filename = toFullPathConsiderDirty(graph.config, fileIdx)
       s = llStreamOpen(filename, fmRead)
       if s == nil:
         rawMessage(graph.config, errCannotOpenFile, filename)
@@ -209,7 +165,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     else:
       s = stream
     while true:
-      openParsers(p, fileIdx, s, cache, graph.config)
+      openParsers(p, fileIdx, s, graph.cache, graph.config)
 
       if sfSystemModule notin module.flags:
         # XXX what about caching? no processing then? what if I change the
@@ -232,7 +188,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
             if n.kind == nkEmpty: break
             sl.add n
           if sfReorder in module.flags:
-            sl = reorder(graph, sl, module, cache)
+            sl = reorder(graph, sl, module)
           discard processTopLevelStmt(sl, a)
           break
         elif not processTopLevelStmt(n, a): break
@@ -241,11 +197,4 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     closePasses(graph, a)
     # id synchronization point for more consistent code generation:
     idSynchronizationPoint(1000)
-  else:
-    openPassesCached(graph, a, module, rd)
-    var n = loadInitSection(rd)
-    for i in countup(0, sonsLen(n) - 1):
-      if graph.stopCompile(): break
-      processTopLevelStmtCached(n.sons[i], a)
-    closePassesCached(graph, a)
   result = true
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 8b3bf6b74..0db16f26c 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -22,7 +22,7 @@ type
     osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
     osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
     osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
-    osGenode, osJS, osNimrodVM, osStandalone
+    osGenode, osJS, osNimVM, osStandalone
 
 type
   TInfoOSProp* = enum
@@ -162,7 +162,7 @@ const
       pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".",
       exeExt: "", extSep: ".", props: {}),
-     (name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+     (name: "NimVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}),
      (name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
@@ -175,7 +175,7 @@ type
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
     cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
-    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64,
+    cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, cpuSparc64,
     cpuMips64, cpuMips64el, cpuRiscV64
 
 type
@@ -202,7 +202,7 @@ const
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
-    (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+    (name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
@@ -210,44 +210,40 @@ const
     (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
 
-var
-  targetCPU*, hostCPU*: TSystemCPU
-  targetOS*, hostOS*: TSystemOS
-
-proc nameToOS*(name: string): TSystemOS
-proc nameToCPU*(name: string): TSystemCPU
-
-var
-  intSize*: int
-  floatSize*: int
-  ptrSize*: int
-  tnl*: string                # target newline
-
-proc setTarget*(o: TSystemOS, c: TSystemCPU) =
+type
+  Target* = object
+    targetCPU*, hostCPU*: TSystemCPU
+    targetOS*, hostOS*: TSystemOS
+    intSize*: int
+    floatSize*: int
+    ptrSize*: int
+    tnl*: string                # target newline
+
+proc setTarget*(t: var Target; o: TSystemOS, c: TSystemCPU) =
   assert(c != cpuNone)
   assert(o != osNone)
   #echo "new Target: OS: ", o, " CPU: ", c
-  targetCPU = c
-  targetOS = o
-  intSize = CPU[c].intSize div 8
-  floatSize = CPU[c].floatSize div 8
-  ptrSize = CPU[c].bit div 8
-  tnl = OS[o].newLine
-
-proc nameToOS(name: string): TSystemOS =
+  t.targetCPU = c
+  t.targetOS = o
+  # assume no cross-compiling
+  t.hostCPU = c
+  t.hostOS = o
+  t.intSize = CPU[c].intSize div 8
+  t.floatSize = CPU[c].floatSize div 8
+  t.ptrSize = CPU[c].bit div 8
+  t.tnl = OS[o].newLine
+
+proc nameToOS*(name: string): TSystemOS =
   for i in countup(succ(osNone), high(TSystemOS)):
     if cmpIgnoreStyle(name, OS[i].name) == 0:
       return i
   result = osNone
 
-proc nameToCPU(name: string): TSystemCPU =
+proc nameToCPU*(name: string): TSystemCPU =
   for i in countup(succ(cpuNone), high(TSystemCPU)):
     if cmpIgnoreStyle(name, CPU[i].name) == 0:
       return i
   result = cpuNone
 
-hostCPU = nameToCPU(system.hostCPU)
-hostOS = nameToOS(system.hostOS)
-
-setTarget(hostOS, hostCPU) # assume no cross-compiling
-
+proc setTargetFromSystem*(t: var Target) =
+  t.setTarget(nameToOS(system.hostOS), nameToCPU(system.hostCPU))
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
index 5da623e49..7b5306f9c 100644
--- a/compiler/plugins/active.nim
+++ b/compiler/plugins/active.nim
@@ -10,4 +10,15 @@
 ## Include file that imports all plugins that are active.
 
 import
-  locals / locals, itersgen
+  "../compiler" / [pluginsupport, idents, ast], locals, itersgen
+
+const
+  plugins: array[2, Plugin] = [
+    ("stdlib", "system", "iterToProc", iterToProcImpl),
+    ("stdlib", "system", "locals", semLocals)
+  ]
+
+proc getPlugin*(ic: IdentCache; fn: PSym): Transformation =
+  for p in plugins:
+    if pluginMatches(ic, p, fn): return p.t
+  return nil
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index ebb65dd4a..440d2e081 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,11 +9,11 @@
 
 ## Plugin to transform an inline iterator into a data structure.
 
-import ".." / [pluginsupport, ast, astalgo,
+import ".." / [ast, astalgo,
   magicsys, lookups, semdata,
-  lambdalifting, rodread, msgs]
+  lambdalifting, msgs]
 
-proc iterToProcImpl(c: PContext, n: PNode): PNode =
+proc iterToProcImpl*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   let iter = n[1]
   if iter.kind != nkSym or iter.sym.kind != skIterator:
@@ -40,11 +40,9 @@ proc iterToProcImpl(c: PContext, n: PNode): PNode =
   prc.typ.rawAddSon t
   let orig = iter.sym.ast
   prc.ast = newProcNode(nkProcDef, n.info,
-                        name = newSymNode(prc),
-                        params = orig[paramsPos],
-                        pragmas = orig[pragmasPos],
-                        body = body)
+              body = body, params = orig[paramsPos], name = newSymNode(prc),
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode)
+
   prc.ast.add iter.sym.ast.sons[resultPos]
   addInterfaceDecl(c, prc)
-
-registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals.nim
index ff7f3be58..0048ff985 100644
--- a/compiler/plugins/locals/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -9,10 +9,10 @@
 
 ## The builtin 'system.locals' implemented as a plugin.
 
-import "../../" / [pluginsupport, ast, astalgo,
+import ".." / [pluginsupport, ast, astalgo,
   magicsys, lookups, semdata, lowerings]
 
-proc semLocals(c: PContext, n: PNode): PNode =
+proc semLocals*(c: PContext, n: PNode): PNode =
   var counter = 0
   var tupleType = newTypeS(tyTuple, c)
   result = newNodeIT(nkPar, n.info, tupleType)
@@ -39,5 +39,3 @@ proc semLocals(c: PContext, n: PNode): PNode =
         var a = newSymNode(it, result.info)
         if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
         result.add(a)
-
-registerPlugin("stdlib", "system", "locals", semLocals)
diff --git a/compiler/pluginsupport.nim b/compiler/pluginsupport.nim
index f67942c97..a44436f11 100644
--- a/compiler/pluginsupport.nim
+++ b/compiler/pluginsupport.nim
@@ -8,40 +8,26 @@
 #
 
 ## Plugin support for the Nim compiler. Right now plugins
-## need to be built with the compiler only: plugins using 
+## need to be built with the compiler only: plugins using
 ## DLLs or the FFI will not work.
 
 import ast, semdata, idents
 
 type
   Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
-  Plugin = ref object
-    fn, module, package: PIdent
+  Plugin* = tuple
+    package, module, fn: string
     t: Transformation
-    next: Plugin
 
-proc pluginMatches(p: Plugin; s: PSym): bool =
-  if s.name.id != p.fn.id:
+proc pluginMatches*(ic: IdentCache; p: Plugin; s: PSym): bool =
+  if s.name.id != ic.getIdent(p.fn).id:
     return false
   let module = s.skipGenericOwner
   if module == nil or module.kind != skModule or
-      module.name.id != p.module.id:
+      module.name.id != ic.getIdent(p.module).id:
     return false
   let package = module.owner
   if package == nil or package.kind != skPackage or
-      package.name.id != p.package.id:
+      package.name.id != ic.getIdent(p.package).id:
     return false
   return true
-
-var head: Plugin
-
-proc getPlugin*(fn: PSym): Transformation =
-  var it = head
-  while it != nil:
-    if pluginMatches(it, fn): return it.t
-    it = it.next
-
-proc registerPlugin*(package, module, fn: string; t: Transformation) =
-  let oldHead = head
-  head = Plugin(fn: getIdent(fn), module: getIdent(module),
-                 package: getIdent(package), t: t, next: oldHead)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index de98a5e42..815ec67d7 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -12,7 +12,7 @@
 import
   os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
   wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
-  rodread, types, lookups, configuration
+  types, lookups, lineinfos
 
 const
   FirstCallConv* = wNimcall
@@ -84,6 +84,13 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
 # implementation
 
+proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") =
+  var recorded = newNodeI(nkCommentStmt, n.info)
+  recorded.add newStrNode(key, n.info)
+  recorded.add newStrNode(val, n.info)
+  if val2.len > 0: recorded.add newStrNode(val2, n.info)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
 const
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
@@ -143,7 +150,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   if c.config.cmd == cmdCompileToC:
     let m = s.getModule()
     incl(m.flags, sfCompileToCpp)
-  extccomp.gMixedMode = true
+  incl c.config.globalOptions, optMixedMode
 
 proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   setExternName(c, s, extname, info)
@@ -227,7 +234,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
 
 proc processCallConv(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
-    var sw = whichKeyword(n.sons[1].ident)
+    let sw = whichKeyword(n.sons[1].ident)
     case sw
     of FirstCallConv..LastCallConv:
       c.optionStack[^1].defaultCC = wordToCallConv(sw)
@@ -404,7 +411,7 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
   var s = expectStrLit(c, n)
   if ext.len > 0 and splitFile(s).ext == "":
     s = addFileExt(s, ext)
-  result = parentDir(n.info.toFullPath) / s
+  result = parentDir(toFullPath(c.config, n.info)) / s
   if not fileExists(result):
     if isAbsolute(s): result = s
     else:
@@ -412,6 +419,10 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
       if result.len == 0: result = s
 
 proc processCompile(c: PContext, n: PNode) =
+  proc docompile(c: PContext; it: PNode; src, dest: string) =
+    var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External})
+    extccomp.addExternalFileToCompile(c.config, cf)
+    recordPragma(c, it, "compile", src, dest)
 
   proc getStrLit(c: PContext, n: PNode; i: int): string =
     n.sons[i] = c.semConstExpr(c, n[i])
@@ -426,30 +437,31 @@ proc processCompile(c: PContext, n: PNode) =
   if it.kind in {nkPar, nkTupleConstr} and it.len == 2:
     let s = getStrLit(c, it, 0)
     let dest = getStrLit(c, it, 1)
-    var found = parentDir(n.info.toFullPath) / s
+    var found = parentDir(toFullPath(c.config, n.info)) / s
     for f in os.walkFiles(found):
-      let nameOnly = extractFilename(f)
-      var cf = Cfile(cname: f,
-          obj: completeCFilePath(c.config, dest % nameOnly),
-          flags: {CfileFlag.External})
-      extccomp.addExternalFileToCompile(c.config, cf)
+      let obj = completeCFilePath(c.config, dest % extractFilename(f))
+      docompile(c, it, f, obj)
   else:
     let s = expectStrLit(c, n)
-    var found = parentDir(n.info.toFullPath) / s
+    var found = parentDir(toFullPath(c.config, n.info)) / s
     if not fileExists(found):
       if isAbsolute(s): found = s
       else:
         found = findFile(c.config, s)
         if found.len == 0: found = s
-    extccomp.addExternalFileToCompile(c.config, found)
+    let obj = toObjFile(c.config, completeCFilePath(c.config, changeFileExt(found, ""), false))
+    docompile(c, it, found, obj)
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
-  let found = relativeFile(c, n, CC[cCompiler].objExt)
+  let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
   case feature
-  of linkNormal: extccomp.addExternalFileToLink(c.config, found)
+  of linkNormal:
+    extccomp.addExternalFileToLink(c.config, found)
+    recordPragma(c, n, "link", found)
   of linkSys:
-    extccomp.addExternalFileToLink(c.config,
-      c.config.libpath / completeCFilePath(c.config, found, false))
+    let dest = c.config.libpath / completeCFilePath(c.config, found, false)
+    extccomp.addExternalFileToLink(c.config, dest)
+    recordPragma(c, n, "link", dest)
   else: internalError(c.config, n.info, "processCommonLink")
 
 proc pragmaBreakpoint(c: PContext, n: PNode) =
@@ -480,9 +492,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
       if c < 0: sub = substr(str, b + 1)
       else: sub = substr(str, b + 1, c - 1)
       if sub != "":
-        var e = searchInScopes(con, getIdent(sub))
+        var e = searchInScopes(con, getIdent(con.cache, sub))
         if e != nil:
-          if e.kind == skStub: loadStub(e)
+          when false:
+            if e.kind == skStub: loadStub(e)
           incl(e.flags, sfUsed)
           addSon(result, newSymNode(e))
         else:
@@ -549,7 +562,7 @@ proc pragmaLine(c: PContext, n: PNode) =
       localError(c.config, n.info, "tuple expected")
   else:
     # sensible default:
-    n.info = getInfoContext(-1)
+    n.info = getInfoContext(c.config, -1)
 
 proc processPragma(c: PContext, n: PNode, i: int) =
   let it = n[i]
@@ -634,7 +647,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
       let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
       if dest == nil or dest.kind in routineKinds:
         localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
-      let src = considerQuotedIdent(c.config, n[0])
+      let src = considerQuotedIdent(c, n[0])
       let alias = newSym(skAlias, src, dest, n[0].info, c.config.options)
       incl(alias.flags, sfExported)
       if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
@@ -657,7 +670,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # We return a dummy symbol; later passes over the type will repair it.
       # Generic instantiation needs to know about this too. But we're lazy
       # and perform the lookup on demand instead.
-      result = newSym(skUnknown, considerQuotedIdent(c.config, n), nil, n.info,
+      result = newSym(skUnknown, considerQuotedIdent(c, n), nil, n.info,
         c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
@@ -710,7 +723,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
   elif key.kind notin nkIdentKinds:
     n.sons[i] = semCustomPragma(c, it)
     return
-  let ident = considerQuotedIdent(c.config, key)
+  let ident = considerQuotedIdent(c, key)
   var userPragma = strTableGet(c.userPragmas, ident)
   if userPragma != nil:
     # number of pragmas increase/decrease with user pragma expansion
@@ -723,7 +736,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
     i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
     dec c.instCounter
   else:
-    var k = whichKeyword(ident)
+    let k = whichKeyword(ident)
     if k in validPragmas:
       case k
       of wExportc:
@@ -890,8 +903,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfPacked)
-      of wHint: message(c.config, it.info, hintUser, expectStrLit(c, it))
-      of wWarning: message(c.config, it.info, warnUser, expectStrLit(c, it))
+      of wHint:
+        let s = expectStrLit(c, it)
+        recordPragma(c, it, "hint", s)
+        message(c.config, it.info, hintUser, s)
+      of wWarning:
+        let s = expectStrLit(c, it)
+        recordPragma(c, it, "warning", s)
+        message(c.config, it.info, warnUser, s)
       of wError:
         if sym != nil and sym.isRoutine:
           # This is subtle but correct: the error *statement* is only
@@ -901,7 +920,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
           noVal(c, it)
           incl(sym.flags, sfError)
         else:
-          localError(c.config, it.info, errUser, expectStrLit(c, it))
+          let s = expectStrLit(c, it)
+          recordPragma(c, it, "error", s)
+          localError(c.config, it.info, errUser, s)
       of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it))
       of wDefine: processDefine(c, it)
       of wUndef: processUndef(c, it)
@@ -1017,9 +1038,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         processExperimental(c, it, sym)
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
-          c.selfName = considerQuotedIdent(c.config, it[1])
+          c.selfName = considerQuotedIdent(c, it[1])
         elif it.kind == nkIdent or it.len == 1:
-          c.selfName = getIdent("self")
+          c.selfName = getIdent(c.cache, "self")
         else:
           localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
@@ -1047,13 +1068,13 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
     for it in c.optionStack:
       let o = it.otherPragmas
       if not o.isNil:
-        pushInfoContext(n.info)
+        pushInfoContext(c.config, n.info)
         var i = 0
         while i < o.len():
           if singlePragma(c, sym, o, i, validPragmas):
             internalError(c.config, n.info, "implicitPragmas")
           inc i
-        popInfoContext()
+        popInfoContext(c.config)
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
       localError(c.config, n.info, ".dynlib requires .exportc")
@@ -1065,8 +1086,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
       if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
 
 proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
-  if n == nil or n.sons == nil:
-    return false
+  if n == nil: return false
 
   for p in n:
     var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p
@@ -1078,7 +1098,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
 proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
   if n == nil: return
   var i = 0
-  while i < n.len():
+  while i < n.len:
     if singlePragma(c, sym, n, i, validPragmas): break
     inc i
 
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
index 00e2c537d..246d1ae5e 100644
--- a/compiler/prefixmatches.nim
+++ b/compiler/prefixmatches.nim
@@ -24,7 +24,7 @@ proc prefixMatch*(p, s: string): PrefixMatch =
   # check for prefix/contains:
   while i < L:
     if s[i] == '_': inc i
-    if eq(s[i], p[0]):
+    if i < L and eq(s[i], p[0]):
       var ii = i+1
       var jj = 1
       while ii < L and jj < p.len:
@@ -43,10 +43,10 @@ proc prefixMatch*(p, s: string): PrefixMatch =
     i = 1
     var j = 1
     while i < s.len:
-      if s[i] == '_' and i < s.len-1:
+      if i < s.len-1 and s[i] == '_':
         if j < p.len and eq(p[j], s[i+1]): inc j
         else: return PrefixMatch.None
-      if s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
+      if i < s.len and s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
         if j < p.len and eq(p[j], s[i]): inc j
         else: return PrefixMatch.None
       inc i
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 042947e72..3f47e7e8a 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -73,7 +73,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
         if (sfExported notin result.flags) and (sfExported in fn.flags):
           let message = ("public implementation '$1' has non-public " &
                          "forward declaration in $2") %
-                        [getProcHeader(result), $result.info]
+                        [getProcHeader(c.config, result), c.config$result.info]
           localError(c.config, fn.info, message)
         return
       of paramsIncompatible:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 75c19a163..e209112e8 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -10,7 +10,7 @@
 # This module implements the renderer of the standard Nim representation.
 
 import
-  lexer, options, idents, strutils, ast, msgs, configuration
+  lexer, options, idents, strutils, ast, msgs, lineinfos
 
 type
   TRenderFlag* = enum
@@ -330,14 +330,15 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
 
 proc atom(g: TSrcGen; n: PNode): string =
   when defined(nimpretty):
+    doAssert g.config != nil, "g.config not initialized!"
     let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
-                    " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
+                    " " & fileSection(g.config, g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
                   else:
                     ""
     if n.info.offsetA <= n.info.offsetB:
       # for some constructed tokens this can not be the case and we're better
       # off to not mess with the offset then.
-      return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
+      return fileSection(g.config, g.fid, n.info.offsetA, n.info.offsetB) & comment
   var f: float32
   case n.kind
   of nkEmpty: result = ""
@@ -811,8 +812,8 @@ proc gident(g: var TSrcGen, n: PNode) =
 
   var t: TTokType
   var s = atom(g, n)
-  if (s[0] in lexer.SymChars):
-    if (n.kind == nkIdent):
+  if s.len > 0 and s[0] in lexer.SymChars:
+    if n.kind == nkIdent:
       if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
           (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
         t = tkSymbol
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index d50be1d99..27b19a373 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -2,7 +2,7 @@
 import
   intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
   sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
-  configuration
+  lineinfos
 
 type
   DepN = ref object
@@ -11,11 +11,11 @@ type
     onStack: bool
     kids: seq[DepN]
     hAQ, hIS, hB, hCmd: int
-    when not defined(release):
+    when defined(debugReorder):
       expls: seq[string]
   DepG = seq[DepN]
 
-when not defined(release):
+when defined(debugReorder):
   var idNames = newTable[int, string]()
 
 proc newDepN(id: int, pnode: PNode): DepN =
@@ -30,10 +30,10 @@ proc newDepN(id: int, pnode: PNode): DepN =
   result.hIS = -1
   result.hB = -1
   result.hCmd = -1
-  when not defined(release):
+  when defined(debugReorder):
     result.expls = @[]
 
-proc accQuoted(n: PNode): PIdent =
+proc accQuoted(cache: IdentCache; n: PNode): PIdent =
   var id = ""
   for i in 0 ..< n.len:
     let x = n[i]
@@ -41,33 +41,33 @@ proc accQuoted(n: PNode): PIdent =
     of nkIdent: id.add(x.ident.s)
     of nkSym: id.add(x.sym.name.s)
     else: discard
-  result = getIdent(id)
+  result = getIdent(cache, id)
 
-proc addDecl(n: PNode; declares: var IntSet) =
+proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
   case n.kind
-  of nkPostfix: addDecl(n[1], declares)
-  of nkPragmaExpr: addDecl(n[0], declares)
+  of nkPostfix: addDecl(cache, n[1], declares)
+  of nkPragmaExpr: addDecl(cache, n[0], declares)
   of nkIdent:
     declares.incl n.ident.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[n.ident.id] = n.ident.s
   of nkSym:
     declares.incl n.sym.name.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[n.sym.name.id] = n.sym.name.s
   of nkAccQuoted:
-    let a = accQuoted(n)
+    let a = accQuoted(cache, n)
     declares.incl a.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[a.id] = a.s
   of nkEnumFieldDef:
-    addDecl(n[0], declares)
+    addDecl(cache, n[0], declares)
   else: discard
 
-proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
-  template deps(n) = computeDeps(n, declares, uses, false)
+proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLevel: bool) =
+  template deps(n) = computeDeps(cache, n, declares, uses, false)
   template decl(n) =
-    if topLevel: addDecl(n, declares)
+    if topLevel: addDecl(cache, n, declares)
   case n.kind
   of procDefs, nkMacroDef, nkTemplateDef:
     decl(n[0])
@@ -93,11 +93,11 @@ proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
       deps(n[i])
   of nkIdent: uses.incl n.ident.id
   of nkSym: uses.incl n.sym.name.id
-  of nkAccQuoted: uses.incl accQuoted(n).id
+  of nkAccQuoted: uses.incl accQuoted(cache, n).id
   of nkOpenSymChoice, nkClosedSymChoice:
     uses.incl n.sons[0].sym.name.id
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
-    for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel)
+    for i in 0..<len(n): computeDeps(cache, n[i], declares, uses, topLevel)
   of nkPragma:
     let a = n.sons[0]
     if a.kind == nkExprColonExpr and a.sons[0].kind == nkIdent and
@@ -136,15 +136,13 @@ proc hasIncludes(n:PNode): bool =
     if a.kind == nkIncludeStmt:
       return true
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                    cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache, graph.config)
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+  result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(FileIndex s.position, fileIdx)
 
 proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
-                    modulePath: string, includedFiles: var IntSet,
-                    cache: IdentCache): PNode =
+                    modulePath: string, includedFiles: var IntSet): PNode =
   # Parses includes and injects them in the current tree
   if not n.hasIncludes:
     return n
@@ -155,11 +153,12 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
         var f = checkModuleName(graph.config, a.sons[i])
         if f != InvalidFileIDX:
           if containsOrIncl(includedFiles, f.int):
-            localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename)
+            localError(graph.config, a.info, "recursive dependency: '$1'" %
+              toFilename(graph.config, f))
           else:
-            let nn = includeModule(graph, module, f, cache)
+            let nn = includeModule(graph, module, f)
             let nnn = expandIncludes(graph, module, nn, modulePath,
-                                      includedFiles, cache)
+                                      includedFiles)
             excl(includedFiles, f.int)
             for b in nnn:
               result.add b
@@ -215,7 +214,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
           # consecutive type and const sections
           var wmsg = "Circular dependency detected. reorder pragma may not be able to" &
             " reorder some nodes properely"
-          when not defined(release):
+          when defined(debugReorder):
             wmsg &= ":\n"
             for i in 0..<cs.len-1:
                 for j in i..<cs.len:
@@ -354,13 +353,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
       if j < i and nj.hasCommand and niHasCmd:
         # Preserve order for commands and calls
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "both have commands and one comes after the other"
       elif j < i and nj.hasImportStmt:
         # Every node that comes after an import statement must
         # depend on that import
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "parent is, or contains, an import statement and child comes after it"
       elif j < i and niHasBody and nj.hasAccQuotedDef:
         # Every function, macro, template... with a body depends
@@ -368,13 +367,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         # That's because it is hard to detect the use of functions
         # like "[]=", "[]", "or" ... in their bodies.
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
       elif j < i and niHasBody and not nj.hasBody and
         intersects(deps[i][0], declares):
           # Keep function declaration before function definition
           ni.kids.add nj
-          when not defined(release):
+          when defined(debugReorder):
             for dep in deps[i][0]:
               if dep in declares:
                 ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
@@ -382,7 +381,7 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         for d in declares:
           if uses.contains(d):
             ni.kids.add nj
-            when not defined(release):
+            when defined(debugReorder):
               ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"
 
 proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
@@ -426,19 +425,19 @@ proc hasForbiddenPragma(n: PNode): bool =
         a[0].ident.s == "push":
           return true
 
-proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PNode =
+proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
   if n.hasForbiddenPragma:
     return n
   var includedFiles = initIntSet()
-  let mpath = module.fileIdx.toFullPath
+  let mpath = toFullPath(graph.config, module.fileIdx)
   let n = expandIncludes(graph, module, n, mpath,
-                          includedFiles, cache).splitSections
+                          includedFiles).splitSections
   result = newNodeI(nkStmtList, n.info)
   var deps = newSeq[(IntSet, IntSet)](n.len)
   for i in 0..<n.len:
     deps[i][0] = initIntSet()
     deps[i][1] = initIntSet()
-    computeDeps(n[i], deps[i][0], deps[i][1], true)
+    computeDeps(graph.cache, n[i], deps[i][0], deps[i][1], true)
 
   var g = buildGraph(n, deps)
   let comps = getStrongComponents(g)
diff --git a/compiler/rod.nim b/compiler/rod.nim
index c144f15ef..f9208f5dc 100644
--- a/compiler/rod.nim
+++ b/compiler/rod.nim
@@ -9,18 +9,21 @@
 
 ## This module implements the canonalization for the various caching mechanisms.
 
-import ast, idgen, msgs
+import ast, idgen, lineinfos, msgs, incremental, modulegraphs
 
-when not defined(nimSymbolfiles):
-  template setupModuleCache* = discard
-  template storeNode*(module: PSym; n: PNode) = discard
-  template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
+when not nimIncremental:
+  template setupModuleCache*(g: ModuleGraph) = discard
+  template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard
+  template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList)
 
-  template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
+  template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = getID()
 
-  template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
+  template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard
 
-  template storeRemaining*(module: PSym) = discard
+  template storeRemaining*(g: ModuleGraph; module: PSym) = discard
 
 else:
   include rodimpl
+
+  # idea for testing all this logic: *Always* load the AST from the DB, whether
+  # we already have it in RAM or not!
diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index aff4f6909..46a0e16db 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -10,59 +10,51 @@
 ## This module implements the new compilation cache.
 
 import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
-  renderer, rodutils, std / sha1, idents, astalgo, magicsys
+  renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
+  btrees, trees
 
 ## Todo:
-## - Implement the 'import' replay logic so that the codegen runs over
-##   dependent modules.
 ## - Make conditional symbols and the configuration part of a module's
-##   dependencies.
-## - Test multi methods.
-## - Implement the limited VM support based on sets.
-## - Depencency computation should use signature hashes in order to
+##   dependencies. Also include the rodfile "version".
+## - Dependency computation should use *signature* hashes in order to
 ##   avoid recompiling dependent modules.
 
-var db: DbConn
+template db(): DbConn = g.incr.db
 
-proc hashFileCached(fileIdx: int32; fullpath: string): string =
-  result = msgs.getHash(fileIdx)
-  if result.len == 0:
-    result = $secureHashFile(fullpath)
-    msgs.setHash(fileIdx, result)
-
-proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool =
+proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string;
+                    cycleCheck: var IntSet): bool =
   let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
     fullpath)
   if root[0].len == 0: return true
-  if root[1] != hashFileCached(fileIdx, fullpath):
+  if root[1] != hashFileCached(g.config, fileIdx, fullpath):
     return true
   # cycle detection: assume "not changed" is correct.
-  if cycleCheck.containsOrIncl(fileIdx):
+  if cycleCheck.containsOrIncl(int fileIdx):
     return false
   # check dependencies (recursively):
   for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
                          root[0]):
     let dep = row[0]
-    if needsRecompile(dep.fileInfoIdx, dep, cycleCheck):
+    if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck):
       return true
   return false
 
-proc getModuleId*(fileIdx: int32; fullpath: string): int =
-  if gSymbolFiles != v2Sf: return getID()
-  let module = db.getRow(
+proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int =
+  if g.config.symbolFiles in {disabledSf, writeOnlySf}: return getID()
+  let module = g.incr.db.getRow(
     sql"select id, fullHash from modules where fullpath = ?", fullpath)
-  let currentFullhash = hashFileCached(fileIdx, fullpath)
+  let currentFullhash = hashFileCached(g.config, fileIdx, fullpath)
   if module[0].len == 0:
     result = int db.insertID(sql"insert into modules(fullpath, interfHash, fullHash) values (?, ?, ?)",
       fullpath, "", currentFullhash)
   else:
     result = parseInt(module[0])
     if currentFullhash == module[1]:
-      # not changed, so use the cached AST (even if it might be wrong
-      # due to its dependencies):
+      # not changed, so use the cached AST:
       doAssert(result != 0)
       var cycleCheck = initIntSet()
-      if not needsRecompile(fileIdx, fullpath, cycleCheck):
+      if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
+        echo "cached successfully! ", fullpath
         return -result
     db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
     db.exec(sql"delete from deps where module = ?", module[0])
@@ -71,20 +63,6 @@ proc getModuleId*(fileIdx: int32; fullpath: string): int =
     db.exec(sql"delete from toplevelstmts where module = ?", module[0])
     db.exec(sql"delete from statics where module = ?", module[0])
 
-type
-  TRodWriter = object
-    module: PSym
-    sstack: seq[PSym]          # a stack of symbols to process
-    tstack: seq[PType]         # a stack of types to process
-    tmarks, smarks: IntSet
-    forwardedSyms: seq[PSym]
-
-  PRodWriter = var TRodWriter
-
-proc initRodWriter(module: PSym): TRodWriter =
-  result = TRodWriter(module: module, sstack: @[], tstack: @[],
-    tmarks: initIntSet(), smarks: initIntSet(), forwardedSyms: @[])
-
 when false:
   proc getDefines(): string =
     result = ""
@@ -92,39 +70,17 @@ when false:
       if result.len != 0: add(result, " ")
       add(result, d)
 
-const
-  rodNL = "\L"
-
-proc pushType(w: PRodWriter, t: PType) =
+proc pushType(w: var Writer, t: PType) =
   if not containsOrIncl(w.tmarks, t.id):
     w.tstack.add(t)
 
-proc pushSym(w: PRodWriter, s: PSym) =
+proc pushSym(w: var Writer, s: PSym) =
   if not containsOrIncl(w.smarks, s.id):
     w.sstack.add(s)
 
-proc toDbFileId(fileIdx: int32): int =
-  if fileIdx == -1: return -1
-  let fullpath = fileIdx.toFullPath
-  let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
-    fullpath)
-  let id = row[0]
-  let fullhash = hashFileCached(fileIdx, fullpath)
-  if id.len == 0:
-    result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
-      fullpath, fullhash)
-  else:
-    if row[1] != fullhash:
-      db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
-    result = parseInt(id)
-
-proc fromDbFileId(dbId: int): int32 =
-  if dbId == -1: return -1
-  let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId)
-  doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
-  result = fileInfoIdx(fullpath)
+template w: untyped = g.incr.w
 
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
+proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode,
                 result: var string) =
   if n == nil:
     # nil nodes have to be stored too:
@@ -139,14 +95,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
-    encodeVInt(n.info.line, result)
+    encodeVInt(int n.info.line, result)
     result.add(',')
-    encodeVInt(toDbFileId(n.info.fileIndex), result)
+    encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result)
   elif fInfo.line != n.info.line:
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
-    encodeVInt(n.info.line, result)
+    encodeVInt(int n.info.line, result)
   elif fInfo.col != n.info.col:
     result.add('?')
     encodeVInt(n.info.col, result)
@@ -182,10 +138,10 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     pushSym(w, n.sym)
   else:
     for i in countup(0, sonsLen(n) - 1):
-      encodeNode(w, n.info, n.sons[i], result)
+      encodeNode(g, n.info, n.sons[i], result)
   add(result, ')')
 
-proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
+proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) =
   var oldLen = result.len
   result.add('<')
   if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
@@ -197,9 +153,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
     encodeVInt(cast[int32](loc.flags), result)
   if loc.lode != nil:
     add(result, '^')
-    encodeNode(w, unknownLineInfo(), loc.lode, result)
-    #encodeVInt(cast[int32](loc.t.id), result)
-    #pushType(w, loc.t)
+    encodeNode(g, unknownLineInfo(), loc.lode, result)
   if loc.r != nil:
     add(result, '!')
     encodeStr($loc.r, result)
@@ -209,13 +163,13 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
   else:
     add(result, '>')
 
-proc encodeType(w: PRodWriter, t: PType, result: var string) =
+proc encodeType(g: ModuleGraph, t: PType, result: var string) =
   if t == nil:
     # nil nodes have to be stored too:
     result.add("[]")
     return
   # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError("encodeType: tyForward")
+  if t.kind == tyForward: internalError(g.config, "encodeType: tyForward")
   # for the new rodfile viewer we use a preceding [ so that the data section
   # can easily be disambiguated:
   add(result, '[')
@@ -223,7 +177,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   add(result, '+')
   encodeVInt(t.id, result)
   if t.n != nil:
-    encodeNode(w, w.module.info, t.n, result)
+    encodeNode(g, unknownLineInfo(), t.n, result)
   if t.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](t.flags), result)
@@ -269,7 +223,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
     add(result, '\20')
     encodeVInt(s.id, result)
     pushSym(w, s)
-  encodeLoc(w, t.loc, result)
+  encodeLoc(g, t.loc, result)
   for i in countup(0, sonsLen(t) - 1):
     if t.sons[i] == nil:
       add(result, "^()")
@@ -278,15 +232,15 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
       encodeVInt(t.sons[i].id, result)
       pushType(w, t.sons[i])
 
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
+proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) =
   add(result, '|')
   encodeVInt(ord(lib.kind), result)
   add(result, '|')
   encodeStr($lib.name, result)
   add(result, '|')
-  encodeNode(w, info, lib.path, result)
+  encodeNode(g, info, lib.path, result)
 
-proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
+proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation];
                           result: var string) =
   for t in s:
     result.add('\15')
@@ -299,7 +253,7 @@ proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
     result.add('\20')
     encodeVInt(t.compilesId, result)
 
-proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
+proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
   if s == nil:
     # nil nodes have to be stored too:
     result.add("{}")
@@ -317,9 +271,9 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   result.add('?')
   if s.info.col != -1'i16: encodeVInt(s.info.col, result)
   result.add(',')
-  if s.info.line != -1'i16: encodeVInt(s.info.line, result)
+  encodeVInt(int s.info.line, result)
   result.add(',')
-  encodeVInt(toDbFileId(s.info.fileIndex), result)
+  encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result)
   if s.owner != nil:
     result.add('*')
     encodeVInt(s.owner.id, result)
@@ -338,11 +292,11 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   if s.offset != - 1:
     result.add('`')
     encodeVInt(s.offset, result)
-  encodeLoc(w, s.loc, result)
-  if s.annex != nil: encodeLib(w, s.annex, s.info, result)
+  encodeLoc(g, s.loc, result)
+  if s.annex != nil: encodeLib(g, s.annex, s.info, result)
   if s.constraint != nil:
     add(result, '#')
-    encodeNode(w, unknownLineInfo(), s.constraint, result)
+    encodeNode(g, unknownLineInfo(), s.constraint, result)
   case s.kind
   of skType, skGenericParam:
     for t in s.typeInstCache:
@@ -350,13 +304,13 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
       encodeVInt(t.id, result)
       pushType(w, t)
   of routineKinds:
-    encodeInstantiations(w, s.procInstCache, result)
+    encodeInstantiations(g, s.procInstCache, result)
     if s.gcUnsafetyReason != nil:
       result.add('\16')
       encodeVInt(s.gcUnsafetyReason.id, result)
       pushSym(w, s.gcUnsafetyReason)
   of skModule, skPackage:
-    encodeInstantiations(w, s.usedGenerics, result)
+    encodeInstantiations(g, s.usedGenerics, result)
     # we don't serialize:
     #tab*: TStrTable         # interface table for modules
   of skLet, skVar, skField, skForVar:
@@ -374,105 +328,93 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
     # we used to attempt to save space here by only storing a dummy AST if
     # it is not necessary, but Nim's heavy compile-time evaluation features
     # make that unfeasible nowadays:
-    encodeNode(w, s.info, s.ast, result)
+    encodeNode(g, s.info, s.ast, result)
 
-proc storeSym(w: PRodWriter; s: PSym) =
+proc storeSym(g: ModuleGraph; s: PSym) =
   if sfForward in s.flags and s.kind != skModule:
     w.forwardedSyms.add s
     return
   var buf = newStringOfCap(160)
-  encodeSym(w, s, buf)
+  encodeSym(g, s, buf)
   # XXX only store the name for exported symbols in order to speed up lookup
   # times once we enable the skStub logic.
+  let m = getModule(s)
+  let mid = if m == nil: 0 else: abs(m.id)
   db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
-    s.id, abs(w.module.id), s.name.s, buf, ord(sfExported in s.flags))
+    s.id, mid, s.name.s, buf, ord(sfExported in s.flags))
 
-proc storeType(w: PRodWriter; t: PType) =
+proc storeType(g: ModuleGraph; t: PType) =
   var buf = newStringOfCap(160)
-  encodeType(w, t, buf)
+  encodeType(g, t, buf)
+  let m = if t.owner != nil: getModule(t.owner) else: nil
+  let mid = if m == nil: 0 else: abs(m.id)
   db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
-    t.id, abs(w.module.id), buf)
+    t.id, mid, buf)
 
-var w = initRodWriter(nil)
-
-proc storeNode*(module: PSym; n: PNode) =
-  if gSymbolFiles != v2Sf: return
-  w.module = module
+proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) =
+  if g.config.symbolFiles == disabledSf: return
   var buf = newStringOfCap(160)
-  encodeNode(w, module.info, n, buf)
+  encodeNode(g, module.info, n, buf)
   db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
     abs(module.id), module.offset, buf)
   inc module.offset
   var i = 0
   while true:
     if i > 10_000:
-      quit "loop never ends!"
+      doAssert false, "loop never ends!"
     if w.sstack.len > 0:
       let s = w.sstack.pop()
       when false:
         echo "popped ", s.name.s, " ", s.id
-      storeSym(w, s)
+      storeSym(g, s)
     elif w.tstack.len > 0:
       let t = w.tstack.pop()
-      storeType(w, t)
+      storeType(g, t)
       when false:
         echo "popped type ", typeToString(t), " ", t.id
     else:
       break
     inc i
 
-proc storeRemaining*(module: PSym) =
-  if gSymbolFiles != v2Sf: return
-  w.module = module
+proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) =
+  storeNode(g, module, n)
+
+proc storeRemaining*(g: ModuleGraph; module: PSym) =
+  if g.config.symbolFiles == disabledSf: return
+  var stillForwarded: seq[PSym] = @[]
   for s in w.forwardedSyms:
-    assert sfForward notin s.flags
-    storeSym(w, s)
-  w.forwardedSyms.setLen 0
+    if sfForward notin s.flags:
+      storeSym(g, s)
+    else:
+      stillForwarded.add s
+  swap w.forwardedSyms, stillForwarded
 
 # ---------------- decoder -----------------------------------
-type
-  TRodReader = object
-    module: PSym
-    #sstack: seq[(PSym, ptr PSym)]       # a stack of symbols to process
-    #tstack: seq[(PType, ptr PType)]     # a stack of types to process
-
-    #tmarks, smarks: IntSet
-    syms: Table[int, PSym] ## XXX make this more efficients
-    types: Table[int, PType]
-    cache: IdentCache
 
+type
   BlobReader = object
     s: string
     pos: int
 
-  PRodReader = var TRodReader
-
-proc initRodReader(cache: IdentCache): TRodReader =
-  TRodReader(module: nil,
-    syms: initTable[int, PSym](), types: initTable[int, PType](),
-    cache: cache)
-
-var gr = initRodReader(newIdentCache())
-
 using
-  r: PRodReader
   b: var BlobReader
+  g: ModuleGraph
 
-proc loadSym(r; id: int, info: TLineInfo): PSym
-proc loadType(r; id: int, info: TLineInfo): PType
+proc loadSym(g; id: int, info: TLineInfo): PSym
+proc loadType(g; id: int, info: TLineInfo): PType
 
-proc decodeLineInfo(r; b; info: var TLineInfo) =
+proc decodeLineInfo(g; b; info: var TLineInfo) =
   if b.s[b.pos] == '?':
     inc(b.pos)
     if b.s[b.pos] == ',': info.col = -1'i16
     else: info.col = int16(decodeVInt(b.s, b.pos))
     if b.s[b.pos] == ',':
       inc(b.pos)
-      if b.s[b.pos] == ',': info.line = -1'i16
-      else: info.line = int16(decodeVInt(b.s, b.pos))
+      if b.s[b.pos] == ',': info.line = 0'u16
+      else: info.line = uint16(decodeVInt(b.s, b.pos))
       if b.s[b.pos] == ',':
         inc(b.pos)
-        info.fileIndex = fromDbFileId(decodeVInt(b.s, b.pos))
+        info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos))
 
 proc skipNode(b) =
   assert b.s[b.pos] == '('
@@ -488,7 +430,7 @@ proc skipNode(b) =
     inc pos
   b.pos = pos+1 # skip ')'
 
-proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
+proc decodeNodeLazyBody(g; b; fInfo: TLineInfo,
                         belongsTo: PSym): PNode =
   result = nil
   if b.s[b.pos] == '(':
@@ -497,14 +439,14 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
       inc(b.pos)
       return                  # nil node
     result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
-    decodeLineInfo(r, b, result.info)
+    decodeLineInfo(g, b, result.info)
     if b.s[b.pos] == '$':
       inc(b.pos)
       result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
     if b.s[b.pos] == '^':
       inc(b.pos)
       var id = decodeVInt(b.s, b.pos)
-      result.typ = loadType(r, id, result.info)
+      result.typ = loadType(g, id, result.info)
     case result.kind
     of nkCharLit..nkUInt64Lit:
       if b.s[b.pos] == '!':
@@ -525,16 +467,16 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
       if b.s[b.pos] == '!':
         inc(b.pos)
         var fl = decodeStr(b.s, b.pos)
-        result.ident = r.cache.getIdent(fl)
+        result.ident = g.cache.getIdent(fl)
       else:
-        internalError(result.info, "decodeNode: nkIdent")
+        internalError(g.config, result.info, "decodeNode: nkIdent")
     of nkSym:
       if b.s[b.pos] == '!':
         inc(b.pos)
         var id = decodeVInt(b.s, b.pos)
-        result.sym = loadSym(r, id, result.info)
+        result.sym = loadSym(g, id, result.info)
       else:
-        internalError(result.info, "decodeNode: nkSym")
+        internalError(g.config, result.info, "decodeNode: nkSym")
     else:
       var i = 0
       while b.s[b.pos] != ')':
@@ -545,17 +487,17 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
             skipNode(b)
           else:
             discard
-        addSonNilAllowed(result, decodeNodeLazyBody(r, b, result.info, nil))
+        addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil))
         inc i
     if b.s[b.pos] == ')': inc(b.pos)
-    else: internalError(result.info, "decodeNode: ')' missing")
+    else: internalError(g.config, result.info, "decodeNode: ')' missing")
   else:
-    internalError(fInfo, "decodeNode: '(' missing " & $b.pos)
+    internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos)
 
-proc decodeNode(r; b; fInfo: TLineInfo): PNode =
-  result = decodeNodeLazyBody(r, b, fInfo, nil)
+proc decodeNode(g; b; fInfo: TLineInfo): PNode =
+  result = decodeNodeLazyBody(g, b, fInfo, nil)
 
-proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
+proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) =
   if b.s[b.pos] == '<':
     inc(b.pos)
     if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
@@ -574,7 +516,7 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
       loc.flags = {}
     if b.s[b.pos] == '^':
       inc(b.pos)
-      loc.lode = decodeNode(r, b, info)
+      loc.lode = decodeNode(g, b, info)
       # rrGetType(b, decodeVInt(b.s, b.pos), info)
     else:
       loc.lode = nil
@@ -584,19 +526,21 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
     else:
       loc.r = nil
     if b.s[b.pos] == '>': inc(b.pos)
-    else: internalError(info, "decodeLoc " & b.s[b.pos])
+    else: internalError(g.config, info, "decodeLoc " & b.s[b.pos])
 
-proc loadBlob(query: SqlQuery; id: int): BlobReader =
+proc loadBlob(g; query: SqlQuery; id: int): BlobReader =
   let blob = db.getValue(query, id)
   if blob.len == 0:
-    internalError("symbolfiles: cannot find ID " & $ id)
+    internalError(g.config, "symbolfiles: cannot find ID " & $ id)
   result = BlobReader(pos: 0)
   shallowCopy(result.s, blob)
+  # ensure we can read without index checks:
+  result.s.add '\0'
 
-proc loadType(r; id: int; info: TLineInfo): PType =
-  result = r.types.getOrDefault(id)
+proc loadType(g; id: int; info: TLineInfo): PType =
+  result = g.incr.r.types.getOrDefault(id)
   if result != nil: return result
-  var b = loadBlob(sql"select data from types where nimid = ?", id)
+  var b = loadBlob(g, sql"select data from types where nimid = ?", id)
 
   if b.s[b.pos] == '[':
     inc(b.pos)
@@ -611,10 +555,10 @@ proc loadType(r; id: int; info: TLineInfo): PType =
     setId(result.id)
     #if debugIds: registerID(result)
   else:
-    internalError(info, "decodeType: no id")
+    internalError(g.config, info, "decodeType: no id")
   # here this also avoids endless recursion for recursive type
-  r.types[result.id] = result
-  if b.s[b.pos] == '(': result.n = decodeNode(r, b, unknownLineInfo())
+  g.incr.r.types.add(result.id, result)
+  if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo())
   if b.s[b.pos] == '$':
     inc(b.pos)
     result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
@@ -623,10 +567,10 @@ proc loadType(r; id: int; info: TLineInfo): PType =
     result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
   if b.s[b.pos] == '*':
     inc(b.pos)
-    result.owner = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.owner = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '&':
     inc(b.pos)
-    result.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '/':
     inc(b.pos)
     result.size = decodeVInt(b.s, b.pos)
@@ -646,65 +590,65 @@ proc loadType(r; id: int; info: TLineInfo): PType =
 
   if b.s[b.pos] == '\15':
     inc(b.pos)
-    result.destructor = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.destructor = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\16':
     inc(b.pos)
-    result.deepCopy = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.deepCopy = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\17':
     inc(b.pos)
-    result.assignment = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.assignment = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\18':
     inc(b.pos)
-    result.sink = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.sink = loadSym(g, decodeVInt(b.s, b.pos), info)
   while b.s[b.pos] == '\19':
     inc(b.pos)
     let x = decodeVInt(b.s, b.pos)
     doAssert b.s[b.pos] == '\20'
     inc(b.pos)
-    let y = loadSym(r, decodeVInt(b.s, b.pos), info)
+    let y = loadSym(g, decodeVInt(b.s, b.pos), info)
     result.methods.safeAdd((x, y))
-  decodeLoc(r, b, result.loc, info)
+  decodeLoc(g, b, result.loc, info)
   while b.s[b.pos] == '^':
     inc(b.pos)
     if b.s[b.pos] == '(':
       inc(b.pos)
       if b.s[b.pos] == ')': inc(b.pos)
-      else: internalError(info, "decodeType ^(" & b.s[b.pos])
+      else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos])
       rawAddSon(result, nil)
     else:
-      var d = decodeVInt(b.s, b.pos)
-      rawAddSon(result, loadType(r, d, info))
+      let d = decodeVInt(b.s, b.pos)
+      rawAddSon(result, loadType(g, d, info))
 
-proc decodeLib(r; b; info: TLineInfo): PLib =
+proc decodeLib(g; b; info: TLineInfo): PLib =
   result = nil
   if b.s[b.pos] == '|':
     new(result)
     inc(b.pos)
     result.kind = TLibKind(decodeVInt(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError("decodeLib: 1")
+    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1")
     inc(b.pos)
     result.name = rope(decodeStr(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError("decodeLib: 2")
+    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2")
     inc(b.pos)
-    result.path = decodeNode(r, b, info)
+    result.path = decodeNode(g, b, info)
 
-proc decodeInstantiations(r; b; info: TLineInfo;
+proc decodeInstantiations(g; b; info: TLineInfo;
                           s: var seq[PInstantiation]) =
   while b.s[b.pos] == '\15':
     inc(b.pos)
     var ii: PInstantiation
     new ii
-    ii.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
+    ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
     ii.concreteTypes = @[]
     while b.s[b.pos] == '\17':
       inc(b.pos)
-      ii.concreteTypes.add loadType(r, decodeVInt(b.s, b.pos), info)
+      ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info)
     if b.s[b.pos] == '\20':
       inc(b.pos)
       ii.compilesId = decodeVInt(b.s, b.pos)
     s.safeAdd ii
 
-proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
+proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
   if b.s[b.pos] == '{':
     inc(b.pos)
     if b.s[b.pos] == '}':
@@ -717,26 +661,26 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     id = decodeVInt(b.s, b.pos)
     setId(id)
   else:
-    internalError(info, "decodeSym: no id")
+    internalError(g.config, info, "decodeSym: no id")
   var ident: PIdent
   if b.s[b.pos] == '&':
     inc(b.pos)
-    ident = r.cache.getIdent(decodeStr(b.s, b.pos))
+    ident = g.cache.getIdent(decodeStr(b.s, b.pos))
   else:
-    internalError(info, "decodeSym: no ident")
+    internalError(g.config, info, "decodeSym: no ident")
   #echo "decoding: {", ident.s
   new(result)
   result.id = id
   result.kind = k
   result.name = ident         # read the rest of the symbol description:
-  r.syms[result.id] = result
+  g.incr.r.syms.add(result.id, result)
   if b.s[b.pos] == '^':
     inc(b.pos)
-    result.typ = loadType(r, decodeVInt(b.s, b.pos), info)
-  decodeLineInfo(r, b, result.info)
+    result.typ = loadType(g, decodeVInt(b.s, b.pos), info)
+  decodeLineInfo(g, b, result.info)
   if b.s[b.pos] == '*':
     inc(b.pos)
-    result.owner = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+    result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
   if b.s[b.pos] == '$':
     inc(b.pos)
     result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos)))
@@ -746,8 +690,6 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
   if b.s[b.pos] == '!':
     inc(b.pos)
     result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
-  else:
-    result.options = r.module.options
   if b.s[b.pos] == '%':
     inc(b.pos)
     result.position = decodeVInt(b.s, b.pos)
@@ -755,28 +697,28 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     inc(b.pos)
     result.offset = decodeVInt(b.s, b.pos)
   else:
-    result.offset = - 1
-  decodeLoc(r, b, result.loc, result.info)
-  result.annex = decodeLib(r, b, info)
+    result.offset = -1
+  decodeLoc(g, b, result.loc, result.info)
+  result.annex = decodeLib(g, b, info)
   if b.s[b.pos] == '#':
     inc(b.pos)
-    result.constraint = decodeNode(r, b, unknownLineInfo())
+    result.constraint = decodeNode(g, b, unknownLineInfo())
   case result.kind
   of skType, skGenericParam:
     while b.s[b.pos] == '\14':
       inc(b.pos)
-      result.typeInstCache.safeAdd loadType(r, decodeVInt(b.s, b.pos), result.info)
+      result.typeInstCache.safeAdd loadType(g, decodeVInt(b.s, b.pos), result.info)
   of routineKinds:
-    decodeInstantiations(r, b, result.info, result.procInstCache)
+    decodeInstantiations(g, b, result.info, result.procInstCache)
     if b.s[b.pos] == '\16':
       inc(b.pos)
-      result.gcUnsafetyReason = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+      result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info)
   of skModule, skPackage:
-    decodeInstantiations(r, b, result.info, result.usedGenerics)
+    decodeInstantiations(g, b, result.info, result.usedGenerics)
   of skLet, skVar, skField, skForVar:
     if b.s[b.pos] == '\18':
       inc(b.pos)
-      result.guard = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+      result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info)
     if b.s[b.pos] == '\19':
       inc(b.pos)
       result.bitsize = decodeVInt(b.s, b.pos).int16
@@ -786,156 +728,133 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     #if result.kind in routineKinds:
     #  result.ast = decodeNodeLazyBody(b, result.info, result)
     #else:
-    result.ast = decodeNode(r, b, result.info)
+    result.ast = decodeNode(g, b, result.info)
   if sfCompilerProc in result.flags:
-    registerCompilerProc(result)
+    registerCompilerProc(g, result)
     #echo "loading ", result.name.s
 
-proc loadSym(r; id: int; info: TLineInfo): PSym =
-  result = r.syms.getOrDefault(id)
+proc loadSym(g; id: int; info: TLineInfo): PSym =
+  result = g.incr.r.syms.getOrDefault(id)
   if result != nil: return result
-  var b = loadBlob(sql"select data from syms where nimid = ?", id)
-  result = loadSymFromBlob(r, b, info)
+  var b = loadBlob(g, sql"select data from syms where nimid = ?", id)
+  result = loadSymFromBlob(g, b, info)
   doAssert id == result.id, "symbol ID is not consistent!"
 
-proc loadModuleSymTab(r; module: PSym) =
+proc loadModuleSymTab(g; module: PSym) =
   ## goal: fill  module.tab
-  gr.syms[module.id] = module
+  g.incr.r.syms.add(module.id, module)
   for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
     let id = parseInt(row[0])
-    var s = r.syms.getOrDefault(id)
+    var s = g.incr.r.syms.getOrDefault(id)
     if s == nil:
       var b = BlobReader(pos: 0)
       shallowCopy(b.s, row[1])
-      s = loadSymFromBlob(r, b, module.info)
+      # ensure we can read without index checks:
+      b.s.add '\0'
+      s = loadSymFromBlob(g, b, module.info)
     assert s != nil
     strTableAdd(module.tab, s)
   if sfSystemModule in module.flags:
-    magicsys.systemModule = module
-
-proc loadNode*(module: PSym; index: int): PNode =
-  assert gSymbolFiles == v2Sf
-  if index == 0:
-    loadModuleSymTab(gr, module)
-    #index = parseInt db.getValue(
-    #  sql"select min(id) from toplevelstmts where module = ?", abs module.id)
-  var b = BlobReader(pos: 0)
-  b.s = db.getValue(sql"select data from toplevelstmts where position = ? and module = ?",
-                    index, abs module.id)
-  if b.s.len == 0:
-    db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
-    return nil # end marker
-  gr.module = module
-  result = decodeNode(gr, b, module.info)
-
-proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) =
-  if gSymbolFiles != v2Sf: return
-
-  let a = toDbFileId(module)
-  let b = toDbFileId(fileIdx)
-
-  db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
-    a, b, ord(isIncludeFile))
-
-# --------------- Database model ---------------------------------------------
-
-proc createDb() =
-  db.exec(sql"""
-    create table if not exists controlblock(
-      idgen integer not null
-    );
-  """)
-
-  db.exec(sql"""
-    create table if not exists filenames(
-      id integer primary key,
-      fullpath varchar(8000) not null,
-      fullHash varchar(256) not null
-    );
-  """)
-  db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
-
-  db.exec(sql"""
-    create table if not exists modules(
-      id integer primary key,
-      fullpath varchar(8000) not null,
-      interfHash varchar(256) not null,
-      fullHash varchar(256) not null,
-
-      created timestamp not null default (DATETIME('now'))
-    );""")
-  db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
-
-  db.exec(sql"""
-    create table if not exists deps(
-      id integer primary key,
-      module integer not null,
-      dependency integer not null,
-      isIncludeFile integer not null,
-      foreign key (module) references filenames(id),
-      foreign key (dependency) references filenames(id)
-    );""")
-  db.exec(sql"""create index if not exists DepsIx on deps(module);""")
-
-  db.exec(sql"""
-    create table if not exists types(
-      id integer primary key,
-      nimid integer not null,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index TypeByModuleIdx on types(module);"
-  db.exec sql"create index TypeByNimIdIdx on types(nimid);"
-
-  db.exec(sql"""
-    create table if not exists syms(
-      id integer primary key,
-      nimid integer not null,
-      module integer not null,
-      name varchar(256) not null,
-      data blob not null,
-      exported int not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index if not exists SymNameIx on syms(name);"
-  db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
-  db.exec sql"create index SymByModuleIdx on syms(module);"
-  db.exec sql"create index SymByNimIdIdx on syms(nimid);"
-
-
-  db.exec(sql"""
-    create table if not exists toplevelstmts(
-      id integer primary key,
-      position integer not null,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
-  db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
-
-  db.exec(sql"""
-    create table if not exists statics(
-      id integer primary key,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
-  db.exec sql"insert into controlblock(idgen) values (0)"
-
-proc setupModuleCache* =
-  if gSymbolFiles != v2Sf: return
-  let dbfile = getNimcacheDir() / "rodfiles.db"
+    g.systemModule = module
+
+proc replay(g: ModuleGraph; module: PSym; n: PNode) =
+  # XXX check if we need to replay nkStaticStmt here.
+  case n.kind
+  #of nkStaticStmt:
+    #evalStaticStmt(module, g, n[0], module)
+    #of nkVarSection, nkLetSection:
+    #  nkVarSections are already covered by the vmgen which produces nkStaticStmt
+  of nkMethodDef:
+    methodDef(g, n[namePos].sym, fromCache=true)
+  of nkCommentStmt:
+    # pragmas are complex and can be user-overriden via templates. So
+    # instead of using the original ``nkPragma`` nodes, we rely on the
+    # fact that pragmas.nim was patched to produce specialized recorded
+    # statements for us in the form of ``nkCommentStmt`` with (key, value)
+    # pairs. Ordinary nkCommentStmt nodes never have children so this is
+    # not ambiguous.
+    # Fortunately only a tiny subset of the available pragmas need to
+    # be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
+    if n.len >= 2:
+      internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
+      case n[0].strVal
+      of "hint": message(g.config, n.info, hintUser, n[1].strVal)
+      of "warning": message(g.config, n.info, warnUser, n[1].strVal)
+      of "error": localError(g.config, n.info, errUser, n[1].strVal)
+      of "compile":
+        internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit
+        var cf = Cfile(cname: n[1].strVal, obj: n[2].strVal,
+                       flags: {CfileFlag.External})
+        extccomp.addExternalFileToCompile(g.config, cf)
+      of "link":
+        extccomp.addExternalFileToLink(g.config, n[1].strVal)
+      of "inc":
+        let destKey = n[1].strVal
+        let by = n[2].intVal
+        let v = getOrDefault(g.cacheCounters, destKey)
+        g.cacheCounters[destKey] = v+by
+      of "put":
+        let destKey = n[1].strVal
+        let key = n[2].strVal
+        let val = n[3]
+        if not contains(g.cacheTables, destKey):
+          g.cacheTables[destKey] = initBTree[string, PNode]()
+        if not contains(g.cacheTables[destKey], key):
+          g.cacheTables[destKey].add(key, val)
+        else:
+          internalError(g.config, n.info, "key already exists: " & key)
+      of "incl":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          block search:
+            for existing in g.cacheSeqs[destKey]:
+              if exprStructuralEquivalent(existing, val, strictSymEquality=true):
+                break search
+            g.cacheSeqs[destKey].add val
+      of "add":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          g.cacheSeqs[destKey].add val
+      else:
+        internalAssert g.config, false
+  of nkImportStmt:
+    for x in n:
+      internalAssert g.config, x.kind == nkStrLit
+      let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal))
+      internalAssert g.config, imported.id < 0
+  of nkStmtList, nkStmtListExpr:
+    for x in n: replay(g, module, x)
+  else: discard "nothing to do for this node"
+
+proc loadNode*(g: ModuleGraph; module: PSym): PNode =
+  loadModuleSymTab(g, module)
+  result = newNodeI(nkStmtList, module.info)
+  for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
+                        abs module.id):
+
+    var b = BlobReader(pos: 0)
+    # ensure we can read without index checks:
+    b.s = row[0] & '\0'
+    result.add decodeNode(g, b, module.info)
+
+  db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
+  echo result
+  replay(g, module, result)
+
+proc setupModuleCache*(g: ModuleGraph) =
+  if g.config.symbolFiles == disabledSf: return
+  g.recordStmt = recordStmt
+  let dbfile = getNimcacheDir(g.config) / "rodfiles.db"
   if not fileExists(dbfile):
     db = open(connection=dbfile, user="nim", password="",
               database="nim")
-    createDb()
+    createDb(db)
   else:
     db = open(connection=dbfile, user="nim", password="",
               database="nim")
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
deleted file mode 100644
index 52e7a924c..000000000
--- a/compiler/rodread.nim
+++ /dev/null
@@ -1,1244 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module is responsible for loading of rod files.
-#
-# Reading and writing binary files are really hard to debug. Therefore we use
-# a "creative" text/binary hybrid format. ROD-files are more efficient
-# to process because symbols can be loaded on demand.
-#
-# A ROD file consists of:
-#
-#  - a header:
-#    NIM:$fileversion\n
-#  - the module's id (even if the module changed, its ID will not!):
-#    ID:Ax3\n
-#  - HASH value of this module:
-#    HASH:HASH-val\n
-#  - a section containing the compiler options and defines this
-#    module has been compiled with:
-#    OPTIONS:options\n
-#    GOPTIONS:options\n # global options
-#    CMD:command\n
-#    DEFINES:defines\n
-#  - FILES(
-#    myfile.inc
-#    lib/mymodA
-#    )
-#  - an include file dependency section:
-#    INCLUDES(
-#    <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section!
-#    )
-#  - a module dependency section:
-#    DEPS: <fileidx> <fileidx>\n
-#  - an interface section:
-#    INTERF(
-#    identifier1 id\n # id is the symbol's id
-#    identifier2 id\n
-#    )
-#  - a compiler proc section:
-#    COMPILERPROCS(
-#    identifier1 id\n # id is the symbol's id
-#    )
-#  - an index consisting of (ID, linenumber)-pairs:
-#    INDEX(
-#    id-diff idx-diff\n
-#    id-diff idx-diff\n
-#    )
-#
-#    Since the whole index has to be read in advance, we compress it by
-#    storing the integer differences to the last entry instead of using the
-#    real numbers.
-#
-#  - an import index consisting of (ID, moduleID)-pairs:
-#    IMPORTS(
-#    id-diff moduleID-diff\n
-#    id-diff moduleID-diff\n
-#    )
-#  - a list of all exported type converters because they are needed for correct
-#    semantic checking:
-#    CONVERTERS:id id\n   # symbol ID
-#
-#    This is a misnomer now; it's really a "load unconditionally" section as
-#    it is also used for pattern templates.
-#
-#  - a list of all (private or exported) methods because they are needed for
-#    correct dispatcher generation:
-#    METHODS: id id\n   # symbol ID
-#  - an AST section that contains the module's AST:
-#    INIT(
-#    idx\n  # position of the node in the DATA section
-#    idx\n
-#    )
-#  - a data section, where each type, symbol or AST is stored.
-#    DATA(
-#    type
-#    (node)
-#    sym
-#    )
-#
-#    The data section MUST be the last section of the file, because processing
-#    stops immediately after ``DATA(`` and the rest is only loaded on demand
-#    by using a mem'mapped file.
-#
-
-import
-  os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
-  ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables,
-  configuration
-
-type
-  TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
-    rrEmpty,                  # dependencies not yet computed
-    rrNone,                   # no need to recompile
-    rrRodDoesNotExist,        # rod file does not exist
-    rrRodInvalid,             # rod file is invalid
-    rrHashChange,             # file has been edited since last recompilation
-    rrDefines,                # defines have changed
-    rrOptions,                # options have changed
-    rrInclDeps,               # an include has changed
-    rrModDeps                 # a module this module depends on has been changed
-
-const
-  reasonToFrmt*: array[TReasonForRecompile, string] = ["",
-    "no need to recompile: $1", "symbol file for $1 does not exist",
-    "symbol file for $1 has the wrong version",
-    "file edited since last compilation: $1",
-    "list of conditional symbols changed for: $1",
-    "list of options changed for: $1",
-    "an include file edited: $1",
-    "a module $1 depends on has changed"]
-
-type
-  TIndex*{.final.} = object   # an index with compression
-    lastIdxKey*, lastIdxVal*: int
-    tab*: TIITable
-    r*: string                # writers use this
-    offset*: int              # readers use this
-
-  TRodReader* = object of RootObj
-    pos: int                 # position; used for parsing
-    s: cstring               # mmap'ed file contents
-    options: TOptions
-    reason: TReasonForRecompile
-    modDeps: seq[FileIndex]
-    files: seq[FileIndex]
-    dataIdx: int             # offset of start of data section
-    convertersIdx: int       # offset of start of converters section
-    initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
-    filename: string
-    index, imports: TIndex
-    readerIndex: int
-    line: int            # only used for debugging, but is always in the code
-    moduleID: int
-    syms: Table[int, PSym]       # already processed symbols
-    memfile: MemFile     # unfortunately there is no point in time where we
-                         # can close this! XXX
-    methods*: TSymSeq
-    origFile: string
-    inViewMode: bool
-    cache*: IdentCache
-    config: ConfigRef
-
-  PRodReader* = ref TRodReader
-
-var rodCompilerprocs*: TStrTable # global because this is needed by magicsys
-
-proc rawLoadStub(s: PSym)
-
-var gTypeTable: TIdTable
-
-proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
-  # `info` is only used for debugging purposes
-proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
-
-proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
-  if r.s[r.pos] == '?':
-    inc(r.pos)
-    if r.s[r.pos] == ',': info.col = -1'i16
-    else: info.col = int16(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] == ',':
-      inc(r.pos)
-      if r.s[r.pos] == ',': info.line = 0'u16
-      else: info.line = uint16(decodeVInt(r.s, r.pos))
-      if r.s[r.pos] == ',':
-        inc(r.pos)
-        info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
-
-proc skipNode(r: PRodReader) =
-  assert r.s[r.pos] == '('
-  var par = 0
-  var pos = r.pos+1
-  while true:
-    case r.s[pos]
-    of ')':
-      if par == 0: break
-      dec par
-    of '(': inc par
-    else: discard
-    inc pos
-  r.pos = pos+1 # skip ')'
-
-proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
-                        belongsTo: PSym): PNode =
-  result = nil
-  if r.s[r.pos] == '(':
-    inc(r.pos)
-    if r.s[r.pos] == ')':
-      inc(r.pos)
-      return                  # nil node
-    result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
-    decodeLineInfo(r, result.info)
-    if r.s[r.pos] == '$':
-      inc(r.pos)
-      result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
-    if r.s[r.pos] == '^':
-      inc(r.pos)
-      var id = decodeVInt(r.s, r.pos)
-      result.typ = rrGetType(r, id, result.info)
-    case result.kind
-    of nkCharLit..nkUInt64Lit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        result.intVal = decodeVBiggestInt(r.s, r.pos)
-    of nkFloatLit..nkFloat64Lit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var fl = decodeStr(r.s, r.pos)
-        result.floatVal = parseFloat(fl)
-    of nkStrLit..nkTripleStrLit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        result.strVal = decodeStr(r.s, r.pos)
-      else:
-        result.strVal = ""    # BUGFIX
-    of nkIdent:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var fl = decodeStr(r.s, r.pos)
-        result.ident = r.cache.getIdent(fl)
-      else:
-        internalError(r.config, result.info, "decodeNode: nkIdent")
-    of nkSym:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var id = decodeVInt(r.s, r.pos)
-        result.sym = rrGetSym(r, id, result.info)
-      else:
-        internalError(r.config, result.info, "decodeNode: nkSym")
-    else:
-      var i = 0
-      while r.s[r.pos] != ')':
-        if belongsTo != nil and i == bodyPos:
-          addSonNilAllowed(result, nil)
-          belongsTo.offset = r.pos
-          skipNode(r)
-        else:
-          addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
-        inc i
-    if r.s[r.pos] == ')': inc(r.pos)
-    else: internalError(r.config, result.info, "decodeNode: ')' missing")
-  else:
-    internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos)
-
-proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
-  result = decodeNodeLazyBody(r, fInfo, nil)
-
-proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
-  if r.s[r.pos] == '<':
-    inc(r.pos)
-    if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
-      loc.k = TLocKind(decodeVInt(r.s, r.pos))
-    else:
-      loc.k = low(loc.k)
-    if r.s[r.pos] == '*':
-      inc(r.pos)
-      loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
-    else:
-      loc.storage = low(loc.storage)
-    if r.s[r.pos] == '$':
-      inc(r.pos)
-      loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
-    else:
-      loc.flags = {}
-    if r.s[r.pos] == '^':
-      inc(r.pos)
-      loc.lode = decodeNode(r, info)
-      # rrGetType(r, decodeVInt(r.s, r.pos), info)
-    else:
-      loc.lode = nil
-    if r.s[r.pos] == '!':
-      inc(r.pos)
-      loc.r = rope(decodeStr(r.s, r.pos))
-    else:
-      loc.r = nil
-    if r.s[r.pos] == '>': inc(r.pos)
-    else: internalError(r.config, info, "decodeLoc " & r.s[r.pos])
-
-proc decodeType(r: PRodReader, info: TLineInfo): PType =
-  result = nil
-  if r.s[r.pos] == '[':
-    inc(r.pos)
-    if r.s[r.pos] == ']':
-      inc(r.pos)
-      return                  # nil type
-  new(result)
-  result.kind = TTypeKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+':
-    inc(r.pos)
-    result.id = decodeVInt(r.s, r.pos)
-    setId(result.id)
-    if debugIds: registerID(result)
-  else:
-    internalError(r.config, info, "decodeType: no id")
-  # here this also avoids endless recursion for recursive type
-  idTablePut(gTypeTable, result, result)
-  if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
-  if r.s[r.pos] == '$':
-    inc(r.pos)
-    result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '?':
-    inc(r.pos)
-    result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '*':
-    inc(r.pos)
-    result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '&':
-    inc(r.pos)
-    result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '/':
-    inc(r.pos)
-    result.size = decodeVInt(r.s, r.pos)
-  else:
-    result.size = - 1
-  if r.s[r.pos] == '=':
-    inc(r.pos)
-    result.align = decodeVInt(r.s, r.pos).int16
-  else:
-    result.align = 2
-
-  if r.s[r.pos] == '\14':
-    inc(r.pos)
-    result.lockLevel = decodeVInt(r.s, r.pos).TLockLevel
-  else:
-    result.lockLevel = UnspecifiedLockLevel
-
-  if r.s[r.pos] == '\15':
-    inc(r.pos)
-    result.destructor = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\16':
-    inc(r.pos)
-    result.deepCopy = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\17':
-    inc(r.pos)
-    result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\18':
-    inc(r.pos)
-    result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  while r.s[r.pos] == '\19':
-    inc(r.pos)
-    let x = decodeVInt(r.s, r.pos)
-    doAssert r.s[r.pos] == '\20'
-    inc(r.pos)
-    let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-    result.methods.add((x, y))
-  decodeLoc(r, result.loc, info)
-  while r.s[r.pos] == '^':
-    inc(r.pos)
-    if r.s[r.pos] == '(':
-      inc(r.pos)
-      if r.s[r.pos] == ')': inc(r.pos)
-      else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos])
-      rawAddSon(result, nil)
-    else:
-      var d = decodeVInt(r.s, r.pos)
-      rawAddSon(result, rrGetType(r, d, info))
-
-proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
-  result = nil
-  if r.s[r.pos] == '|':
-    new(result)
-    inc(r.pos)
-    result.kind = TLibKind(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 1")
-    inc(r.pos)
-    result.name = rope(decodeStr(r.s, r.pos))
-    if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2")
-    inc(r.pos)
-    result.path = decodeNode(r, info)
-
-proc decodeInstantiations(r: PRodReader; info: TLineInfo;
-                          s: var seq[PInstantiation]) =
-  while r.s[r.pos] == '\15':
-    inc(r.pos)
-    var ii: PInstantiation
-    new ii
-    ii.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-    ii.concreteTypes = @[]
-    while r.s[r.pos] == '\17':
-      inc(r.pos)
-      ii.concreteTypes.add rrGetType(r, decodeVInt(r.s, r.pos), info)
-    if r.s[r.pos] == '\20':
-      inc(r.pos)
-      ii.compilesId = decodeVInt(r.s, r.pos)
-    s.add ii
-
-proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
-  var
-    id: int
-    ident: PIdent
-  result = nil
-  if r.s[r.pos] == '{':
-    inc(r.pos)
-    if r.s[r.pos] == '}':
-      inc(r.pos)
-      return                  # nil sym
-  var k = TSymKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+':
-    inc(r.pos)
-    id = decodeVInt(r.s, r.pos)
-    setId(id)
-  else:
-    internalError(r.config, info, "decodeSym: no id")
-  if r.s[r.pos] == '&':
-    inc(r.pos)
-    ident = r.cache.getIdent(decodeStr(r.s, r.pos))
-  else:
-    internalError(r.config, info, "decodeSym: no ident")
-  #echo "decoding: {", ident.s
-  result = r.syms.getOrDefault(id)
-  if result == nil:
-    new(result)
-    result.id = id
-    r.syms[result.id] = result
-    if debugIds: registerID(result)
-  elif result.id != id:
-    internalError(r.config, info, "decodeSym: wrong id")
-  elif result.kind != skStub and not r.inViewMode:
-    # we already loaded the symbol
-    return
-  else:
-    reset(result[])
-    result.id = id
-  result.kind = k
-  result.name = ident         # read the rest of the symbol description:
-  if r.s[r.pos] == '^':
-    inc(r.pos)
-    result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
-  decodeLineInfo(r, result.info)
-  if r.s[r.pos] == '*':
-    inc(r.pos)
-    result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-  if r.s[r.pos] == '$':
-    inc(r.pos)
-    result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '@':
-    inc(r.pos)
-    result.magic = TMagic(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '!':
-    inc(r.pos)
-    result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-  else:
-    result.options = r.options
-  if r.s[r.pos] == '%':
-    inc(r.pos)
-    result.position = decodeVInt(r.s, r.pos)
-  elif result.kind notin routineKinds + {skModule}:
-    result.position = 0
-    # this may have been misused as reader index! But we still
-    # need it for routines as the body is loaded lazily.
-  if r.s[r.pos] == '`':
-    inc(r.pos)
-    result.offset = decodeVInt(r.s, r.pos)
-  else:
-    result.offset = - 1
-  decodeLoc(r, result.loc, result.info)
-  result.annex = decodeLib(r, info)
-  if r.s[r.pos] == '#':
-    inc(r.pos)
-    result.constraint = decodeNode(r, unknownLineInfo())
-  case result.kind
-  of skType, skGenericParam:
-    while r.s[r.pos] == '\14':
-      inc(r.pos)
-      result.typeInstCache.add rrGetType(r, decodeVInt(r.s, r.pos), result.info)
-  of routineKinds:
-    decodeInstantiations(r, result.info, result.procInstCache)
-    if r.s[r.pos] == '\16':
-      inc(r.pos)
-      result.gcUnsafetyReason = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-  of skModule, skPackage:
-    decodeInstantiations(r, result.info, result.usedGenerics)
-  of skLet, skVar, skField, skForVar:
-    if r.s[r.pos] == '\18':
-      inc(r.pos)
-      result.guard = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-    if r.s[r.pos] == '\19':
-      inc(r.pos)
-      result.bitsize = decodeVInt(r.s, r.pos).int16
-  else: discard
-
-  if r.s[r.pos] == '(':
-    if result.kind in routineKinds:
-      result.ast = decodeNodeLazyBody(r, result.info, result)
-      # since we load the body lazily, we need to set the reader to
-      # be able to reload:
-      result.position = r.readerIndex
-    else:
-      result.ast = decodeNode(r, result.info)
-  #echo "decoded: ", ident.s, "}"
-
-proc skipSection(r: PRodReader) =
-  if r.s[r.pos] == ':':
-    while r.s[r.pos] > '\x0A': inc(r.pos)
-  elif r.s[r.pos] == '(':
-    var c = 0                 # count () pairs
-    inc(r.pos)
-    while true:
-      case r.s[r.pos]
-      of '\x0A': inc(r.line)
-      of '(': inc(c)
-      of ')':
-        if c == 0:
-          inc(r.pos)
-          break
-        elif c > 0:
-          dec(c)
-      of '\0': break          # end of file
-      else: discard
-      inc(r.pos)
-  else:
-    internalError(r.config, "skipSection " & $r.line)
-
-proc rdWord(r: PRodReader): string =
-  result = ""
-  while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
-    add(result, r.s[r.pos])
-    inc(r.pos)
-
-proc newStub(r: PRodReader, name: string, id: int): PSym =
-  new(result)
-  result.kind = skStub
-  result.id = id
-  result.name = r.cache.getIdent(name)
-  result.position = r.readerIndex
-  setId(id)                   #MessageOut(result.name.s);
-  if debugIds: registerID(result)
-
-proc processInterf(r: PRodReader, module: PSym) =
-  if r.interfIdx == 0: internalError(r.config, "processInterf")
-  r.pos = r.interfIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    var w = decodeStr(r.s, r.pos)
-    inc(r.pos)
-    var key = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var s = newStub(r, w, key)
-    s.owner = module
-    strTableAdd(module.tab, s)
-    r.syms[s.id] = s
-
-proc processCompilerProcs(r: PRodReader, module: PSym) =
-  if r.compilerProcsIdx == 0: internalError(r.config, "processCompilerProcs")
-  r.pos = r.compilerProcsIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    var w = decodeStr(r.s, r.pos)
-    inc(r.pos)
-    var key = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var s = r.syms.getOrDefault(key)
-    if s == nil:
-      s = newStub(r, w, key)
-      s.owner = module
-      r.syms[s.id] = s
-    strTableAdd(rodCompilerprocs, s)
-
-proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
-  var key, val, tmp: int
-  inc(r.pos, 2)               # skip "(\10"
-  inc(r.line)
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    tmp = decodeVInt(r.s, r.pos)
-    if r.s[r.pos] == ' ':
-      inc(r.pos)
-      key = idx.lastIdxKey + tmp
-      val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
-    else:
-      key = idx.lastIdxKey + 1
-      val = tmp + idx.lastIdxVal
-    iiTablePut(idx.tab, key, val)
-    if not outf.isNil: outf.write(key, " ", val, "\n")
-    idx.lastIdxKey = key
-    idx.lastIdxVal = val
-    setId(key)                # ensure that this id will not be used
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-  if r.s[r.pos] == ')': inc(r.pos)
-
-proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
-  if old == new: return false
-  # we use a 'case' statement without 'else' so that addition of a
-  # new command forces us to consider it here :-)
-  case old
-  of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
-      cmdCompileToJS, cmdCompileToLLVM:
-    if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
-               cmdInteractive}:
-      return false
-  of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
-      cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
-      cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
-    discard
-  # else: trigger recompilation:
-  result = true
-
-proc processRodFile(r: PRodReader, hash: SecureHash) =
-  var
-    w: string
-    d: int
-  var inclHash: SecureHash
-  while r.s[r.pos] != '\0':
-    var section = rdWord(r)
-    if r.reason != rrNone:
-      break                   # no need to process this file further
-    case section
-    of "HASH":
-      inc(r.pos)              # skip ':'
-      if hash != parseSecureHash(decodeStr(r.s, r.pos)):
-        r.reason = rrHashChange
-    of "ID":
-      inc(r.pos)              # skip ':'
-      r.moduleID = decodeVInt(r.s, r.pos)
-      setId(r.moduleID)
-    of "ORIGFILE":
-      inc(r.pos)
-      r.origFile = decodeStr(r.s, r.pos)
-    of "OPTIONS":
-      inc(r.pos)              # skip ':'
-      r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      if r.config.options != r.options: r.reason = rrOptions
-    of "GOPTIONS":
-      inc(r.pos)              # skip ':'
-      var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
-      if r.config.globalOptions-harmlessOptions != dep-harmlessOptions:
-        r.reason = rrOptions
-    of "CMD":
-      inc(r.pos)              # skip ':'
-      var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
-      if cmdChangeTriggersRecompilation(dep, r.config.cmd): r.reason = rrOptions
-    of "DEFINES":
-      inc(r.pos)              # skip ':'
-      d = 0
-      while r.s[r.pos] > '\x0A':
-        w = decodeStr(r.s, r.pos)
-        inc(d)
-        if not isDefined(r.config, w):
-          r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
-        if r.s[r.pos] == ' ': inc(r.pos)
-      if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines
-    of "FILES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      while r.s[r.pos] != ')':
-        let finalPath = decodeStr(r.s, r.pos)
-        #let resolvedPath = relativePath.findModule(r.origFile)
-        #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
-        r.files.add(fileInfoIdx(r.config, finalPath))
-        inc(r.pos)            # skip #10
-        inc(r.line)
-      if r.s[r.pos] == ')': inc(r.pos)
-    of "INCLUDES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      while r.s[r.pos] != ')':
-        w = r.files[decodeVInt(r.s, r.pos)].toFullPath
-        inc(r.pos)            # skip ' '
-        inclHash = parseSecureHash(decodeStr(r.s, r.pos))
-        if r.reason == rrNone:
-          if not existsFile(w) or (inclHash != secureHashFile(w)):
-            r.reason = rrInclDeps
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-      if r.s[r.pos] == ')': inc(r.pos)
-    of "DEPS":
-      inc(r.pos)              # skip ':'
-      while r.s[r.pos] > '\x0A':
-        r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))])
-        if r.s[r.pos] == ' ': inc(r.pos)
-    of "INTERF":
-      r.interfIdx = r.pos + 2
-      skipSection(r)
-    of "COMPILERPROCS":
-      r.compilerProcsIdx = r.pos + 2
-      skipSection(r)
-    of "INDEX":
-      processIndex(r, r.index)
-    of "IMPORTS":
-      processIndex(r, r.imports)
-    of "CONVERTERS":
-      r.convertersIdx = r.pos + 1
-      skipSection(r)
-    of "METHODS":
-      r.methodsIdx = r.pos + 1
-      skipSection(r)
-    of "DATA":
-      r.dataIdx = r.pos + 2 # "(\10"
-      # We do not read the DATA section here! We read the needed objects on
-      # demand. And the DATA section comes last in the file, so we stop here:
-      break
-    of "INIT":
-      r.initIdx = r.pos + 2   # "(\10"
-      skipSection(r)
-    else:
-      internalError(r.config, "invalid section: '" & section &
-                    "' at " & $r.line & " in " & r.filename)
-      #MsgWriteln("skipping section: " & section &
-      #           " at " & $r.line & " in " & r.filename)
-      skipSection(r)
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-
-
-proc startsWith(buf: cstring, token: string, pos = 0): bool =
-  var s = 0
-  while s < token.len and buf[pos+s] == token[s]: inc s
-  result = s == token.len
-
-proc newRodReader(modfilename: string, hash: SecureHash,
-                  readerIndex: int; cache: IdentCache;
-                  config: ConfigRef): PRodReader =
-  new(result)
-  result.cache = cache
-  result.config = config
-  try:
-    result.memfile = memfiles.open(modfilename)
-  except OSError:
-    return nil
-  result.files = @[]
-  result.modDeps = @[]
-  result.methods = @[]
-  var r = result
-  r.reason = rrNone
-  r.pos = 0
-  r.line = 1
-  r.readerIndex = readerIndex
-  r.filename = modfilename
-  r.syms = initTable[int, PSym]()
-  # we terminate the file explicitly with ``\0``, so the cast to `cstring`
-  # is safe:
-  r.s = cast[cstring](r.memfile.mem)
-  if startsWith(r.s, "NIM:"):
-    initIiTable(r.index.tab)
-    initIiTable(r.imports.tab) # looks like a ROD file
-    inc(r.pos, 4)
-    var version = ""
-    while r.s[r.pos] notin {'\0', '\x0A'}:
-      add(version, r.s[r.pos])
-      inc(r.pos)
-    if r.s[r.pos] == '\x0A': inc(r.pos)
-    if version != RodFileVersion:
-      # since ROD files are only for caching, no backwards compatibility is
-      # needed
-      #echo "expected version ", version, " ", RodFileVersion
-      result.memfile.close
-      result = nil
-  else:
-    result.memfile.close
-    result = nil
-
-proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
-  result = PType(idTableGet(gTypeTable, id))
-  if result == nil:
-    # load the type:
-    var oldPos = r.pos
-    var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey: internalError(r.config, info, "rrGetType")
-    r.pos = d + r.dataIdx
-    result = decodeType(r, info)
-    r.pos = oldPos
-
-type
-  TFileModuleRec = object
-    filename*: string
-    reason*: TReasonForRecompile
-    rd*: PRodReader
-    hash*: SecureHash
-    hashDone*: bool
-
-  TFileModuleMap = seq[TFileModuleRec]
-
-var gMods*: TFileModuleMap = @[]
-
-proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
-  # all compiled modules
-  if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0")
-  var oldPos = rd.pos
-  rd.pos = offset + rd.dataIdx
-  result = decodeSym(rd, info)
-  rd.pos = oldPos
-
-proc findSomeWhere(id: int) =
-  for i in countup(0, high(gMods)):
-    var rd = gMods[i].rd
-    if rd != nil:
-      var d = iiTableGet(rd.index.tab, id)
-      if d != InvalidKey:
-        echo "found id ", id, " in ", gMods[i].filename
-
-proc getReader(moduleId: int): PRodReader =
-  # we can't index 'gMods' here as it's indexed by a *file index* which is not
-  # the module ID! We could introduce a mapping ID->PRodReader but I'll leave
-  # this for later versions if benchmarking shows the linear search causes
-  # problems:
-  for i in 0 ..< gMods.len:
-    result = gMods[i].rd
-    if result != nil and result.moduleID == moduleId: return result
-  return nil
-
-proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
-  result = r.syms.getOrDefault(id)
-  if result == nil:
-    # load the symbol:
-    var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey:
-      # import from other module:
-      var moduleID = iiTableGet(r.imports.tab, id)
-      if moduleID < 0:
-        var x = ""
-        encodeVInt(id, x)
-        internalError(r.config, info, "missing from both indexes: +" & x)
-      var rd = getReader(moduleID)
-      doAssert rd != nil
-      d = iiTableGet(rd.index.tab, id)
-      if d != InvalidKey:
-        result = decodeSymSafePos(rd, d, info)
-      else:
-        var x = ""
-        encodeVInt(id, x)
-        when false: findSomeWhere(id)
-        internalError(r.config, info, "rrGetSym: no reader found: +" & x)
-    else:
-      # own symbol:
-      result = decodeSymSafePos(r, d, info)
-  if result != nil and result.kind == skStub: rawLoadStub(result)
-
-proc loadInitSection*(r: PRodReader): PNode =
-  if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection")
-  var oldPos = r.pos
-  r.pos = r.initIdx
-  result = newNode(nkStmtList)
-  while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
-    var d = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var p = r.pos
-    r.pos = d + r.dataIdx
-    addSon(result, decodeNode(r, unknownLineInfo()))
-    r.pos = p
-  r.pos = oldPos
-
-proc loadConverters(r: PRodReader) =
-  # We have to ensure that no exported converter is a stub anymore, and the
-  # import mechanism takes care of the rest.
-  if r.convertersIdx == 0 or r.dataIdx == 0:
-    internalError(r.config, "importConverters")
-  r.pos = r.convertersIdx
-  while r.s[r.pos] > '\x0A':
-    var d = decodeVInt(r.s, r.pos)
-    discard rrGetSym(r, d, unknownLineInfo())
-    if r.s[r.pos] == ' ': inc(r.pos)
-
-proc loadMethods(r: PRodReader) =
-  if r.methodsIdx == 0 or r.dataIdx == 0:
-    internalError(r.config, "loadMethods")
-  r.pos = r.methodsIdx
-  while r.s[r.pos] > '\x0A':
-    var d = decodeVInt(r.s, r.pos)
-    r.methods.add(rrGetSym(r, d, unknownLineInfo()))
-    if r.s[r.pos] == ' ': inc(r.pos)
-
-proc getHash*(fileIdx: FileIndex): SecureHash =
-  if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone:
-    return gMods[fileIdx.int32].hash
-
-  result = secureHashFile(fileIdx.toFullPath)
-  if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1)
-  gMods[fileIdx.int32].hash = result
-
-template growCache*(cache, pos) =
-  if cache.len <= pos: cache.setLen(pos+1)
-
-proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile =
-  assert fileIdx != InvalidFileIDX
-  growCache gMods, fileIdx.int32
-  if gMods[fileIdx.int32].reason != rrEmpty:
-    # reason has already been computed for this module:
-    return gMods[fileIdx.int32].reason
-  let filename = fileIdx.toFilename
-  var hash = getHash(fileIdx)
-  gMods[fileIdx.int32].reason = rrNone  # we need to set it here to avoid cycles
-  result = rrNone
-  var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt)
-  var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf)
-  if r == nil:
-    result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
-  else:
-    processRodFile(r, hash)
-    result = r.reason
-    if result == rrNone:
-      # check modules it depends on
-      # NOTE: we need to process the entire module graph so that no ID will
-      # be used twice! However, compilation speed does not suffer much from
-      # this, since results are cached.
-      var res = checkDep(systemFileIdx, cache, conf)
-      if res != rrNone: result = rrModDeps
-      for i in countup(0, high(r.modDeps)):
-        res = checkDep(r.modDeps[i], cache, conf)
-        if res != rrNone:
-          result = rrModDeps
-          # we cannot break here, because of side-effects of `checkDep`
-  if result != rrNone:
-    rawMessage(conf, hintProcessing, reasonToFrmt[result] % filename)
-  if result != rrNone or optForceFullMake in conf.globalOptions:
-    # recompilation is necessary:
-    if r != nil: memfiles.close(r.memfile)
-    r = nil
-  gMods[fileIdx.int32].rd = r
-  gMods[fileIdx.int32].reason = result  # now we know better
-
-proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader =
-  if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}:
-    module.id = getID()
-    return nil
-  idgen.loadMaxIds(conf, conf.projectPath / conf.projectName)
-  let fileIdx = module.fileIdx
-  discard checkDep(fileIdx, cache, conf)
-  #if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile")
-  result = gMods[fileIdx.int32].rd
-  if result != nil:
-    module.id = result.moduleID
-    result.syms[module.id] = module
-    processInterf(result, module)
-    processCompilerProcs(result, module)
-    loadConverters(result)
-    loadMethods(result)
-  else:
-    module.id = getID()
-
-proc rawLoadStub(s: PSym) =
-  assert s.kind == skStub
-  #if s.kind != skStub: internalError("loadStub")
-  var rd = gMods[s.position].rd
-  var theId = s.id                # used for later check
-  var d = iiTableGet(rd.index.tab, s.id)
-  #if d == InvalidKey: internalError("loadStub: invalid key")
-  var rs = decodeSymSafePos(rd, d, unknownLineInfo())
-  when false:
-    if rs != s:
-      #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
-      #     "\ns:  ", toHex(cast[int](s.position), int.sizeof * 2)
-      internalError(rs.info, "loadStub: wrong symbol")
-    elif rs.id != theId:
-      internalError(rs.info, "loadStub: wrong ID")
-
-proc loadStub*(s: PSym) =
-  ## loads the stub symbol `s`.
-
-  # deactivate the GC here because we do a deep recursion and generate no
-  # garbage when restoring parts of the object graph anyway.
-  # Since we die with internal errors if this fails, no try-finally is
-  # necessary.
-  GC_disable()
-  rawLoadStub(s)
-  GC_enable()
-
-proc getBody*(s: PSym): PNode =
-  ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file
-  ## it may perform an expensive reload operation. Otherwise it's a simple
-  ## accessor.
-  assert s.kind in routineKinds
-  # prevent crashes due to incorrect macro transformations (bug #2377)
-  if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
-  result = s.ast.sons[bodyPos]
-  if result == nil:
-    assert s.offset != 0
-    var r = gMods[s.position].rd
-    var oldPos = r.pos
-    r.pos = s.offset
-    result = decodeNode(r, s.info)
-    r.pos = oldPos
-    s.ast.sons[bodyPos] = result
-    s.offset = 0
-
-initIdTable(gTypeTable)
-initStrTable(rodCompilerprocs)
-
-# viewer:
-proc writeNode(f: File; n: PNode) =
-  f.write("(")
-  if n != nil:
-    f.write($n.kind)
-    if n.typ != nil:
-      f.write('^')
-      f.write(n.typ.id)
-    case n.kind
-    of nkCharLit..nkUInt64Lit:
-      if n.intVal != 0:
-        f.write('!')
-        f.write(n.intVal)
-    of nkFloatLit..nkFloat64Lit:
-      if n.floatVal != 0.0:
-        f.write('!')
-        f.write($n.floatVal)
-    of nkStrLit..nkTripleStrLit:
-      if n.strVal != "":
-        f.write('!')
-        f.write(n.strVal.escape)
-    of nkIdent:
-      f.write('!')
-      f.write(n.ident.s)
-    of nkSym:
-      f.write('!')
-      f.write(n.sym.id)
-    else:
-      for i in countup(0, sonsLen(n) - 1):
-        writeNode(f, n.sons[i])
-  f.write(")")
-
-proc writeSym(f: File; s: PSym) =
-  if s == nil:
-    f.write("{}\n")
-    return
-  f.write("{")
-  f.write($s.kind)
-  f.write('+')
-  f.write(s.id)
-  f.write('&')
-  f.write(s.name.s)
-  if s.typ != nil:
-    f.write('^')
-    f.write(s.typ.id)
-  if s.owner != nil:
-    f.write('*')
-    f.write(s.owner.id)
-  if s.flags != {}:
-    f.write('$')
-    f.write($s.flags)
-  if s.magic != mNone:
-    f.write('@')
-    f.write($s.magic)
-  f.write('!')
-  f.write($s.options)
-  if s.position != 0:
-    f.write('%')
-    f.write($s.position)
-  if s.offset != -1:
-    f.write('`')
-    f.write($s.offset)
-  if s.constraint != nil:
-    f.write('#')
-    f.writeNode(s.constraint)
-  if s.ast != nil:
-    f.writeNode(s.ast)
-  f.write("}\n")
-
-proc writeType(f: File; t: PType) =
-  if t == nil:
-    f.write("[]\n")
-    return
-  f.write('[')
-  f.write($t.kind)
-  f.write('+')
-  f.write($t.id)
-  if t.n != nil:
-    f.writeNode(t.n)
-  if t.flags != {}:
-    f.write('$')
-    f.write($t.flags)
-  if t.callConv != low(t.callConv):
-    f.write('?')
-    f.write($t.callConv)
-  if t.owner != nil:
-    f.write('*')
-    f.write($t.owner.id)
-  if t.sym != nil:
-    f.write('&')
-    f.write(t.sym.id)
-  if t.size != -1:
-    f.write('/')
-    f.write($t.size)
-  if t.align != 2:
-    f.write('=')
-    f.write($t.align)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      f.write("^()")
-    else:
-      f.write('^')
-      f.write($t.sons[i].id)
-  f.write("]\n")
-
-proc viewFile(rodfile: string) =
-  let conf = newConfigRef()
-  var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf)
-  if r == nil:
-    rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" &
-       rodfile)
-    return
-  r.inViewMode = true
-  var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite)
-  while r.s[r.pos] != '\0':
-    let section = rdWord(r)
-    case section
-    of "HASH":
-      inc(r.pos)              # skip ':'
-      outf.writeLine("HASH:", $decodeVInt(r.s, r.pos))
-    of "ID":
-      inc(r.pos)              # skip ':'
-      r.moduleID = decodeVInt(r.s, r.pos)
-      setId(r.moduleID)
-      outf.writeLine("ID:", $r.moduleID)
-    of "ORIGFILE":
-      inc(r.pos)
-      r.origFile = decodeStr(r.s, r.pos)
-      outf.writeLine("ORIGFILE:", r.origFile)
-    of "OPTIONS":
-      inc(r.pos)              # skip ':'
-      r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("OPTIONS:", $r.options)
-    of "GOPTIONS":
-      inc(r.pos)              # skip ':'
-      let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("GOPTIONS:", $dep)
-    of "CMD":
-      inc(r.pos)              # skip ':'
-      let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("CMD:", $dep)
-    of "DEFINES":
-      inc(r.pos)              # skip ':'
-      var d = 0
-      outf.write("DEFINES:")
-      while r.s[r.pos] > '\x0A':
-        let w = decodeStr(r.s, r.pos)
-        inc(d)
-        outf.write(" ", w)
-        if r.s[r.pos] == ' ': inc(r.pos)
-      outf.write("\n")
-    of "FILES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      outf.write("FILES(\n")
-      while r.s[r.pos] != ')':
-        let relativePath = decodeStr(r.s, r.pos)
-        let resolvedPath = findModule(conf, relativePath, r.origFile)
-        let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
-        r.files.add(fileInfoIdx(conf, finalPath))
-        inc(r.pos)            # skip #10
-        inc(r.line)
-        outf.writeLine finalPath
-      if r.s[r.pos] == ')': inc(r.pos)
-      outf.write(")\n")
-    of "INCLUDES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      outf.write("INCLUDES(\n")
-      while r.s[r.pos] != ')':
-        let w = r.files[decodeVInt(r.s, r.pos)]
-        inc(r.pos)            # skip ' '
-        let inclHash = decodeVInt(r.s, r.pos)
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-        outf.write(w.int32, " ", inclHash, "\n")
-      if r.s[r.pos] == ')': inc(r.pos)
-      outf.write(")\n")
-    of "DEPS":
-      inc(r.pos)              # skip ':'
-      outf.write("DEPS:")
-      while r.s[r.pos] > '\x0A':
-        let v = int32(decodeVInt(r.s, r.pos))
-        r.modDeps.add(r.files[v])
-        if r.s[r.pos] == ' ': inc(r.pos)
-        outf.write(" ", r.files[v].int32)
-      outf.write("\n")
-    of "INTERF",  "COMPILERPROCS":
-      inc r.pos, 2
-      if section == "INTERF": r.interfIdx = r.pos
-      else: r.compilerProcsIdx = r.pos
-      outf.write(section, "(\n")
-      while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-        let w = decodeStr(r.s, r.pos)
-        inc(r.pos)
-        let key = decodeVInt(r.s, r.pos)
-        inc(r.pos)                # #10
-        outf.write(w, " ", key, "\n")
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write(")\n")
-    of "INDEX":
-      outf.write(section, "(\n")
-      processIndex(r, r.index, outf)
-      outf.write(")\n")
-    of "IMPORTS":
-      outf.write(section, "(\n")
-      processIndex(r, r.imports, outf)
-      outf.write(")\n")
-    of "CONVERTERS",  "METHODS":
-      inc r.pos
-      if section == "METHODS": r.methodsIdx = r.pos
-      else: r.convertersIdx = r.pos
-      outf.write(section, ":")
-      while r.s[r.pos] > '\x0A':
-        let d = decodeVInt(r.s, r.pos)
-        outf.write(" ", $d)
-        if r.s[r.pos] == ' ': inc(r.pos)
-      outf.write("\n")
-    of "DATA":
-      inc(r.pos, 2)
-      r.dataIdx = r.pos
-      outf.write("DATA(\n")
-      while r.s[r.pos] != ')':
-        if r.s[r.pos] == '(':
-          outf.writeNode decodeNode(r, unknownLineInfo())
-          outf.write("\n")
-        elif r.s[r.pos] == '[':
-          outf.writeType decodeType(r, unknownLineInfo())
-        else:
-          outf.writeSym decodeSym(r, unknownLineInfo())
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write(")\n")
-    of "INIT":
-      outf.write("INIT(\n")
-      inc r.pos, 2
-      r.initIdx = r.pos
-      while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
-        let d = decodeVInt(r.s, r.pos)
-        inc(r.pos)                # #10
-        #let p = r.pos
-        #r.pos = d + r.dataIdx
-        #outf.writeNode decodeNode(r, UnknownLineInfo())
-        #outf.write("\n")
-        #r.pos = p
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write("<not supported by viewer>)\n")
-    else:
-      internalError(r.config, "invalid section: '" & section &
-                    "' at " & $r.line & " in " & r.filename)
-      skipSection(r)
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-  outf.close
-
-when isMainModule:
-  viewFile(paramStr(1).addFileExt(RodExt))
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
deleted file mode 100644
index 4686baf2b..000000000
--- a/compiler/rodwrite.nim
+++ /dev/null
@@ -1,659 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module is responsible for writing of rod files. Note that writing of
-# rod files is a pass, reading of rod files is not! This is why reading and
-# writing of rod files is split into two different modules.
-
-import
-  intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
-  condsyms, ropes, idents, std / sha1, rodread, passes, idgen,
-  rodutils, modulepaths
-
-from modulegraphs import ModuleGraph
-
-type
-  TRodWriter = object of TPassContext
-    module: PSym
-    hash: SecureHash
-    options: TOptions
-    defines: string
-    inclDeps: string
-    modDeps: string
-    interf: string
-    compilerProcs: string
-    index, imports: TIndex
-    converters, methods: string
-    init: string
-    data: string
-    sstack: TSymSeq          # a stack of symbols to process
-    tstack: TTypeSeq         # a stack of types to process
-    files: TStringSeq
-    origFile: string
-    cache: IdentCache
-    config: ConfigRef
-
-  PRodWriter = ref TRodWriter
-
-proc getDefines(conf: ConfigRef): string =
-  result = ""
-  for d in definedSymbolNames(conf.symbols):
-    if result.len != 0: add(result, " ")
-    add(result, d)
-
-proc fileIdx(w: PRodWriter, filename: string): int =
-  for i in countup(0, high(w.files)):
-    if w.files[i] == filename:
-      return i
-  result = len(w.files)
-  setLen(w.files, result + 1)
-  w.files[result] = filename
-
-template filename*(w: PRodWriter): string =
-  toFilename(FileIndex w.module.position)
-
-proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache;
-                  config: ConfigRef): PRodWriter =
-  new(result)
-  result.config = config
-  result.sstack = @[]
-  result.tstack = @[]
-  initIiTable(result.index.tab)
-  initIiTable(result.imports.tab)
-  result.index.r = ""
-  result.imports.r = ""
-  result.hash = hash
-  result.module = module
-  result.defines = getDefines(config)
-  result.options = config.options
-  result.files = @[]
-  result.inclDeps = ""
-  result.modDeps = ""
-  result.interf = newStringOfCap(2_000)
-  result.compilerProcs = ""
-  result.converters = ""
-  result.methods = ""
-  result.init = ""
-  result.origFile = module.info.toFullPath
-  result.data = newStringOfCap(12_000)
-  result.cache = cache
-
-proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) =
-  if w.modDeps.len != 0: add(w.modDeps, ' ')
-  let resolved = findModule(w.config, dep, info.toFullPath)
-  encodeVInt(fileIdx(w, resolved), w.modDeps)
-
-const
-  rodNL = "\x0A"
-
-proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) =
-  let resolved = findModule(w.config, dep, info.toFullPath)
-  encodeVInt(fileIdx(w, resolved), w.inclDeps)
-  add(w.inclDeps, " ")
-  encodeStr($secureHashFile(resolved), w.inclDeps)
-  add(w.inclDeps, rodNL)
-
-proc pushType(w: PRodWriter, t: PType) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, t.id) == InvalidKey:
-    w.tstack.add(t)
-
-proc pushSym(w: PRodWriter, s: PSym) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, s.id) == InvalidKey:
-    when false:
-      if s.kind == skMethod:
-        echo "encoding ", s.id, " ", s.name.s
-        writeStackTrace()
-    w.sstack.add(s)
-
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
-                result: var string) =
-  if n == nil:
-    # nil nodes have to be stored too:
-    result.add("()")
-    return
-  result.add('(')
-  encodeVInt(ord(n.kind), result)
-  # we do not write comments for now
-  # Line information takes easily 20% or more of the filesize! Therefore we
-  # omit line information if it is the same as the father's line information:
-  if fInfo.fileIndex != n.info.fileIndex:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-    result.add(',')
-    encodeVInt(fileIdx(w, toFullPath(n.info)), result)
-  elif fInfo.line != n.info.line:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-  elif fInfo.col != n.info.col:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-  # No need to output the file index, as this is the serialization of one
-  # file.
-  var f = n.flags * PersistentNodeFlags
-  if f != {}:
-    result.add('$')
-    encodeVInt(cast[int32](f), result)
-  if n.typ != nil:
-    result.add('^')
-    encodeVInt(n.typ.id, result)
-    pushType(w, n.typ)
-  case n.kind
-  of nkCharLit..nkUInt64Lit:
-    if n.intVal != 0:
-      result.add('!')
-      encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit:
-    if n.floatVal != 0.0:
-      result.add('!')
-      encodeStr($n.floatVal, result)
-  of nkStrLit..nkTripleStrLit:
-    if n.strVal != "":
-      result.add('!')
-      encodeStr(n.strVal, result)
-  of nkIdent:
-    result.add('!')
-    encodeStr(n.ident.s, result)
-  of nkSym:
-    result.add('!')
-    encodeVInt(n.sym.id, result)
-    pushSym(w, n.sym)
-  else:
-    for i in countup(0, sonsLen(n) - 1):
-      encodeNode(w, n.info, n.sons[i], result)
-  add(result, ')')
-
-proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
-  var oldLen = result.len
-  result.add('<')
-  if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
-  if loc.storage != low(loc.storage):
-    add(result, '*')
-    encodeVInt(ord(loc.storage), result)
-  if loc.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](loc.flags), result)
-  if loc.lode != nil:
-    add(result, '^')
-    encodeNode(w, unknownLineInfo(), loc.lode, result)
-    #encodeVInt(cast[int32](loc.t.id), result)
-    #pushType(w, loc.t)
-  if loc.r != nil:
-    add(result, '!')
-    encodeStr($loc.r, result)
-  if oldLen + 1 == result.len:
-    # no data was necessary, so remove the '<' again:
-    setLen(result, oldLen)
-  else:
-    add(result, '>')
-
-proc encodeType(w: PRodWriter, t: PType, result: var string) =
-  if t == nil:
-    # nil nodes have to be stored too:
-    result.add("[]")
-    return
-  # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError(w.config, "encodeType: tyForward")
-  # for the new rodfile viewer we use a preceding [ so that the data section
-  # can easily be disambiguated:
-  add(result, '[')
-  encodeVInt(ord(t.kind), result)
-  add(result, '+')
-  encodeVInt(t.id, result)
-  if t.n != nil:
-    encodeNode(w, unknownLineInfo(), t.n, result)
-  if t.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv):
-    add(result, '?')
-    encodeVInt(ord(t.callConv), result)
-  if t.owner != nil:
-    add(result, '*')
-    encodeVInt(t.owner.id, result)
-    pushSym(w, t.owner)
-  if t.sym != nil:
-    add(result, '&')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-  if t.size != - 1:
-    add(result, '/')
-    encodeVBiggestInt(t.size, result)
-  if t.align != 2:
-    add(result, '=')
-    encodeVInt(t.align, result)
-  if t.lockLevel.ord != UnspecifiedLockLevel.ord:
-    add(result, '\14')
-    encodeVInt(t.lockLevel.int16, result)
-  if t.destructor != nil and t.destructor.id != 0:
-    add(result, '\15')
-    encodeVInt(t.destructor.id, result)
-    pushSym(w, t.destructor)
-  if t.deepCopy != nil:
-    add(result, '\16')
-    encodeVInt(t.deepcopy.id, result)
-    pushSym(w, t.deepcopy)
-  if t.assignment != nil:
-    add(result, '\17')
-    encodeVInt(t.assignment.id, result)
-    pushSym(w, t.assignment)
-  if t.sink != nil:
-    add(result, '\18')
-    encodeVInt(t.sink.id, result)
-    pushSym(w, t.sink)
-  for i, s in items(t.methods):
-    add(result, '\19')
-    encodeVInt(i, result)
-    add(result, '\20')
-    encodeVInt(s.id, result)
-    pushSym(w, s)
-  encodeLoc(w, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      add(result, "^()")
-    else:
-      add(result, '^')
-      encodeVInt(t.sons[i].id, result)
-      pushType(w, t.sons[i])
-
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
-  add(result, '|')
-  encodeVInt(ord(lib.kind), result)
-  add(result, '|')
-  encodeStr($lib.name, result)
-  add(result, '|')
-  encodeNode(w, info, lib.path, result)
-
-proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
-                          result: var string) =
-  for t in s:
-    result.add('\15')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-    for tt in t.concreteTypes:
-      result.add('\17')
-      encodeVInt(tt.id, result)
-      pushType(w, tt)
-    result.add('\20')
-    encodeVInt(t.compilesId, result)
-
-proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
-  if s == nil:
-    # nil nodes have to be stored too:
-    result.add("{}")
-    return
-  # we need no surrounding {} here because the symbol is in a line of its own
-  encodeVInt(ord(s.kind), result)
-  result.add('+')
-  encodeVInt(s.id, result)
-  result.add('&')
-  encodeStr(s.name.s, result)
-  if s.typ != nil:
-    result.add('^')
-    encodeVInt(s.typ.id, result)
-    pushType(w, s.typ)
-  result.add('?')
-  if s.info.col != -1'i16: encodeVInt(s.info.col, result)
-  result.add(',')
-  if s.info.line != 0'u16: encodeVInt(int s.info.line, result)
-  result.add(',')
-  encodeVInt(fileIdx(w, toFullPath(s.info)), result)
-  if s.owner != nil:
-    result.add('*')
-    encodeVInt(s.owner.id, result)
-    pushSym(w, s.owner)
-  if s.flags != {}:
-    result.add('$')
-    encodeVInt(cast[int32](s.flags), result)
-  if s.magic != mNone:
-    result.add('@')
-    encodeVInt(ord(s.magic), result)
-  if s.options != w.options:
-    result.add('!')
-    encodeVInt(cast[int32](s.options), result)
-  if s.position != 0:
-    result.add('%')
-    encodeVInt(s.position, result)
-  if s.offset != - 1:
-    result.add('`')
-    encodeVInt(s.offset, result)
-  encodeLoc(w, s.loc, result)
-  if s.annex != nil: encodeLib(w, s.annex, s.info, result)
-  if s.constraint != nil:
-    add(result, '#')
-    encodeNode(w, unknownLineInfo(), s.constraint, result)
-  case s.kind
-  of skType, skGenericParam:
-    for t in s.typeInstCache:
-      result.add('\14')
-      encodeVInt(t.id, result)
-      pushType(w, t)
-  of routineKinds:
-    encodeInstantiations(w, s.procInstCache, result)
-    if s.gcUnsafetyReason != nil:
-      result.add('\16')
-      encodeVInt(s.gcUnsafetyReason.id, result)
-      pushSym(w, s.gcUnsafetyReason)
-  of skModule, skPackage:
-    encodeInstantiations(w, s.usedGenerics, result)
-    # we don't serialize:
-    #tab*: TStrTable         # interface table for modules
-  of skLet, skVar, skField, skForVar:
-    if s.guard != nil:
-      result.add('\18')
-      encodeVInt(s.guard.id, result)
-      pushSym(w, s.guard)
-    if s.bitsize != 0:
-      result.add('\19')
-      encodeVInt(s.bitsize, result)
-  else: discard
-  # lazy loading will soon reload the ast lazily, so the ast needs to be
-  # the last entry of a symbol:
-  if s.ast != nil:
-    # we used to attempt to save space here by only storing a dummy AST if
-    # it is not necessary, but Nim's heavy compile-time evaluation features
-    # make that unfeasible nowadays:
-    encodeNode(w, s.info, s.ast, result)
-
-proc addToIndex(w: var TIndex, key, val: int) =
-  if key - w.lastIdxKey == 1:
-    # we do not store a key-diff of 1 to safe space
-    encodeVInt(val - w.lastIdxVal, w.r)
-  else:
-    encodeVInt(key - w.lastIdxKey, w.r)
-    add(w.r, ' ')
-    encodeVInt(val - w.lastIdxVal, w.r)
-  add(w.r, rodNL)
-  w.lastIdxKey = key
-  w.lastIdxVal = val
-  iiTablePut(w.tab, key, val)
-
-const debugWrittenIds = false
-
-when debugWrittenIds:
-  var debugWritten = initIntSet()
-
-proc symStack(w: PRodWriter): int =
-  var i = 0
-  while i < len(w.sstack):
-    var s = w.sstack[i]
-    if sfForward in s.flags:
-      w.sstack[result] = s
-      inc result
-    elif iiTableGet(w.index.tab, s.id) == InvalidKey:
-      var m = getModule(s)
-      #if m == nil and s.kind != skPackage and sfGenSym notin s.flags:
-      #  internalError("symStack: module nil: " & s.name.s & " " & $s.kind & " ID " & $s.id)
-      if m == nil or s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id:
-        # put definition in here
-        var L = w.data.len
-        addToIndex(w.index, s.id, L)
-        when debugWrittenIds: incl(debugWritten, s.id)
-        encodeSym(w, s, w.data)
-        add(w.data, rodNL)
-        # put into interface section if appropriate:
-        if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
-            s.kind in ExportableSymKinds:
-          encodeStr(s.name.s, w.interf)
-          add(w.interf, ' ')
-          encodeVInt(s.id, w.interf)
-          add(w.interf, rodNL)
-        if sfCompilerProc in s.flags:
-          encodeStr(s.name.s, w.compilerProcs)
-          add(w.compilerProcs, ' ')
-          encodeVInt(s.id, w.compilerProcs)
-          add(w.compilerProcs, rodNL)
-        if s.kind == skConverter or (s.ast != nil and hasPattern(s)):
-          if w.converters.len != 0: add(w.converters, ' ')
-          encodeVInt(s.id, w.converters)
-        if s.kind == skMethod and sfDispatcher notin s.flags:
-          if w.methods.len != 0: add(w.methods, ' ')
-          encodeVInt(s.id, w.methods)
-      elif iiTableGet(w.imports.tab, s.id) == InvalidKey:
-        addToIndex(w.imports, s.id, m.id)
-        when debugWrittenIds:
-          if not contains(debugWritten, s.id):
-            echo(w.filename)
-            debug(s)
-            debug(s.owner)
-            debug(m)
-            internalError("Symbol referred to but never written")
-    inc(i)
-  setLen(w.sstack, result)
-
-proc typeStack(w: PRodWriter): int =
-  var i = 0
-  while i < len(w.tstack):
-    var t = w.tstack[i]
-    if t.kind == tyForward:
-      w.tstack[result] = t
-      inc result
-    elif iiTableGet(w.index.tab, t.id) == InvalidKey:
-      var L = w.data.len
-      addToIndex(w.index, t.id, L)
-      encodeType(w, t, w.data)
-      add(w.data, rodNL)
-    inc(i)
-  setLen(w.tstack, result)
-
-proc processStacks(w: PRodWriter, finalPass: bool) =
-  var oldS = 0
-  var oldT = 0
-  while true:
-    var slen = symStack(w)
-    var tlen = typeStack(w)
-    if slen == oldS and tlen == oldT: break
-    oldS = slen
-    oldT = tlen
-  if finalPass and (oldS != 0 or oldT != 0):
-    internalError(w.config, "could not serialize some forwarded symbols/types")
-
-proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
-  pushSym(w, s)
-  processStacks(w, false)
-
-proc addInterfaceSym(w: PRodWriter, s: PSym) =
-  if w == nil: return
-  if s.kind in ExportableSymKinds and
-      {sfExported, sfCompilerProc} * s.flags != {}:
-    rawAddInterfaceSym(w, s)
-
-proc addStmt(w: PRodWriter, n: PNode) =
-  encodeVInt(w.data.len, w.init)
-  add(w.init, rodNL)
-  encodeNode(w, unknownLineInfo(), n, w.data)
-  add(w.data, rodNL)
-  processStacks(w, false)
-
-proc writeRod(w: PRodWriter) =
-  processStacks(w, true)
-  var f: File
-  if not open(f, completeGeneratedFilePath(w.config, changeFileExt(
-                      withPackageName(w.config, w.filename), RodExt)),
-              fmWrite):
-    #echo "couldn't write rod file for: ", w.filename
-    return
-  # write header:
-  f.write("NIM:")
-  f.write(RodFileVersion)
-  f.write(rodNL)
-  var id = "ID:"
-  encodeVInt(w.module.id, id)
-  f.write(id)
-  f.write(rodNL)
-
-  var orig = "ORIGFILE:"
-  encodeStr(w.origFile, orig)
-  f.write(orig)
-  f.write(rodNL)
-
-  var hash = "HASH:"
-  encodeStr($w.hash, hash)
-  f.write(hash)
-  f.write(rodNL)
-
-  var options = "OPTIONS:"
-  encodeVInt(cast[int32](w.options), options)
-  f.write(options)
-  f.write(rodNL)
-
-  var goptions = "GOPTIONS:"
-  encodeVInt(cast[int32](w.config.globalOptions), goptions)
-  f.write(goptions)
-  f.write(rodNL)
-
-  var cmd = "CMD:"
-  encodeVInt(cast[int32](w.config.cmd), cmd)
-  f.write(cmd)
-  f.write(rodNL)
-
-  f.write("DEFINES:")
-  f.write(w.defines)
-  f.write(rodNL)
-
-  var files = "FILES(" & rodNL
-  for i in countup(0, high(w.files)):
-    encodeStr(w.files[i], files)
-    files.add(rodNL)
-  f.write(files)
-  f.write(')' & rodNL)
-
-  f.write("INCLUDES(" & rodNL)
-  f.write(w.inclDeps)
-  f.write(')' & rodNL)
-
-  f.write("DEPS:")
-  f.write(w.modDeps)
-  f.write(rodNL)
-
-  f.write("INTERF(" & rodNL)
-  f.write(w.interf)
-  f.write(')' & rodNL)
-
-  f.write("COMPILERPROCS(" & rodNL)
-  f.write(w.compilerProcs)
-  f.write(')' & rodNL)
-
-  f.write("INDEX(" & rodNL)
-  f.write(w.index.r)
-  f.write(')' & rodNL)
-
-  f.write("IMPORTS(" & rodNL)
-  f.write(w.imports.r)
-  f.write(')' & rodNL)
-
-  f.write("CONVERTERS:")
-  f.write(w.converters)
-  f.write(rodNL)
-
-  f.write("METHODS:")
-  f.write(w.methods)
-  f.write(rodNL)
-
-  f.write("INIT(" & rodNL)
-  f.write(w.init)
-  f.write(')' & rodNL)
-
-  f.write("DATA(" & rodNL)
-  f.write(w.data)
-  f.write(')' & rodNL)
-  # write trailing zero which is necessary because we use memory mapped files
-  # for reading:
-  f.write("\0")
-  f.close()
-
-  #echo "interf: ", w.interf.len
-  #echo "index:  ", w.index.r.len
-  #echo "init:   ", w.init.len
-  #echo "data:   ", w.data.len
-
-proc process(c: PPassContext, n: PNode): PNode =
-  result = n
-  if c == nil: return
-  var w = PRodWriter(c)
-  case n.kind
-  of nkStmtList:
-    for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
-    #var s = n.sons[namePos].sym
-    #addInterfaceSym(w, s)
-  of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
-      nkTemplateDef, nkMacroDef:
-    let s = n.sons[namePos].sym
-    if s == nil: internalError(w.config, n.info, "rodwrite.process")
-    if n.sons[bodyPos] == nil:
-      internalError(w.config, n.info, "rodwrite.process: body is nil")
-    if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
-        sfForward notin s.flags:
-      addInterfaceSym(w, s)
-  of nkMethodDef:
-    let s = n.sons[namePos].sym
-    if s == nil: internalError(w.config, n.info, "rodwrite.process")
-    if n.sons[bodyPos] == nil:
-      internalError(w.config, n.info, "rodwrite.process: body is nil")
-    if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
-        sfForward notin s.flags:
-      pushSym(w, s)
-      processStacks(w, false)
-
-  of nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      addInterfaceSym(w, a.sons[0].sym)
-  of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      if a.sons[0].kind != nkSym: internalError(w.config, a.info, "rodwrite.process")
-      var s = a.sons[0].sym
-      addInterfaceSym(w, s)
-      # this takes care of enum fields too
-      # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
-      # type aliasing! Otherwise the same enum symbol would be included
-      # several times!
-      #
-      #        if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
-      #          a := s.typ.n;
-      #          for j := 0 to sonsLen(a)-1 do
-      #            addInterfaceSym(w, a.sons[j].sym);
-      #        end
-  of nkImportStmt:
-    for i in countup(0, sonsLen(n) - 1):
-      addModDep(w, getModuleName(w.config, n.sons[i]), n.info)
-    addStmt(w, n)
-  of nkFromStmt, nkImportExceptStmt:
-    addModDep(w, getModuleName(w.config, n.sons[0]), n.info)
-    addStmt(w, n)
-  of nkIncludeStmt:
-    for i in countup(0, sonsLen(n) - 1):
-      addInclDep(w, getModuleName(w.config, n.sons[i]), n.info)
-  of nkPragma:
-    addStmt(w, n)
-  else:
-    discard
-
-proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  if module.id < 0: internalError(g.config, "rodwrite: module ID not set")
-  var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache, g.config)
-  rawAddInterfaceSym(w, module)
-  result = w
-
-proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
-  result = process(c, n)
-  var w = PRodWriter(c)
-  writeRod(w)
-  idgen.saveMaxIds(graph.config, graph.config.projectPath / graph.config.projectName)
-
-const rodwritePass* = makePass(open = myOpen, close = myClose, process = process)
-
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 05d5e840c..973f16916 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -56,7 +56,7 @@
 #  To cache them they are inserted in a `cache` array.
 
 import
-  platform, hashes
+  hashes
 
 type
   FormatStr* = string  # later we may change it to CString for better
@@ -70,17 +70,6 @@ type
     length*: int
     data*: string             # != nil if a leaf
 
-  RopeSeq* = seq[Rope]
-
-  RopesError* = enum
-    rCannotOpenFile
-    rInvalidFormatStr
-
-# implementation
-
-var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
-  # avoid dependency on msgs.nim
-
 proc len*(a: Rope): int =
   ## the rope's length
   if a == nil: result = 0
@@ -102,7 +91,7 @@ proc freezeMutableRope*(r: Rope) {.inline.} =
   r.length = r.data.len
 
 var
-  cache: array[0..2048*2 - 1, Rope]
+  cache: array[0..2048*2 - 1, Rope] # XXX Global here!
 
 proc resetRopeCache* =
   for i in low(cache)..high(cache):
@@ -204,13 +193,14 @@ proc writeRope*(f: File, r: Rope) =
   ## writes a rope to a file.
   for s in leaves(r): write(f, s)
 
-proc writeRope*(head: Rope, filename: string, useWarning = false) =
+proc writeRope*(head: Rope, filename: string): bool =
   var f: File
   if open(f, filename, fmWrite):
     if head != nil: writeRope(f, head)
     close(f)
+    result = true
   else:
-    errorHandler(rCannotOpenFile, filename, useWarning)
+    result = false
 
 proc `$`*(r: Rope): string =
   ## converts a rope back to a string.
@@ -225,11 +215,6 @@ proc ropeConcat*(a: varargs[Rope]): Rope =
 proc prepend*(a: var Rope, b: Rope) = a = b & a
 proc prepend*(a: var Rope, b: string) = a = b & a
 
-var
-  rnl* = tnl.newRope
-  softRnl* = tnl.newRope
-  noRnl* = "".newRope
-
 proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
   var i = 0
   var length = len(frmt)
@@ -254,7 +239,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
           if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
-          errorHandler(rInvalidFormatStr, $(j))
+          doAssert false, "invalid format string: " & frmt
         else:
           add(result, args[j-1])
       of '{':
@@ -265,20 +250,21 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
           inc(i)
         num = j
         if frmt[i] == '}': inc(i)
-        else: errorHandler(rInvalidFormatStr, $(frmt[i]))
+        else:
+          doAssert false, "invalid format string: " & frmt
 
         if j > high(args) + 1:
-          errorHandler(rInvalidFormatStr, $(j))
+          doAssert false, "invalid format string: " & frmt
         else:
           add(result, args[j-1])
       of 'n':
-        add(result, softRnl)
+        add(result, "\n")
         inc(i)
       of 'N':
-        add(result, rnl)
+        add(result, "\n")
         inc(i)
       else:
-        errorHandler(rInvalidFormatStr, $(frmt[i]))
+        doAssert false, "invalid format string: " & frmt
     var start = i
     while i < length:
       if frmt[i] != '$': inc(i)
@@ -350,7 +336,6 @@ proc equalsFile*(r: Rope, filename: string): bool =
 proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
   # returns true if overwritten
   if not equalsFile(r, filename):
-    writeRope(r, filename)
-    result = true
+    result = writeRope(r, filename)
   else:
     result = false
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 30e5e4803..ae7e030b8 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -13,7 +13,7 @@
 import
   ast, modules, idents, passes, passaux, condsyms,
   options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
-  os, times, osproc, wordrecg, strtabs, modulegraphs, configuration
+  os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
 from strutils import cmpIgnoreStyle, contains
@@ -152,28 +152,27 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
 proc runNimScript*(cache: IdentCache; scriptName: string;
                    freshDefines=true; conf: ConfigRef) =
   rawMessage(conf, hintConf, scriptName)
-  passes.gIncludeFile = includeModule
-  passes.gImportModule = importModule
-  let graph = newModuleGraph(conf)
+
+  let graph = newModuleGraph(cache, conf)
+  connectCallbacks(graph)
   if freshDefines: initDefines(conf.symbols)
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(semPass)
-  registerPass(evalPass)
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
 
   conf.searchPaths.add(conf.libpath)
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  vm.globalCtx = setupVM(m, cache, scriptName, graph)
+  graph.vm = setupVM(m, cache, scriptName, graph)
 
-  graph.compileSystemModule(cache)
-  discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)
+  graph.compileSystemModule()
+  discard graph.processModule(m, llStreamOpen(scriptName, fmRead))
 
   # ensure we load 'system.nim' again for the real non-config stuff!
   resetSystemArtifacts(graph)
-  vm.globalCtx = nil
   # do not remove the defined symbols
   #initDefines()
   undefSymbol(conf.symbols, "nimscript")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 55e1f47dc..45feed1b8 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -13,10 +13,10 @@ import
   ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, modulepaths, importer,
-  procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
+  procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
-  semparallel, lowerings, pluginsupport, plugins.active, rod, configuration
+  semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -53,10 +53,10 @@ proc isArrayConstr(n: PNode): bool {.inline.} =
   result = n.kind == nkBracket and
     n.typ.skipTypes(abstractInst).kind == tyArray
 
-template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
+template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) =
   # we check quickly if the node is where the cursor is
   when defined(nimsuggest):
-    if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
+    if n.info.fileIndex == conf.m.trackPos.fileIndex and n.info.line == conf.m.trackPos.line:
       requiresCheck = true
 
 template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
@@ -96,7 +96,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
 proc inferWithMetatype(c: PContext, formal: PType,
                        arg: PNode, coerceDistincts = false): PNode
 
-var commonTypeBegin = PType(kind: tyExpr)
+template commonTypeBegin*(): PType = PType(kind: tyExpr)
 
 proc commonType*(x, y: PType): PType =
   # new type relation that is used for array constructors,
@@ -181,7 +181,7 @@ proc commonType*(x: PType, y: PNode): PType =
   commonType(x, y.typ)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
@@ -205,7 +205,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # template; we must fix it here: see #909
     result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -240,14 +240,14 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode
 
-proc symFromType(t: PType, info: TLineInfo): PSym =
+proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
   if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent"AnonType", t.owner, info)
+  result = newSym(skType, getIdent(c.cache, "AnonType"), t.owner, info)
   result.flags.incl sfAnon
   result.typ = t
 
 proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(t, info), info)
+  result = newSymNode(symFromType(c, t, info), info)
   result.typ = makeTypeDesc(c, t)
 
 when false:
@@ -308,13 +308,13 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
 
   let oldErrorCount = c.config.errorCounter
   let oldErrorMax = c.config.errorMax
-  let oldErrorOutputs = errorOutputs
+  let oldErrorOutputs = c.config.m.errorOutputs
 
-  errorOutputs = {}
+  c.config.m.errorOutputs = {}
   c.config.errorMax = high(int)
 
   try:
-    result = evalConstExpr(c.module, c.cache, c.graph, e)
+    result = evalConstExpr(c.module, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       result = nil
     else:
@@ -325,7 +325,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
 
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
-  errorOutputs = oldErrorOutputs
+  c.config.m.errorOutputs = oldErrorOutputs
 
 const
   errConstExprExpected = "constant expression expected"
@@ -338,12 +338,12 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
   result = getConstExpr(c.module, e, c.graph)
   if result == nil:
     #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
-    result = evalConstExpr(c.module, c.cache, c.graph, e)
+    result = evalConstExpr(c.module, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       if e.info != n.info:
-        pushInfoContext(n.info)
+        pushInfoContext(c.config, n.info)
         localError(c.config, e.info, errConstExprExpected)
-        popInfoContext()
+        popInfoContext(c.config)
       else:
         localError(c.config, e.info, errConstExprExpected)
       # error correction:
@@ -381,8 +381,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   ## coherence, making sure that variables declared with 'let' aren't
   ## reassigned, and binding the unbound identifiers that the macro output
   ## contains.
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > evalTemplateLimit:
+  inc(c.config.evalTemplateCounter)
+  if c.config.evalTemplateCounter > evalTemplateLimit:
     globalError(c.config, s.info, "template instantiation too nested")
   c.friendModules.add(s.owner.getModule)
 
@@ -421,8 +421,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
 
       result = semExpr(c, result, flags)
       result = fitNode(c, retType, result, result.info)
-      #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
-  dec(evalTemplateCounter)
+      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
+  dec(c.config.evalTemplateCounter)
   discard c.friendModules.pop()
 
 const
@@ -430,7 +430,7 @@ const
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode =
-  pushInfoContext(nOrig.info)
+  pushInfoContext(c.config, nOrig.info)
 
   markUsed(c.config, n.info, sym, c.graph.usageSym)
   styleCheckUse(n.info, sym)
@@ -446,11 +446,11 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-  result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym)
+  result = evalMacroCall(c.module, c.graph, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, n, result, sym, flags)
   result = wrapInComesFrom(nOrig.info, sym, result)
-  popInfoContext()
+  popInfoContext(c.config)
 
 proc forceBool(c: PContext, n: PNode): PNode =
   result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
@@ -482,8 +482,8 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
         addSon(n, prc.ast)
   c.lastGenericIdx = c.generics.len
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  var c = newContext(graph, module, cache)
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+  var c = newContext(graph, module)
   if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
   c.semExpr = semExpr
@@ -505,19 +505,13 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
     graph.systemModule = module
   c.topLevelScope = openScope(c)
   # don't be verbose unless the module belongs to the main package:
-  if module.owner.id == gMainPackageId:
+  if module.owner.id == graph.config.mainPackageId:
     graph.config.notes = graph.config.mainPackageNotes
   else:
     if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
     graph.config.notes = graph.config.foreignPackageNotes
   result = c
 
-proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext =
-  result = myOpen(graph, module, rd.cache)
-
-proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) =
-  for m in items(rd.methods): methodDef(graph, m, true)
-
 proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
   if g.systemModule == nil: return false
   case n.kind
@@ -583,31 +577,31 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
   if c.config.errorMax <= 1:
     result = semStmtAndGenerateGenerics(c, n)
   else:
-    let oldContextLen = msgs.getInfoContextLen()
+    let oldContextLen = msgs.getInfoContextLen(c.config)
     let oldInGenericInst = c.inGenericInst
     try:
       result = semStmtAndGenerateGenerics(c, n)
     except ERecoverableError, ESuggestDone:
       recoverContext(c)
       c.inGenericInst = oldInGenericInst
-      msgs.setInfoContextLen(oldContextLen)
+      msgs.setInfoContextLen(c.config, oldContextLen)
       if getCurrentException() of ESuggestDone:
         c.suggestionsMade = true
         result = nil
       else:
-        result = ast.emptyNode
+        result = newNodeI(nkEmpty, n.info)
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
-  rod.storeNode(c.module, result)
+  rod.storeNode(c.graph, c.module, result)
 
 proc testExamples(c: PContext) =
-  let inp = toFullPath(c.module.info)
+  let inp = toFullPath(c.config, c.module.info)
   let outp = inp.changeFileExt"" & "_examples.nim"
   renderModule(c.runnableExamples, inp, outp)
   let backend = if isDefined(c.config, "js"): "js"
                 elif isDefined(c.config, "cpp"): "cpp"
                 elif isDefined(c.config, "objc"): "objc"
                 else: "c"
-  if os.execShellCmd("nim " & backend & " -r " & outp) != 0:
+  if os.execShellCmd(os.getAppFilename() & " " & backend & " -r " & outp) != 0:
     quit "[Examples] failed"
   removeFile(outp)
 
@@ -623,12 +617,10 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   addCodeForGenerics(c, result)
   if c.module.ast != nil:
     result.add(c.module.ast)
-  if c.rd != nil:
-    replayMethodDefs(graph, c.rd)
   popOwner(c)
   popProcCon(c)
-  storeRemaining(c.module)
+  storeRemaining(c.graph, c.module)
   if c.runnableExamples != nil: testExamples(c)
 
-const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose,
+const semPass* = makePass(myOpen, myProcess, myClose,
                           isFrontend = true)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index f0fde195c..97ff4a7fc 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -157,12 +157,12 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 proc addVar(father, v, value: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = value
   addSon(father, vpart)
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
+  var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
   temp.typ = getSysType(c.c.graph, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
@@ -207,7 +207,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       if t.kind == tySequence:
         # XXX add 'nil' handling here
         body.add newSeqCall(c.c, x, y)
-      let i = declareCounter(c, body, firstOrd(t))
+      let i = declareCounter(c, body, firstOrd(c.c.config, t))
       let whileLoop = genWhileLoop(c, i, x)
       let elemType = t.lastSon
       liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -268,17 +268,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   a.kind = kind
   let body = newNodeI(nkStmtList, info)
   let procname = case kind
-                 of attachedAsgn: getIdent"="
-                 of attachedSink: getIdent"=sink"
-                 of attachedDeepCopy: getIdent"=deepcopy"
-                 of attachedDestructor: getIdent"=destroy"
+                 of attachedAsgn: getIdent(c.cache, "=")
+                 of attachedSink: getIdent(c.cache, "=sink")
+                 of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
+                 of attachedDestructor: getIdent(c.cache, "=destroy")
 
   result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
   a.asgnForType = typ
 
-  let dest = newSym(skParam, getIdent"dest", result, info)
-  let src = newSym(skParam, getIdent"src", result, info)
+  let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
+  let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
   dest.typ = makeVarType(c, typ)
   src.typ = typ
 
@@ -297,7 +297,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   of attachedDestructor: typ.destructor = result
 
   var n = newNodeI(nkProcDef, info, bodyPos+1)
-  for i in 0 ..< n.len: n.sons[i] = emptyNode
+  for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
   n.sons[namePos] = newSymNode(result)
   n.sons[paramsPos] = result.typ.n
   n.sons[bodyPos] = body
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index df99d6c24..5d3df064f 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -170,7 +170,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
       add(candidates, renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
     else:
-      add(candidates, err.sym.getProcHeader(prefer))
+      add(candidates, getProcHeader(c.config, err.sym, prefer))
     add(candidates, "\n")
     if err.firstMismatch != 0 and n.len > 1:
       let cond = n.len > 2
@@ -213,7 +213,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
   # as semOverlodedCall is already pretty slow (and we need this information
   # only in case of an error).
-  if errorOutputs == {}:
+  if c.config.m.errorOutputs == {}:
     # fail fast:
     globalError(c.config, n.info, "type mismatch")
   if errors.len == 0:
@@ -293,7 +293,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       orig.sons[0..1] = [nil, orig[1], f]
 
       template tryOp(x) =
-        let op = newIdentNode(getIdent(x), n.info)
+        let op = newIdentNode(getIdent(c.cache, x), n.info)
         n.sons[0] = op
         orig.sons[0] = op
         pickBest(op)
@@ -306,17 +306,17 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
     elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
       # we need to strip away the trailing '=' here:
-      let calleeName = newIdentNode(getIdent(f.ident.s[0..f.ident.s.len-2]), n.info)
-      let callOp = newIdentNode(getIdent".=", n.info)
+      let calleeName = newIdentNode(getIdent(c.cache, f.ident.s[0..f.ident.s.len-2]), n.info)
+      let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
       pickBest(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
       if nfDotField in n.flags and nfExplicitCall notin n.flags:
-        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s)
+        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s)
       else:
-        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s)
+        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s)
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -333,7 +333,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     internalAssert c.config, result.state == csMatch
     #writeMatches(result)
     #writeMatches(alt)
-    if errorOutputs == {}:
+    if c.config.m.errorOutputs == {}:
       # quick error message for performance of 'compiles' built-in:
       globalError(c.config, n.info, errGenerated, "ambiguous call")
     elif c.config.errorCounter == 0:
@@ -345,7 +345,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       add(args, ")")
 
       localError(c.config, n.info, errAmbiguousCallXYZ % [
-        getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
+        getProcHeader(c.config, result.calleeSym),
+        getProcHeader(c.config, alt.calleeSym),
         args])
 
 proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 12ac19ca3..c858b6839 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -13,8 +13,8 @@ import
   strutils, intsets, options, lexer, ast, astalgo, trees, treetab,
   wordrecg,
   ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
-  magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef,
-  modulegraphs, configuration
+  magicsys, nversion, nimsets, parser, times, passes, vmdef,
+  modulegraphs, lineinfos
 
 type
   TOptionEntry* = object      # entries to put on a stack for pragma parsing
@@ -75,6 +75,7 @@ type
 
   PContext* = ref TContext
   TContext* = object of TPassContext # a context represents a module
+    enforceVoidContext*: PType
     module*: PSym              # the module sym belonging to the context
     currentScope*: PScope      # current scope
     importTable*: PScope       # scope for all imported symbols
@@ -148,7 +149,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
 
 proc filename*(c: PContext): string =
   # the module's filename
-  return toFilename(FileIndex c.module.position)
+  return toFilename(c.config, FileIndex c.module.position)
 
 proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
@@ -210,8 +211,9 @@ proc newOptionEntry*(conf: ConfigRef): POptionEntry =
   result.dynlib = nil
   result.notes = conf.notes
 
-proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext =
+proc newContext*(graph: ModuleGraph; module: PSym): PContext =
   new(result)
+  result.enforceVoidContext = PType(kind: tyStmt)
   result.ambiguousSymbols = initIntSet()
   result.optionStack = @[]
   result.libs = @[]
@@ -225,7 +227,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
   initStrTable(result.userPragmas)
   result.generics = @[]
   result.unknownIdents = initIntSet()
-  result.cache = cache
+  result.cache = graph.cache
   result.graph = graph
   initStrTable(result.signatures)
   result.typesWithOps = @[]
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6b32fa66c..f0cda504f 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -26,10 +26,10 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
   markUsed(c.config, n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
-  pushInfoContext(n.info)
+  pushInfoContext(c.config, n.info)
   result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
-  popInfoContext()
+  popInfoContext(c.config)
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
@@ -58,7 +58,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # do not produce another redundant error message:
     #raiseRecoverableError("")
     result = errorNode(c, n)
-  if result.typ == nil or result.typ == enforceVoidContext:
+  if result.typ == nil or result.typ == c.enforceVoidContext:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
@@ -137,7 +137,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
     else:
       discard
 
-proc isCastable(dst, src: PType): bool =
+proc isCastable(conf: ConfigRef; dst, src: PType): bool =
   ## Checks whether the source type can be cast to the destination type.
   ## Casting is very unrestrictive; casts are allowed as long as
   ## castDest.size >= src.size, and typeAllowed(dst, skParam)
@@ -152,8 +152,8 @@ proc isCastable(dst, src: PType): bool =
     return false
 
   var dstSize, srcSize: BiggestInt
-  dstSize = computeSize(dst)
-  srcSize = computeSize(src)
+  dstSize = computeSize(conf, dst)
+  srcSize = computeSize(conf, src)
   if dstSize < 0:
     result = false
   elif srcSize < 0:
@@ -167,7 +167,7 @@ proc isCastable(dst, src: PType): bool =
         (skipTypes(dst, abstractInst).kind in IntegralTypes) or
         (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
   if result and src.kind == tyNil:
-    result = dst.size <= platform.ptrSize
+    result = dst.size <= conf.target.ptrSize
 
 proc isSymChoice(n: PNode): bool {.inline.} =
   result = n.kind in nkSymChoices
@@ -251,7 +251,7 @@ proc semCast(c: PContext, n: PNode): PNode =
   let castedExpr = semExprWithType(c, n.sons[1])
   if tfHasMeta in targetType.flags:
     localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
-  if not isCastable(targetType, castedExpr.typ):
+  if not isCastable(c.config, targetType, castedExpr.typ):
     let tar = $targetType
     let alt = typeToString(targetType, preferDesc)
     let msg = if tar != alt: tar & "=" & alt else: tar
@@ -353,7 +353,7 @@ proc semOpAux(c: PContext, n: PNode) =
     var a = n.sons[i]
     if a.kind == nkExprEqExpr and sonsLen(a) == 2:
       let info = a.sons[0].info
-      a.sons[0] = newIdentNode(considerQuotedIdent(c.config, a.sons[0], a), info)
+      a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0], a), info)
       a.sons[1] = semExprWithType(c, a.sons[1], flags)
       a.typ = a.sons[1].typ
     else:
@@ -361,7 +361,7 @@ proc semOpAux(c: PContext, n: PNode) =
 
 proc overloadedCallOpr(c: PContext, n: PNode): PNode =
   # quick check if there is *any* () operator overloaded:
-  var par = getIdent("()")
+  var par = getIdent(c.cache, "()")
   if searchInScopes(c, par) == nil:
     result = nil
   else:
@@ -407,7 +407,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   of nkCharLit..nkUInt64Lit:
     if check and n.kind != nkUInt64Lit:
       let value = n.intVal
-      if value < firstOrd(newType) or value > lastOrd(newType):
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
         localError(c.config, n.info, "cannot convert " & $value &
                                          " to " & typeToString(newType))
   else: discard
@@ -617,12 +617,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
       call.add(a)
     #echo "NOW evaluating at compile time: ", call.renderTree
     if sfCompileTime in callee.flags:
-      result = evalStaticExpr(c.module, c.cache, c.graph, call, c.p.owner)
+      result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
       if result.isNil:
         localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
       else: result = fixupTypeAfterEval(c, result, n)
     else:
-      result = evalConstExpr(c.module, c.cache, c.graph, call)
+      result = evalConstExpr(c.module, c.graph, call)
       if result.isNil: result = n
       else: result = fixupTypeAfterEval(c, result, n)
     #if result != n:
@@ -631,10 +631,10 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
 proc semStaticExpr(c: PContext, n: PNode): PNode =
   let a = semExpr(c, n.sons[0])
   if a.findUnresolvedStatic != nil: return a
-  result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner)
+  result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
   if result.isNil:
     localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
-    result = emptyNode
+    result = c.graph.emptyNode
   else:
     result = fixupTypeAfterEval(c, result, a)
 
@@ -742,10 +742,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # This is a proc variable, apply normal overload resolution
     let m = resolveIndirectCall(c, n, nOrig, t)
     if m.state != csMatch:
-      if errorOutputs == {}:
+      if c.config.m.errorOutputs == {}:
         # speed up error generation:
         globalError(c.config, n.info, "type mismatch")
-        return emptyNode
+        return c.graph.emptyNode
       else:
         var hasErrorType = false
         var msg = "type mismatch: got <"
@@ -802,7 +802,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"echo")
+  var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo"))
   if e != nil:
     add(result, newSymNode(e))
   else:
@@ -866,7 +866,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         else:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           s = newNodeIT(nkCurly, n.info, setType)
           for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
@@ -881,7 +881,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         if result != nil:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
           addSon(inExpr, newSymNode(c.graph.opContains, n.info))
           addSon(inExpr, s)
@@ -938,7 +938,7 @@ proc readTypeParameter(c: PContext, typ: PType,
           if rawTyp.n != nil:
             return rawTyp.n
           else:
-            return emptyNode
+            return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
           return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
@@ -1082,7 +1082,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   when defined(nimsuggest):
     if c.config.cmd == cmdIdeTools:
       suggestExpr(c, n)
-      if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
+      if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n)
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
   if s != nil:
@@ -1097,7 +1097,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
   n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
   #restoreOldStyleType(n.sons[0])
-  var i = considerQuotedIdent(c.config, n.sons[1], n)
+  var i = considerQuotedIdent(c, n.sons[1], n)
   var ty = n.sons[0].typ
   var f: PSym = nil
   result = nil
@@ -1106,7 +1106,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     case t.kind
     of tyTypeParamsHolders:
       result = readTypeParameter(c, t, i, n.info)
-      if result == emptyNode:
+      if result == c.graph.emptyNode:
         result = n
         n.typ = makeTypeFromExpr(c, n.copyTree)
       return
@@ -1217,7 +1217,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
     addSon(result, n.sons[1])
     addSon(result, copyTree(n[0]))
   else:
-    var i = considerQuotedIdent(c.config, n.sons[1], n)
+    var i = considerQuotedIdent(c, n.sons[1], n)
     result = newNodeI(nkDotCall, n.info)
     result.flags.incl nfDotField
     addSon(result, newIdentNode(i, n[1].info))
@@ -1328,11 +1328,11 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = semSubscript(c, n, flags)
   if result == nil:
     # overloaded [] operator:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")))
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
-  var id = considerQuotedIdent(c.config, a[1], a)
-  var setterId = newIdentNode(getIdent(id.s & '='), n.info)
+  var id = considerQuotedIdent(c, a[1], a)
+  var setterId = newIdentNode(getIdent(c.cache, id.s & '='), n.info)
   # a[0] is already checked for semantics, that does ``builtinFieldAccess``
   # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
   # nodes?
@@ -1409,7 +1409,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
     # --> `[]=`(a, i, x)
     a = semSubscript(c, a, {efLValue})
     if a == nil:
-      result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
+      result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "[]="))
       add(result, n[1])
       if mode == noOverloadedSubscript:
         bracketNotFoundError(c, result)
@@ -1419,7 +1419,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         return result
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
-    result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
+    result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "{}="))
     add(result, n[1])
     return semExprNoType(c, result)
   of nkPar, nkTupleConstr:
@@ -1427,7 +1427,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner))
+      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner))
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
@@ -1450,7 +1450,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       rhs = semExprWithType(c, n.sons[1],
         if lhsIsResult: {efAllowDestructor} else: {})
     if lhsIsResult:
-      n.typ = enforceVoidContext
+      n.typ = c.enforceVoidContext
       if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
         var rhsTyp = rhs.typ
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
@@ -1489,7 +1489,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
         n.sons[0] = semAsgn(c, a)
         # optimize away ``result = result``:
         if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
-          n.sons[0] = ast.emptyNode
+          n.sons[0] = c.graph.emptyNode
       else:
         localError(c.config, n.info, errNoReturnTypeDeclared)
   else:
@@ -1593,13 +1593,13 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
     checkSonsLen(n, 2, c.config)
     var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
     if m != nil and m.kind == skModule:
-      let ident = considerQuotedIdent(c.config, n[1], n)
+      let ident = considerQuotedIdent(c, n[1], n)
       if m == c.module:
         result = strTableGet(c.topLevelScope.symbols, ident)
       else:
         result = strTableGet(m.tab, ident)
   of nkAccQuoted:
-    result = lookUpForDefined(c, considerQuotedIdent(c.config, n), onlyCurrentScope)
+    result = lookUpForDefined(c, considerQuotedIdent(c, n), onlyCurrentScope)
   of nkSym:
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
@@ -1612,7 +1612,7 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
   checkSonsLen(n, 2, c.config)
   # we replace this node by a 'true' or 'false' node:
   result = newIntNode(nkIntLit, 0)
-  if not onlyCurrentScope and considerQuotedIdent(c.config, n[0], n).s == "defined":
+  if not onlyCurrentScope and considerQuotedIdent(c, n[0], n).s == "defined":
     if n.sons[1].kind != nkIdent:
       localError(c.config, n.info, "obsolete usage of 'defined', use 'declared' instead")
     elif isDefined(c.config, n.sons[1].ident.s):
@@ -1711,7 +1711,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
                        ids: var seq[PNode]) =
   template returnQuote(q) =
     quotes.add q
-    n = newIdentNode(getIdent($quotes.len), n.info)
+    n = newIdentNode(getIdent(c.cache, $quotes.len), n.info)
     ids.add n
     return
 
@@ -1722,7 +1722,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
       if examinedOp == op:
         returnQuote n[1]
       elif examinedOp.startsWith(op):
-        n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info)
+        n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info)
   elif n.kind == nkAccQuoted and op == "``":
     returnQuote n[0]
 
@@ -1748,14 +1748,17 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
   processQuotations(c, quotedBlock, op, quotes, ids)
 
   var dummyTemplate = newProcNode(
-    nkTemplateDef, quotedBlock.info, quotedBlock,
-    name = newAnonSym(c, skTemplate, n.info).newSymNode)
+    nkTemplateDef, quotedBlock.info, body = quotedBlock,
+    params = c.graph.emptyNode,
+    name = newAnonSym(c, skTemplate, n.info).newSymNode,
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode)
 
   if ids.len > 0:
     dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
     dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type
     ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
-    ids.add emptyNode # no default value
+    ids.add c.graph.emptyNode # no default value
     dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
 
   var tmpl = semTemplateDef(c, dummyTemplate)
@@ -1780,9 +1783,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   openScope(c)
   let oldOwnerLen = len(c.graph.owners)
   let oldGenerics = c.generics
-  let oldErrorOutputs = errorOutputs
-  if efExplain notin flags: errorOutputs = {}
-  let oldContextLen = msgs.getInfoContextLen()
+  let oldErrorOutputs = c.config.m.errorOutputs
+  if efExplain notin flags: c.config.m.errorOutputs = {}
+  let oldContextLen = msgs.getInfoContextLen(c.config)
 
   let oldInGenericContext = c.inGenericContext
   let oldInUnrolledContext = c.inUnrolledContext
@@ -1804,10 +1807,10 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.inGenericInst = oldInGenericInst
   c.inStaticContext = oldInStaticContext
   c.p = oldProcCon
-  msgs.setInfoContextLen(oldContextLen)
+  msgs.setInfoContextLen(c.config, oldContextLen)
   setLen(c.graph.owners, oldOwnerLen)
   c.currentScope = oldScope
-  errorOutputs = oldErrorOutputs
+  c.config.m.errorOutputs = oldErrorOutputs
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
 
@@ -1913,7 +1916,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         result.typ = typ
       result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
     else:
-      result.add emptyNode
+      result.add c.graph.emptyNode
   of mProcCall:
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
@@ -1937,17 +1940,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mRunnableExamples:
     if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
       if sfMainModule in c.module.flags:
-        let inp = toFullPath(c.module.info)
+        let inp = toFullPath(c.config, c.module.info)
         if c.runnableExamples == nil:
           c.runnableExamples = newTree(nkStmtList,
             newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
         let imports = newTree(nkStmtList)
         extractImports(n.lastSon, imports)
         for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon)
+        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
       result = setMs(n, s)
     else:
-      result = emptyNode
+      result = c.graph.emptyNode
   else:
     result = semDirectOp(c, n, flags)
 
@@ -2040,7 +2043,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
     if not isOrdinalType(typ):
       localError(c.config, n.info, errOrdinalTypeExpected)
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    elif lengthOrd(typ) > MaxSetElements:
+    elif lengthOrd(c.config, typ) > MaxSetElements:
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
     addSonSkipIntLit(result.typ, typ)
     for i in countup(0, sonsLen(n) - 1):
@@ -2306,7 +2309,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     #when defined(nimsuggest):
-    #  if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n)
+    #  if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)
     let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
     var s = qualifiedLookUp(c, n.sons[0], mode)
     if s != nil:
@@ -2363,12 +2366,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     checkMinSonsLen(n, 1, c.config)
     result = semArrayAccess(c, n, flags)
   of nkCurlyExpr:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags)
   of nkPragmaExpr:
     var
       expr = n[0]
       pragma = n[1]
-      pragmaName = considerQuotedIdent(c.config, pragma[0])
+      pragmaName = considerQuotedIdent(c, pragma[0])
       flags = flags
 
     case whichKeyword(pragmaName)
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 16d79c559..869f5ae74 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -23,10 +23,10 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
   of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
   of nkIdent, nkSym:
     result = n
-    let ident = considerQuotedIdent(c.c.config, n)
+    let ident = considerQuotedIdent(c.c, n)
     var L = sonsLen(forLoop)
     if c.replaceByFieldName:
-      if ident.id == considerQuotedIdent(c.c.config, forLoop[0]).id:
+      if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
         let fieldName = if c.tupleType.isNil: c.field.name.s
                         elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
                         else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
@@ -34,7 +34,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
         return
     # other fields:
     for i in ord(c.replaceByFieldName)..L-3:
-      if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id:
+      if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
         var call = forLoop.sons[L-2]
         var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
         if c.field.isNil:
@@ -107,10 +107,10 @@ 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"true")
+  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
-    trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info)
+    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
   result.sons[0] = newSymNode(trueSymbol, n.info)
@@ -162,7 +162,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # we avoid it now if we can:
   if containsNode(stmts, {nkBreakStmt}):
     var b = newNodeI(nkBreakStmt, n.info)
-    b.add(ast.emptyNode)
+    b.add(newNodeI(nkEmpty, n.info))
     stmts.add(b)
   else:
     result = stmts
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index daf9ce983..10a223ea2 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -13,7 +13,7 @@
 import
   strutils, options, ast, astalgo, trees, treetab, nimsets, times,
   nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
-  commands, magicsys, modulegraphs, strtabs
+  commands, magicsys, modulegraphs, strtabs, lineinfos
 
 proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   case skipTypes(n.typ, abstractVarRange).kind
@@ -53,24 +53,24 @@ proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
   # expression
 proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
 
-proc checkInRange(n: PNode, res: BiggestInt): bool =
-  if res in firstOrd(n.typ)..lastOrd(n.typ):
+proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool =
+  if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ):
     result = true
 
 proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   let res = a +% b
   if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, g)
 
 proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   let res = a -% b
   if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, g)
 
 proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if a != firstOrd(n.typ):
+  if a != firstOrd(g.config, n.typ):
     result = newIntNodeT(a, n, g)
 
 proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
@@ -82,7 +82,7 @@ proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
     result = newIntNodeT(a %% b, n, g)
 
 proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
+  if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64):
     result = newIntNodeT(a div b, n, g)
 
 proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
@@ -96,7 +96,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
 
   # Fast path for normal case: small multiplicands, and no info
   # is lost in either method.
-  if resAsFloat == floatProd and checkInRange(n, res):
+  if resAsFloat == floatProd and checkInRange(g.config, n, res):
     return newIntNodeT(res, n, g)
 
   # Somebody somewhere lost info. Close enough, or way off? Note
@@ -107,7 +107,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   # abs(diff)/abs(prod) <= 1/32 iff
   #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
   if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     return newIntNodeT(res, n, g)
 
 proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
@@ -210,9 +210,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g)
   of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g)
   of mNot: result = newIntNodeT(1 - getInt(a), n, g)
-  of mCard: result = newIntNodeT(nimsets.cardSet(a), n, g)
+  of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g)
   of mBitnotI: result = newIntNodeT(not getInt(a), n, g)
-  of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n, g)
+  of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
   of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
     if a.kind == nkNilLit:
       result = newIntNodeT(0, n, g)
@@ -229,7 +229,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mAbsI: result = foldAbs(getInt(a), n, g)
   of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
     # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
-    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n, g)
+    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(g.config, a.typ) * 8) - 1), n, g)
   of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g)
   of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g)
   of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g)
@@ -304,21 +304,21 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g)
   of mModU: result = foldModU(getInt(a), getInt(b), n, g)
   of mDivU: result = foldDivU(getInt(a), getInt(b), n, g)
-  of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n, g)
-  of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n, g)
+  of mLeSet: result = newIntNodeT(ord(containsSets(g.config, a, b)), n, g)
+  of mEqSet: result = newIntNodeT(ord(equalSets(g.config, a, b)), n, g)
   of mLtSet:
-    result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g)
+    result = newIntNodeT(ord(containsSets(g.config, a, b) and not equalSets(g.config, a, b)), n, g)
   of mMulSet:
-    result = nimsets.intersectSets(a, b)
+    result = nimsets.intersectSets(g.config, a, b)
     result.info = n.info
   of mPlusSet:
-    result = nimsets.unionSets(a, b)
+    result = nimsets.unionSets(g.config, a, b)
     result.info = n.info
   of mMinusSet:
-    result = nimsets.diffSets(a, b)
+    result = nimsets.diffSets(g.config, a, b)
     result.info = n.info
   of mSymDiffSet:
-    result = nimsets.symdiffSets(a, b)
+    result = nimsets.symdiffSets(g.config, a, b)
     result.info = n.info
   of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
   of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g)
@@ -415,9 +415,9 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode =
 proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
   var err = false
   if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
-    err = value <% firstOrd(n.typ) or value >% lastOrd(n.typ, fixedUnsigned=true)
+    err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
   else:
-    err = value < firstOrd(n.typ) or value > lastOrd(n.typ)
+    err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
   if err:
     localError(g.config, n.info, "cannot convert " & $value &
                                      " to " & typeToString(n.typ))
@@ -472,7 +472,7 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
     else:
       localError(g.config, n.info, "index out of bounds: " & $n)
   of nkBracket:
-    idx = idx - x.typ.firstOrd
+    idx = idx - firstOrd(g.config, x.typ)
     if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
     else: localError(g.config, n.info, "index out of bounds: " & $n)
   of nkStrLit..nkTripleStrLit:
@@ -547,11 +547,11 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
                                                    "yyyy-MM-dd"), n, g)
       of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
                                                    "HH:mm:ss"), n, g)
-      of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n, g)
-      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n, g)
-      of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n, g)
-      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n, g)
-      of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n, g)
+      of mCpuEndian: result = newIntNodeT(ord(CPU[g.config.target.targetCPU].endian), n, g)
+      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g)
+      of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g)
+      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g)
+      of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g)
       of mAppType: result = getAppType(n, g)
       of mNaN: result = newFloatNodeT(NaN, n, g)
       of mInf: result = newFloatNodeT(Inf, n, g)
@@ -599,22 +599,22 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
         return
       of mSizeOf:
         var a = n.sons[1]
-        if computeSize(a.typ) < 0:
+        if computeSize(g.config, a.typ) < 0:
           localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
           result = nil
         elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
              IntegralTypes+NilableTypes+{tySet}:
           #{tyArray,tyObject,tyTuple}:
-          result = newIntNodeT(getSize(a.typ), n, g)
+          result = newIntNodeT(getSize(g.config, a.typ), n, g)
         else:
           result = nil
           # XXX: size computation for complex types is still wrong
       of mLow:
-        result = newIntNodeT(firstOrd(n.sons[1].typ), n, g)
+        result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g)
       of mHigh:
         if skipTypes(n.sons[1].typ, abstractVar).kind notin
             {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
-          result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g)
+          result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
         else:
           var a = getArrayConstr(m, n.sons[1], g)
           if a.kind == nkBracket:
@@ -630,7 +630,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       of mLengthArray:
         # It doesn't matter if the argument is const or not for mLengthArray.
         # This fixes bug #544.
-        result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g)
+        result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g)
       of mAstToStr:
         result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
       of mConStrStr:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 8f06e748e..727fa4f3f 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -55,7 +55,7 @@ template macroToExpandSym(s): untyped =
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
                           ctx: var GenericCtx; fromDotExpr=false): PNode =
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
   incl(s.flags, sfUsed)
   case s.kind
   of skUnknown:
@@ -103,7 +103,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var GenericCtx): PNode =
   result = n
-  let ident = considerQuotedIdent(c.config, n)
+  let ident = considerQuotedIdent(c, n)
   var s = searchInScopes(c, ident).skipAlias(n, c.config)
   if s == nil:
     s = strTableGet(c.pureEnumFields, ident)
@@ -129,7 +129,7 @@ proc newDot(n, b: PNode): PNode =
 proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
                  ctx: var GenericCtx; isMacro: var bool): PNode =
   assert n.kind == nkDotExpr
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
@@ -140,7 +140,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
     result = n
     let n = n[1]
-    let ident = considerQuotedIdent(c.config, n)
+    let ident = considerQuotedIdent(c, n)
     var s = searchInScopes(c, ident).skipAlias(n, c.config)
     if s != nil and s.kind in routineKinds:
       isMacro = s.kind in {skTemplate, skMacro}
@@ -170,7 +170,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if withinTypeDesc in flags: inc c.inTypeContext
 
   #if conf.cmd == cmdIdeTools: suggestStmt(c, n)
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   case n.kind
   of nkIdent, nkAccQuoted:
@@ -207,7 +207,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if  s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
-        considerQuotedIdent(c.config, fn).id notin ctx.toMixin:
+        considerQuotedIdent(c, fn).id notin ctx.toMixin:
       errorUndeclaredIdentifier(c, n.info, fn.renderTree)
 
     var first = int ord(withinConcept in flags)
@@ -275,12 +275,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
   of nkCurlyExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("{}"), n.info)
+    result.add newIdentNode(getIdent(c.cache, "{}"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     result = semGenericStmt(c, result, flags, ctx)
   of nkBracketExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("[]"), n.info)
+    result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     withBracketExpr ctx, n.sons[0]:
       result = semGenericStmt(c, result, flags, ctx)
@@ -293,13 +293,13 @@ proc semGenericStmt(c: PContext, n: PNode,
     case k
     of nkCurlyExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("{}="), n.info)
+      result.add newIdentNode(getIdent(c.cache, "{}="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       result = semGenericStmt(c, result, flags, ctx)
     of nkBracketExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("[]="), n.info)
+      result.add newIdentNode(getIdent(c.cache, "[]="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       withBracketExpr ctx, a.sons[0]:
@@ -448,7 +448,7 @@ proc semGenericStmt(c: PContext, n: PNode,
                                               flags, ctx)
     if n.sons[paramsPos].kind != nkEmpty:
       if n.sons[paramsPos].sons[0].kind != nkEmpty:
-        addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
+        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
     n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
     var body: PNode
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index a5f0ca7d8..95b631850 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -159,13 +159,13 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       var oldPrc = c.generics[i].inst.sym
       pushProcCon(c, oldPrc)
       pushOwner(c, oldPrc)
-      pushInfoContext(oldPrc.info)
+      pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
       n.sons[bodyPos] = copyTree(s.getBody)
       instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
-      popInfoContext()
+      popInfoContext(c.config)
       popOwner(c)
       popProcCon(c)
 
@@ -236,7 +236,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   # at this point semtypinst have to become part of sem, because it
   # will need to use openScope, addDecl, etc.
   #addDecl(c, prc)
-  pushInfoContext(info)
+  pushInfoContext(c.config, info)
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(c, addr(typeMap), info, nil)
   var result = instCopyType(cl, prc.typ)
@@ -278,7 +278,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   skipIntLiteralParams(result)
 
   prc.typ = result
-  popInfoContext()
+  popInfoContext(c.config)
 
 proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
                       info: TLineInfo): PSym =
@@ -309,7 +309,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   let gp = n.sons[genericParamsPos]
   internalAssert c.config, gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
-  pushInfoContext(info)
+  pushInfoContext(c.config, info)
   var entry = TInstantiation.new
   entry.sym = result
   # we need to compare both the generic types and the concrete types:
@@ -328,7 +328,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     inc i
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
-  n.sons[genericParamsPos] = ast.emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # we MUST not add potentially wrong instantiations to the caching mechanism.
@@ -353,7 +353,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   else:
     result = oldPrc
   popProcCon(c)
-  popInfoContext()
+  popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
   popOwner(c)
   c.currentScope = oldScope
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 8515e971d..1975fb77b 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -41,7 +41,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   result = semSubscript(c, result, flags)
   if result.isNil:
     let x = copyTree(n)
-    x.sons[0] = newIdentNode(getIdent"[]", n.info)
+    x.sons[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
     bracketNotFoundError(c, x)
     #localError(c.config, n.info, "could not resolve: " & $n)
     result = n
@@ -76,9 +76,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
   result = newNodeIT(nkTupleConstr, n.info, n.typ)
   let idx = expectIntLit(c, n.sons[1])
   let useFullPaths = expectIntLit(c, n.sons[2])
-  let info = getInfoContext(idx)
+  let info = getInfoContext(c.config, idx)
   var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
-  filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename
+  filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info)
   var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
   line.intVal = toLinenumber(info)
   var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
@@ -154,7 +154,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
   else:
     localError(c.config, traitCall.info, "unknown trait")
-    result = emptyNode
+    result = newNodeI(nkEmpty, traitCall.info)
 
 proc semTypeTraits(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 2, c.config)
@@ -174,7 +174,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
   if isOrdinalType(parType):
     discard
   elif parType.kind == tySet:
-    result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
+    result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info)
   else:
     localError(c.config, n.info, errOrdinalTypeExpected)
     result.typ = errorType(c)
@@ -194,7 +194,7 @@ proc semBindSym(c: PContext, n: PNode): PNode =
     localError(c.config, n.sons[2].info, errConstExprExpected)
     return errorNode(c, n)
 
-  let id = newIdentNode(getIdent(sl.strVal), n.info)
+  let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info)
   let s = qualifiedLookUp(c, id, {checkUndeclared})
   if s != nil:
     # we need to mark all symbols:
@@ -209,10 +209,6 @@ proc semBindSym(c: PContext, n: PNode): PNode =
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 
-proc isStrangeArray(t: PType): bool =
-  let t = t.skipTypes(abstractInst)
-  result = t.kind == tyArray and t.firstOrd != 0
-
 proc semOf(c: PContext, n: PNode): PNode =
   if sonsLen(n) == 3:
     n.sons[1] = semExprWithType(c, n.sons[1])
@@ -283,7 +279,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mRoof:
     localError(c.config, n.info, "builtin roof operator is not supported anymore")
   of mPlugin:
-    let plugin = getPlugin(n[0].sym)
+    let plugin = getPlugin(c.cache, n[0].sym)
     if plugin.isNil:
       localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s)
       result = n
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 1e0802953..8b639806d 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -54,7 +54,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
       invalidObjConstr(c, assignment)
       continue
 
-    if fieldId == considerQuotedIdent(c.config, assignment[0]).id:
+    if fieldId == considerQuotedIdent(c, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
@@ -295,11 +295,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
       if field.kind != nkExprColonExpr:
         invalidObjConstr(c, field)
         continue
-      let id = considerQuotedIdent(c.config, field[0])
+      let id = considerQuotedIdent(c, field[0])
       # This node was not processed. There are two possible reasons:
       # 1) It was shadowed by a field with the same name on the left
       for j in 1 ..< i:
-        let prevId = considerQuotedIdent(c.config, result[j][0])
+        let prevId = considerQuotedIdent(c, result[j][0])
         if prevId.id == id.id:
           localError(c.config, field.info, errFieldInitTwice % id.s)
           return
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index ea5aab628..0d780bdee 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -136,8 +136,8 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) =
     localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
 
 proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
-  checkLe(c, arr.lowBound, idx)
-  checkLe(c, idx, arr.highBound(c.guards.o))
+  checkLe(c, lowBound(c.graph.config, arr), idx)
+  checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o))
 
 proc addLowerBoundAsFacts(c: var AnalysisCtx) =
   for v in c.locals:
@@ -438,7 +438,7 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
         let t = b[1][0].typ.sons[0]
         if spawnResult(t, true) == srByVar:
           result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
-          it.sons[it.len-1] = emptyNode
+          it.sons[it.len-1] = newNodeI(nkEmpty, it.info)
         else:
           it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
@@ -483,7 +483,7 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
-  var temp = newSym(skTemp, getIdent"barrier", owner, n.info)
+  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info)
   temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index b66d7d9f2..4d3ee0408 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, writetracking, configuration,
+  wordrecg, strutils, options, guards, writetracking, lineinfos,
   modulegraphs
 
 when defined(useDfa):
@@ -185,7 +185,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       if reason.kind == nkSym:
         a.owner.gcUnsafetyReason = reason.sym
       else:
-        a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
+        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name,
                                           a.owner, reason.info, {})
 
 when true:
@@ -410,7 +410,7 @@ proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
         result.add(it.sons[1])
       return
 
-proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
+proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
   let spec = effectSpec(x, effectType)
   if isNil(spec):
     let s = n.sons[namePos].sym
@@ -424,14 +424,14 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
     for i in 0 ..< real.len:
       var t = typeToString(real[i].typ)
       if t.startsWith("ref "): t = substr(t, 4)
-      effects.sons[i] = newIdentNode(getIdent(t), n.info)
+      effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
       # set the type so that the following analysis doesn't screw up:
       effects.sons[i].typ = real[i].typ
 
     result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(specialWords[effectType]), n.info), effects])
+      newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
 
-proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
+proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
   let s = n.sons[namePos].sym
   let params = s.typ.n
 
@@ -442,21 +442,21 @@ proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
 
   if effects.len > 0:
     result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(pragmaName), n.info), effects])
+      newIdentNode(getIdent(cache, pragmaName), n.info), effects])
 
-proc documentNewEffect(n: PNode): PNode =
+proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
   let s = n.sons[namePos].sym
   if tfReturnsNew in s.typ.flags:
-    result = newIdentNode(getIdent("new"), n.info)
+    result = newIdentNode(getIdent(cache, "new"), n.info)
 
-proc documentRaises*(n: PNode) =
+proc documentRaises*(cache: IdentCache; n: PNode) =
   if n.sons[namePos].kind != nkSym: return
   let pragmas = n.sons[pragmasPos]
-  let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects)
-  let p2 = documentEffect(n, pragmas, wTags, tagEffects)
-  let p3 = documentWriteEffect(n, sfWrittenTo, "writes")
-  let p4 = documentNewEffect(n)
-  let p5 = documentWriteEffect(n, sfEscapes, "escapes")
+  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
+  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
+  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
+  let p4 = documentNewEffect(cache, n)
+  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
 
   if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
     if pragmas.kind == nkEmpty:
@@ -859,9 +859,9 @@ proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
-      pushInfoContext(spec.info)
+      pushInfoContext(g.config, spec.info)
       localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
-      popInfoContext()
+      popInfoContext(g.config)
   # hint about unnecessarily listed exception types:
   if hints:
     for s in 0 ..< spec.len:
@@ -915,8 +915,8 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
-  effects.sons[usesEffects] = ast.emptyNode
-  effects.sons[writeEffects] = ast.emptyNode
+  effects.sons[usesEffects] = g.emptyNode
+  effects.sons[writeEffects] = g.emptyNode
 
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 3687e50e9..0f8e77fc9 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -36,8 +36,6 @@ const
   errRecursiveDependencyX = "recursive dependency: '$1'"
   errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1"
 
-var enforceVoidContext = PType(kind: tyStmt) # XXX global variable here
-
 proc semDiscard(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
@@ -87,15 +85,15 @@ proc semWhile(c: PContext, n: PNode): PNode =
   n.sons[1] = semStmt(c, n.sons[1])
   dec(c.p.nestedLoopCounter)
   closeScope(c)
-  if n.sons[1].typ == enforceVoidContext:
-    result.typ = enforceVoidContext
+  if n.sons[1].typ == c.enforceVoidContext:
+    result.typ = c.enforceVoidContext
 
-proc toCover(t: PType): BiggestInt =
-  var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
+proc toCover(c: PContext, t: PType): BiggestInt =
+  let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
   if t2.kind == tyEnum and enumHasHoles(t2):
     result = sonsLen(t2.n)
   else:
-    result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
+    result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
 
 proc semProc(c: PContext, n: PNode): PNode
 
@@ -146,7 +144,7 @@ proc discardCheck(c: PContext, result: PNode) =
           result.typ.typeToString & "' and has to be discarded"
       if result.info.line != n.info.line or
           result.info.fileIndex != n.info.fileIndex:
-        s.add "; start of expression here: " & $result.info
+        s.add "; start of expression here: " & c.config$result.info
       if result.typ.kind == tyProc:
         s.add "; for a function call use ()"
       localError(c.config, n.info, s)
@@ -173,7 +171,7 @@ proc semIf(c: PContext, n: PNode): PNode =
     for it in n: discardCheck(c, it.lastSon)
     result.kind = nkIfStmt
     # propagate any enforced VoidContext:
-    if typ == enforceVoidContext: result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
   else:
     for it in n:
       let j = it.len-1
@@ -203,7 +201,7 @@ proc semCase(c: PContext, n: PNode): PNode =
   for i in countup(1, sonsLen(n) - 1):
     var x = n.sons[i]
     when defined(nimsuggest):
-      if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
+      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
         suggestEnum(c, x, caseTyp)
     case x.kind
     of nkOfBranch:
@@ -230,7 +228,7 @@ proc semCase(c: PContext, n: PNode): PNode =
     else:
       illFormedAst(x, c.config)
   if chckCovered:
-    if covered == toCover(n.sons[0].typ):
+    if covered == toCover(c, n.sons[0].typ):
       hasElse = true
     else:
       localError(c.config, n.info, "not all cases are covered")
@@ -238,8 +236,8 @@ proc semCase(c: PContext, n: PNode): PNode =
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
     for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
     # propagate any enforced VoidContext:
-    if typ == enforceVoidContext:
-      result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
   else:
     for i in 1..n.len-1:
       var it = n.sons[i]
@@ -318,8 +316,8 @@ proc semTry(c: PContext, n: PNode): PNode =
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
     discardCheck(c, n.sons[0])
     for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
-    if typ == enforceVoidContext:
-      result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
   else:
     if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon)
     n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
@@ -394,7 +392,7 @@ proc isDiscardUnderscore(v: PSym): bool =
     result = true
 
 proc semUsing(c: PContext; n: PNode): PNode =
-  result = ast.emptyNode
+  result = c.graph.emptyNode
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using")
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
@@ -442,10 +440,10 @@ proc makeDeref(n: PNode): PNode =
 proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
   if n.len == 2:
     let x = semExprWithType(c, n[0])
-    let y = considerQuotedIdent(c.config, n[1])
+    let y = considerQuotedIdent(c, n[1])
     let obj = x.typ.skipTypes(abstractPtrs)
     if obj.kind == tyObject and tfPartial in obj.flags:
-      let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info)
+      let field = newSym(skField, getIdent(c.cache, y.s), obj.sym, n[1].info)
       field.typ = skipIntLit(typ)
       field.position = sonsLen(obj.n)
       addSon(obj.n, newSymNode(field))
@@ -481,7 +479,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       typ = semTypeNode(c, a.sons[length-2], nil)
     else:
       typ = nil
-    var def: PNode = ast.emptyNode
+    var def: PNode = c.graph.emptyNode
     if a.sons[length-1].kind != nkEmpty:
       def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
       if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
@@ -571,8 +569,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         b.sons[j] = newSymNode(v)
       checkNilable(c, v)
       if sfCompileTime in v.flags: hasCompileTime = true
+      if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
+        message(c.config, v.info, hintGlobalVar)
   if hasCompileTime:
-    vm.setupCompileTimeVar(c.module, c.cache, c.graph, result)
+    vm.setupCompileTimeVar(c.module, c.graph, result)
+    # handled by the VM codegen:
+    #c.graph.recordStmt(c.graph, c.module, result)
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
@@ -664,7 +666,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
 
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = newNodeI(nkCall, arg.info)
-  result.add(newIdentNode(it.getIdent, arg.info))
+  result.add(newIdentNode(getIdent(c.cache, it), arg.info))
   if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
     result.add newDeref(arg)
   else:
@@ -698,7 +700,8 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
             match = symx
           else:
             localError(c.config, n.info, errAmbiguousCallXYZ % [
-              getProcHeader(match), getProcHeader(symx), $iterExpr])
+              getProcHeader(c.config, match),
+              getProcHeader(c.config, symx), $iterExpr])
       symx = nextOverloadIter(o, c, headSymbol)
 
     if match == nil: return
@@ -746,8 +749,8 @@ proc semFor(c: PContext, n: PNode): PNode =
   else:
     result = semForVars(c, n)
   # propagate any enforced VoidContext:
-  if n.sons[length-1].typ == enforceVoidContext:
-    result.typ = enforceVoidContext
+  if n.sons[length-1].typ == c.enforceVoidContext:
+    result.typ = c.enforceVoidContext
   closeScope(c)
 
 proc semRaise(c: PContext, n: PNode): PNode =
@@ -795,8 +798,8 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     let name = a.sons[0]
     var s: PSym
     if name.kind == nkDotExpr and a[2].kind == nkObjectTy:
-      let pkgName = considerQuotedIdent(c.config, name[0])
-      let typName = considerQuotedIdent(c.config, name[1])
+      let pkgName = considerQuotedIdent(c, name[0])
+      let typName = considerQuotedIdent(c, name[1])
       let pkg = c.graph.packageSyms.strTableGet(pkgName)
       if pkg.isNil or pkg.kind != skPackage:
         localError(c.config, name.info, "unknown package name: " & pkgName.s)
@@ -834,7 +837,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
               typsym.info = s.info
             else:
               localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " &
-                      "previous type completion was here: " & $typsym.info)
+                      "previous type completion was here: " & c.config$typsym.info)
             s = typsym
       # add it here, so that recursive types are possible:
       if sfGenSym notin s.flags: addInterfaceDecl(c, s)
@@ -988,7 +991,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       internalAssert c.config, st.kind in {tyPtr, tyRef}
       internalAssert c.config, st.lastSon.sym == nil
       incl st.flags, tfRefsAnonObj
-      let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"),
+      let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
                               getCurrOwner(c), s.info)
       obj.typ = st.lastSon
       st.lastSon.sym = obj
@@ -1056,9 +1059,9 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
         var f = checkModuleName(c.config, n.sons[i])
         if f != InvalidFileIDX:
           if containsOrIncl(c.includedFiles, f.int):
-            localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
+            localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
           else:
-            let code = gIncludeFile(c.graph, c.module, f, c.cache)
+            let code = c.graph.includeFileCallback(c.graph, c.module, f)
             gatherStmts c, code, result
             excl(c.includedFiles, f.int)
     of nkStmtList:
@@ -1130,7 +1133,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
 
 proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
   if t != nil:
-    var s = newSym(skResult, getIdent"result", getCurrOwner(c), info)
+    var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), info)
     s.typ = t
     incl(s.flags, sfUsed)
     addParamOrResult(c, s, owner)
@@ -1149,7 +1152,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
     result = n.sym
     if result.kind notin {skMacro, skTemplate}: result = nil
   else:
-    result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate})
+    result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate})
 
 proc semProcAnnotation(c: PContext, prc: PNode;
                        validPragmas: TSpecialWords): PNode =
@@ -1161,7 +1164,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     let m = lookupMacro(c, key)
     if m == nil:
       if key.kind == nkIdent and key.ident.id == ord(wDelegator):
-        if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()":
+        if considerQuotedIdent(c, prc.sons[namePos]).s == "()":
           prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info)
           prc.sons[pragmasPos] = copyExcept(n, i)
         else:
@@ -1176,7 +1179,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     x.add(newSymNode(m))
     prc.sons[pragmasPos] = copyExcept(n, i)
     if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
-      prc.sons[pragmasPos] = emptyNode
+      prc.sons[pragmasPos] = c.graph.emptyNode
 
     if it.kind in nkPragmaCallKinds and it.len > 1:
       # pass pragma arguments to the macro too:
@@ -1199,7 +1202,7 @@ proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
   # issue https://github.com/nim-lang/Nim/issues/1713
   result = semGenericParamList(c, orig)
   if n.sons[miscPos].kind == nkEmpty:
-    n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig)
+    n.sons[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
   else:
     n.sons[miscPos].sons[1] = orig
   n.sons[genericParamsPos] = result
@@ -1270,7 +1273,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   result = n
   s.ast = result
   n.sons[namePos].sym = s
-  n.sons[genericParamsPos] = emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   # for LL we need to avoid wrong aliasing
   let params = copyTree n.typ.n
   n.sons[paramsPos] = params
@@ -1397,14 +1400,14 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       localError(c.config, n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
-proc cursorInProcAux(n: PNode): bool =
-  if inCheckpoint(n.info) != cpNone: return true
+proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
+  if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true
   for i in 0..<n.safeLen:
-    if cursorInProcAux(n[i]): return true
+    if cursorInProcAux(conf, n[i]): return true
 
-proc cursorInProc(n: PNode): bool =
-  if n.info.fileIndex == gTrackPos.fileIndex:
-    result = cursorInProcAux(n)
+proc cursorInProc(conf: ConfigRef; n: PNode): bool =
+  if n.info.fileIndex == conf.m.trackPos.fileIndex:
+    result = cursorInProcAux(conf, n)
 
 type
   TProcCompilationSteps = enum
@@ -1543,7 +1546,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       # linking names do agree:
       if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
         localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
-          ("'" & proto.name.s & "' from " & $proto.info))
+          ("'" & proto.name.s & "' from " & c.config$proto.info))
     if sfForward notin proto.flags:
       wrongRedefinition(c, n.info, proto.name.s)
     excl(proto.flags, sfForward)
@@ -1584,7 +1587,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     # only used for overload resolution (there is no instantiation of
     # the symbol, so we must process the body now)
     if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not
-        cursorInProc(n.sons[bodyPos]):
+        cursorInProc(c.config, n.sons[bodyPos]):
       discard "speed up nimsuggest"
       if s.kind == skMethod: semMethodPrototype(c, s, n)
     else:
@@ -1604,7 +1607,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
           n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
       else:
         if s.typ.sons[0] != nil and kind != skIterator:
-          addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
+          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
 
         openScope(c)
         n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
@@ -1613,7 +1616,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         if s.kind == skMethod: semMethodPrototype(c, s, n)
       if sfImportc in s.flags:
         # so we just ignore the body after semantic checking for importc:
-        n.sons[bodyPos] = ast.emptyNode
+        n.sons[bodyPos] = c.graph.emptyNode
       popProcCon(c)
   else:
     if s.kind == skMethod: semMethodPrototype(c, s, n)
@@ -1692,7 +1695,7 @@ proc semMethod(c: PContext, n: PNode): PNode =
     let ret = s.typ.sons[0]
     disp.typ.sons[0] = ret
     if disp.ast[resultPos].kind == nkSym:
-      if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode
+      if isEmptyType(ret): disp.ast.sons[resultPos] = c.graph.emptyNode
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
@@ -1738,9 +1741,9 @@ proc evalInclude(c: PContext, n: PNode): PNode =
     var f = checkModuleName(c.config, n.sons[i])
     if f != InvalidFileIDX:
       if containsOrIncl(c.includedFiles, f.int):
-        localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
+        localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
       else:
-        addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache)))
+        addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f)))
         excl(c.includedFiles, f.int)
 
 proc setLine(n: PNode, info: TLineInfo) =
@@ -1769,9 +1772,11 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   let a = semStmt(c, n.sons[0])
   dec c.inStaticContext
   n.sons[0] = a
-  evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
-  result = newNodeI(nkDiscardStmt, n.info, 1)
-  result.sons[0] = emptyNode
+  evalStaticStmt(c.module, c.graph, a, c.p.owner)
+  # for incremental replays, keep the AST as required for replays:
+  result = n
+  #result = newNodeI(nkDiscardStmt, n.info, 1)
+  #result.sons[0] = c.graph.emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
@@ -1834,9 +1839,9 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
           localError(c.config, result.info, "concept predicate failed")
       of tyUnknown: continue
       else: discard
-    if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
+    if n.sons[i].typ == c.enforceVoidContext: #or usesResult(n.sons[i]):
       voidContext = true
-      n.typ = enforceVoidContext
+      n.typ = c.enforceVoidContext
     if i == last and (length == 1 or efWantValue in flags):
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 352bc5c6b..086d09091 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -99,7 +99,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
   for i in 0 ..< n.len:
-    toMixin.incl(considerQuotedIdent(c.config, n.sons[i]).id)
+    toMixin.incl(considerQuotedIdent(c, n.sons[i]).id)
   result = newNodeI(nkEmpty, n.info)
 
 proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
@@ -166,7 +166,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
       result.sons[i] = onlyReplaceParams(c, n.sons[i])
 
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerQuotedIdent(c.c.config, n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
@@ -206,7 +206,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
       #
       # We need to ensure that both 'a' produce the same gensym'ed symbol.
       # So we need only check the *current* scope.
-      let s = localSearchInScope(c.c, considerQuotedIdent(c.c.config, ident))
+      let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident))
       if s != nil and s.owner == c.owner and sfGenSym in s.flags:
         styleCheckUse(n.info, s)
         replaceIdentBySym(c.c, n, newSymNode(s, n.info))
@@ -305,7 +305,7 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   result = n
-  semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
@@ -460,14 +460,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         x.sons[1] = semTemplBody(c, x.sons[1])
   of nkBracketExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("[]"), n.info)
+    result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     let n0 = semTemplBody(c, n.sons[0])
     withBracketExpr c, n0:
       result = semTemplBodySons(c, result)
   of nkCurlyExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("{}"), n.info)
+    result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
   of nkAsgn, nkFastAsgn:
@@ -479,7 +479,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     case k
     of nkBracketExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("[]="), n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       let a0 = semTemplBody(c, a.sons[0])
@@ -487,7 +487,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = semTemplBodySons(c, result)
     of nkCurlyExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("{}="), n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       result = semTemplBodySons(c, result)
@@ -518,7 +518,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
 
 proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
   result = n
-  semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
   case n.kind
   of nkIdent:
     let s = qualifiedLookUp(c.c, n, {})
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 8b5c26f99..d15ff8412 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -66,7 +66,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     base = semTypeNode(c, n.sons[0].sons[0], nil)
     if base.kind != tyEnum:
       localError(c.config, n.sons[0].info, "inheritance only works with an enum")
-    counter = lastOrd(base) + 1
+    counter = lastOrd(c.config, base) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
   var symbols: TStrTable
@@ -133,7 +133,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
         localError(c.config, n.info, errOrdinalTypeExpected)
-      elif lengthOrd(base) > MaxSetElements:
+      elif lengthOrd(c.config, base) > MaxSetElements:
         localError(c.config, n.info, errSetTooBig)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "set")
@@ -157,7 +157,7 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
     var base = semTypeNode(c, n.sons[1], nil)
     addSonSkipIntLit(result, base)
     if sonsLen(n) == 3:
-      result.n = newIdentNode(considerQuotedIdent(c.config, n.sons[2]), n.sons[2].info)
+      result.n = newIdentNode(considerQuotedIdent(c, n.sons[2]), n.sons[2].info)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
     addSonSkipIntLit(result, errorType(c))
@@ -267,7 +267,7 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
           n.sons[1].floatVal < 0.0:
         incl(result.flags, tfNeedsInit)
     else:
-      if n[1].kind == nkInfix and considerQuotedIdent(c.config, n[1][0]).s == "..<":
+      if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
         localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
       else:
         localError(c.config, n.sons[0].info, "expected range")
@@ -462,7 +462,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
       # for gensym'ed identifiers the identifier may already have been
       # transformed to a symbol and we need to use that here:
       result = newSymG(kind, n.sons[1], c)
-      var v = considerQuotedIdent(c.config, n.sons[0])
+      var v = considerQuotedIdent(c, n.sons[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
@@ -553,7 +553,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
         inc(covered)
       else:
         if r.kind == nkCurly:
-          r = r.deduplicate
+          r = deduplicate(c.config, r)
 
         # first element is special and will overwrite: branch.sons[i]:
         branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered)
@@ -584,10 +584,10 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
   if not isOrdinalType(typ):
     localError(c.config, n.info, "selector must be of an ordinal type")
-  elif firstOrd(typ) != 0:
+  elif firstOrd(c.config, typ) != 0:
     localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
                                      ") must be 0 for discriminant")
-  elif lengthOrd(typ) > 0x00007FFF:
+  elif lengthOrd(c.config, typ) > 0x00007FFF:
     localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
   var chckCovered = true
   for i in countup(1, sonsLen(n) - 1):
@@ -603,7 +603,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
     else: illFormedAst(n, c.config)
     delSon(b, sonsLen(b) - 1)
     semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype)
-  if chckCovered and covered != lengthOrd(a.sons[0].typ):
+  if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
     localError(c.config, a.info, "not all cases are covered")
   addSon(father, a)
 
@@ -658,7 +658,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     var length = sonsLen(n)
     var a: PNode
     if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
-    else: a = ast.emptyNode
+    else: a = newNodeI(nkEmpty, n.info)
     if n.sons[length-1].kind != nkEmpty:
       localError(c.config, n.sons[length-1].info, errInitHereNotAllowed)
     var typ: PType
@@ -770,7 +770,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
   semRecordNodeAux(c, n.sons[2], check, pos, result.n, result)
   if n.sons[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
-    var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c)
+    var s = newSymS(skType, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
     s.typ = result
     pragma(c, s, n.sons[0], typePragmas)
   if base == nil and tfInheritable notin result.flags:
@@ -804,8 +804,6 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   else:
     if sfGenSym notin param.flags: addDecl(c, param)
 
-let typedescId = getIdent"typedesc"
-
 template shouldHaveMeta(t) =
   internalAssert c.config, tfHasMeta in t.flags
   # result.lastSon.flags.incl tfHasMeta
@@ -815,13 +813,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    info: TLineInfo, anon = false): PType =
   if paramType == nil: return # (e.g. proc return type)
 
-  proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
+  proc addImplicitGenericImpl(c: PContext; typeClass: PType, typId: PIdent): PType =
     if genericParams == nil:
       # This happens with anonymous proc types appearing in signatures
       # XXX: we need to lift these earlier
       return
     let finalTypId = if typId != nil: typId
-                     else: getIdent(paramName & ":type")
+                     else: getIdent(c.cache, paramName & ":type")
     # is this a bindOnce type class already present in the param list?
     for i in countup(0, genericParams.len - 1):
       if genericParams.sons[i].sym.name.id == finalTypId.id:
@@ -852,11 +850,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     (if lifted != nil: lifted else: typ)
 
   template addImplicitGeneric(e): untyped =
-    addImplicitGenericImpl(e, paramTypId)
+    addImplicitGenericImpl(c, e, paramTypId)
 
   case paramType.kind:
   of tyAnything:
-    result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
+    result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
 
   of tyStatic:
     # proc(a: expr{string}, b: expr{nkLambda})
@@ -873,7 +871,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     if tfUnresolved notin paramType.flags:
       # naked typedescs are not bindOnce types
       if paramType.base.kind == tyNone and paramTypId != nil and
-         paramTypId.id == typedescId.id: paramTypId = nil
+          paramTypId.id == getIdent(c.cache, "typedesc").id:
+        # XXX Why doesn't this check for tyTypeDesc instead?
+        paramTypId = nil
       result = addImplicitGeneric(
         c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
 
@@ -1322,7 +1322,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
   # start with 'ccClosure', but of course pragmas can overwrite this:
   result.callConv = ccClosure
   # dummy symbol for `pragma`:
-  var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c)
+  var s = newSymS(kind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
   s.typ = result
   if n.sons[1].kind != nkEmpty and n.sons[1].len > 0:
     pragma(c, s, n.sons[1], procTypePragmas)
@@ -1345,7 +1345,7 @@ proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
 
 proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
   if n.kind == nkType:
-    result = symFromType(n.typ, n.info)
+    result = symFromType(c, n.typ, n.info)
   else:
     localError(c.config, n.info, errTypeExpected)
     result = errorSym(c, n)
@@ -1393,7 +1393,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n, prev)
     else:
-      let op = considerQuotedIdent(c.config, n.sons[0])
+      let op = considerQuotedIdent(c, n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
         checkSonsLen(n, 3, c.config)
         var
@@ -1585,7 +1585,7 @@ when false:
     result = semTypeNodeInner(c, n, prev)
     instAllTypeBoundOp(c, n.info)
 
-proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
+proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
   # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
   m.typ.kind = kind
   m.typ.size = size
@@ -1596,10 +1596,10 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
 
   # FIXME: proper support for clongdouble should be added.
   # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
-  if targetCPU == cpuI386 and size == 8:
+  if conf.target.targetCPU == cpuI386 and size == 8:
     #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
     if kind in {tyFloat64, tyFloat} and
-       targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
+        conf.target.targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
       m.typ.align = 4
     # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double)
     elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}:
@@ -1609,73 +1609,73 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
 
 proc processMagicType(c: PContext, m: PSym) =
   case m.magic
-  of mInt: setMagicType(m, tyInt, intSize)
-  of mInt8: setMagicType(m, tyInt8, 1)
-  of mInt16: setMagicType(m, tyInt16, 2)
-  of mInt32: setMagicType(m, tyInt32, 4)
-  of mInt64: setMagicType(m, tyInt64, 8)
-  of mUInt: setMagicType(m, tyUInt, intSize)
-  of mUInt8: setMagicType(m, tyUInt8, 1)
-  of mUInt16: setMagicType(m, tyUInt16, 2)
-  of mUInt32: setMagicType(m, tyUInt32, 4)
-  of mUInt64: setMagicType(m, tyUInt64, 8)
-  of mFloat: setMagicType(m, tyFloat, floatSize)
-  of mFloat32: setMagicType(m, tyFloat32, 4)
-  of mFloat64: setMagicType(m, tyFloat64, 8)
-  of mFloat128: setMagicType(m, tyFloat128, 16)
-  of mBool: setMagicType(m, tyBool, 1)
-  of mChar: setMagicType(m, tyChar, 1)
+  of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize)
+  of mInt8: setMagicType(c.config, m, tyInt8, 1)
+  of mInt16: setMagicType(c.config, m, tyInt16, 2)
+  of mInt32: setMagicType(c.config, m, tyInt32, 4)
+  of mInt64: setMagicType(c.config, m, tyInt64, 8)
+  of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize)
+  of mUInt8: setMagicType(c.config, m, tyUInt8, 1)
+  of mUInt16: setMagicType(c.config, m, tyUInt16, 2)
+  of mUInt32: setMagicType(c.config, m, tyUInt32, 4)
+  of mUInt64: setMagicType(c.config, m, tyUInt64, 8)
+  of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize)
+  of mFloat32: setMagicType(c.config, m, tyFloat32, 4)
+  of mFloat64: setMagicType(c.config, m, tyFloat64, 8)
+  of mFloat128: setMagicType(c.config, m, tyFloat128, 16)
+  of mBool: setMagicType(c.config, m, tyBool, 1)
+  of mChar: setMagicType(c.config, m, tyChar, 1)
   of mString:
-    setMagicType(m, tyString, ptrSize)
+    setMagicType(c.config, m, tyString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
   of mCstring:
-    setMagicType(m, tyCString, ptrSize)
+    setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
-  of mPointer: setMagicType(m, tyPointer, ptrSize)
+  of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize)
   of mEmptySet:
-    setMagicType(m, tySet, 1)
+    setMagicType(c.config, m, tySet, 1)
     rawAddSon(m.typ, newTypeS(tyEmpty, c))
-  of mIntSetBaseType: setMagicType(m, tyRange, intSize)
-  of mNil: setMagicType(m, tyNil, ptrSize)
+  of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize)
+  of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
   of mExpr:
     if m.name.s == "auto":
-      setMagicType(m, tyAnything, 0)
+      setMagicType(c.config, m, tyAnything, 0)
     else:
-      setMagicType(m, tyExpr, 0)
+      setMagicType(c.config, m, tyExpr, 0)
       if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt
   of mStmt:
-    setMagicType(m, tyStmt, 0)
+    setMagicType(c.config, m, tyStmt, 0)
     if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
   of mTypeDesc:
-    setMagicType(m, tyTypeDesc, 0)
+    setMagicType(c.config, m, tyTypeDesc, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mVoidType:
-    setMagicType(m, tyVoid, 0)
+    setMagicType(c.config, m, tyVoid, 0)
   of mArray:
-    setMagicType(m, tyArray, 0)
+    setMagicType(c.config, m, tyArray, 0)
   of mOpenArray:
-    setMagicType(m, tyOpenArray, 0)
+    setMagicType(c.config, m, tyOpenArray, 0)
   of mVarargs:
-    setMagicType(m, tyVarargs, 0)
+    setMagicType(c.config, m, tyVarargs, 0)
   of mRange:
-    setMagicType(m, tyRange, 0)
+    setMagicType(c.config, m, tyRange, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mSet:
-    setMagicType(m, tySet, 0)
+    setMagicType(c.config, m, tySet, 0)
   of mSeq:
-    setMagicType(m, tySequence, 0)
+    setMagicType(c.config, m, tySequence, 0)
   of mOpt:
-    setMagicType(m, tyOpt, 0)
+    setMagicType(c.config, m, tyOpt, 0)
   of mOrdinal:
-    setMagicType(m, tyOrdinal, 0)
+    setMagicType(c.config, m, tyOrdinal, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mPNimrodNode:
     incl m.typ.flags, tfTriggersCompileTime
   of mException: discard
   of mBuiltinType:
     case m.name.s
-    of "lent": setMagicType(m, tyLent, ptrSize)
-    of "sink": setMagicType(m, tySink, 0)
+    of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
+    of "sink": setMagicType(c.config, m, tySink, 0)
     else: localError(c.config, m.info, errTypeExpected)
   else: localError(c.config, m.info, errTypeExpected)
 
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 61d92bb19..a24972d04 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -9,7 +9,8 @@
 
 # This module does the instantiation of generic types.
 
-import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
+import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
+  lineinfos
 
 const
   tfInstClearedFlags = {tfHasMeta, tfUnresolved}
@@ -27,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
     localError(conf, info, "invalid pragma: acyclic")
   elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
-  elif computeSize(t) == szIllegalRecursion:
+  elif computeSize(conf, t) == szIllegalRecursion:
     localError(conf, info,  "illegal recursion in type '" & typeToString(t) & "'")
   when false:
     if t.kind == tyObject and t.sons[0] != nil:
@@ -568,35 +569,35 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), n.info, owner)
   cl.allowMetaTypes = allowMetaTypes
-  pushInfoContext(n.info)
+  pushInfoContext(p.config, n.info)
   result = replaceTypeVarsN(cl, n)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
                             original, new: PSym): PNode =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), n.info, original)
   idTablePut(cl.symMap, original, new)
-  pushInfoContext(n.info)
+  pushInfoContext(p.config, n.info)
   result = replaceTypeVarsN(cl, n)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
                            t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), info, nil)
-  pushInfoContext(info)
+  pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
                                  t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), info, nil)
   cl.allowMetaTypes = true
-  pushInfoContext(info)
+  pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
-  popInfoContext()
+  popInfoContext(p.config)
 
 template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
                                t: PType): untyped =
diff --git a/compiler/service.nim b/compiler/service.nim
index f1a988ae5..0e82c03f8 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -11,7 +11,7 @@
 
 import
   times, commands, options, msgs, nimconf,
-  extccomp, strutils, os, platform, parseopt, idents, configuration
+  extccomp, strutils, os, platform, parseopt, idents, lineinfos
 
 when useCaas:
   import net
@@ -26,25 +26,6 @@ var
     # in caas mode, the list of defines and options will be given at start-up?
     # it's enough to check that the previous compilation command is the same?
 
-proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
-  var p = parseopt.initOptParser(cmd)
-  var argsCount = 0
-  while true:
-    parseopt.next(p)
-    case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      if p.key == " ":
-        p.key = "-"
-        if processArgument(pass, p, argsCount, config): break
-      else:
-        processSwitch(pass, p, config)
-    of cmdArgument:
-      if processArgument(pass, p, argsCount, config): break
-  if pass == passCmd2:
-    if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
-      rawMessage(config, errGenerated, errArgsNeedRunOption)
-
 proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) =
   template execute(cmd) =
     curCaasCmd = cmd
@@ -71,7 +52,7 @@ proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; conf
       var inp = "".TaintedString
       server.listen()
       var stdoutSocket = newSocket()
-      msgs.writelnHook = proc (line: string) =
+      config.writelnHook = proc (line: string) =
         stdoutSocket.send(line & "\c\L")
 
       while true:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 41cac2a4a..ab5ac7b45 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -13,9 +13,9 @@
 import
   intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
   magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
-  nimfix.pretty
+  nimfix / pretty, lineinfos
 
-when not defined(noDocgen):
+when defined(booting) or defined(nimsuggest):
   import docgen
 
 type
@@ -253,7 +253,7 @@ proc complexDisambiguation(a, b: PType): int =
     result = x - y
 
 proc writeMatches*(c: TCandidate) =
-  echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info
+  echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info
   echo "  exact matches: ", c.exactMatches
   echo "  generic matches: ", c.genericMatches
   echo "  subtype matches: ", c.subtypeMatches
@@ -376,8 +376,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
     if k == f.kind: result = isSubrange
     elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64,
                                    tyUInt..tyUInt64} and
-        isIntLit(ab) and ab.n.intVal >= firstOrd(f) and
-                         ab.n.intVal <= lastOrd(f):
+        isIntLit(ab) and ab.n.intVal >= firstOrd(nil, f) and
+                         ab.n.intVal <= lastOrd(nil, f):
+      # passing 'nil' to firstOrd/lastOrd here as type checking rules should
+      # not depent on the target integer size configurations!
       # integer literal in the proper range; we want ``i16 + 4`` to stay an
       # ``int16`` operation so we declare the ``4`` pseudo-equal to int16
       result = isFromIntLit
@@ -387,8 +389,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
       result = isConvertible
     elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64,
                                                   tyUInt8..tyUInt32} and
-                         a.n[0].intVal >= firstOrd(f) and
-                         a.n[1].intVal <= lastOrd(f):
+                         a.n[0].intVal >= firstOrd(nil, f) and
+                         a.n[1].intVal <= lastOrd(nil, f):
+      # passing 'nil' to firstOrd/lastOrd here as type checking rules should
+      # not depent on the target integer size configurations!
       result = isConvertible
     else: result = isNone
     #elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
@@ -634,10 +638,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 
 proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
   let
-    a0 = firstOrd(a)
-    a1 = lastOrd(a)
-    f0 = firstOrd(f)
-    f1 = lastOrd(f)
+    a0 = firstOrd(nil, a)
+    a1 = lastOrd(nil, a)
+    f0 = firstOrd(nil, f)
+    f1 = lastOrd(nil, f)
   if a0 == f0 and a1 == f1:
     result = isEqual
   elif a0 >= f0 and a1 <= f1:
@@ -718,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
       addDecl(c, param)
 
   var
-    oldWriteHook: type(writelnHook)
+    oldWriteHook: type(m.c.config.writelnHook)
     diagnostics: seq[string]
     errorPrefix: string
     flags: TExprFlags = {}
@@ -726,12 +730,12 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
                          sfExplain in typeClass.sym.flags
 
   if collectDiagnostics:
-    oldWriteHook = writelnHook
+    oldWriteHook = m.c.config.writelnHook
     # XXX: we can't write to m.diagnostics directly, because
     # Nim doesn't support capturing var params in closures
     diagnostics = @[]
     flags = {efExplain}
-    writelnHook = proc (s: string) =
+    m.c.config.writelnHook = proc (s: string) =
       if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
@@ -740,7 +744,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   var checkedBody = c.semTryExpr(c, body.copyTree, flags)
 
   if collectDiagnostics:
-    writelnHook = oldWriteHook
+    m.c.config.writelnHook = oldWriteHook
     for msg in diagnostics:
       m.diagnostics.safeAdd msg
       m.diagnosticsEnabled = true
@@ -764,11 +768,11 @@ proc shouldSkipDistinct(m: TCandidate; rules: PNode, callIdent: PIdent): bool =
   # XXX This is bad as 'considerQuotedIdent' can produce an error!
   if rules.kind == nkWith:
     for r in rules:
-      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return true
+      if considerQuotedIdent(m.c, r) == callIdent: return true
     return false
   else:
     for r in rules:
-      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return false
+      if considerQuotedIdent(m.c, r) == callIdent: return false
     return true
 
 proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
@@ -887,17 +891,17 @@ proc inferStaticsInRange(c: var TCandidate,
     if inferStaticParam(c, exp, rhs):
       return isGeneric
     else:
-      failureToInferStaticParam(c.c.graph.config, exp)
+      failureToInferStaticParam(c.c.config, exp)
 
   if lowerBound.kind == nkIntLit:
     if upperBound.kind == nkIntLit:
-      if lengthOrd(concrete) == upperBound.intVal - lowerBound.intVal + 1:
+      if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1:
         return isGeneric
       else:
         return isNone
-    doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
+    doInferStatic(upperBound, lengthOrd(c.c.config, concrete) + lowerBound.intVal - 1)
   elif upperBound.kind == nkIntLit:
-    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
+    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(c.c.config, concrete))
 
 template subtypeCheck() =
   if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
@@ -1176,7 +1180,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
       else:
-        if lengthOrd(fRange) != lengthOrd(aRange):
+        if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
           result = isNone
     else: discard
   of tyOpenArray, tyVarargs:
@@ -1342,7 +1346,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       if a.len == 1:
         let pointsTo = a.sons[0].skipTypes(abstractInst)
         if pointsTo.kind == tyChar: result = isConvertible
-        elif pointsTo.kind == tyArray and firstOrd(pointsTo.sons[0]) == 0 and
+        elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo.sons[0]) == 0 and
             skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and
             pointsTo.sons[1].kind == tyChar:
           result = isConvertible
@@ -1774,7 +1778,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
   else:
     result.typ = f
   if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
-  addSon(result, ast.emptyNode)
+  addSon(result, c.graph.emptyNode)
   addSon(result, arg)
 
 proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
@@ -1993,7 +1997,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
-      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {})
+      let p = c.graph
+      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
+          params = p.emptyNode, name = p.emptyNode, pattern = p.emptyNode,
+          genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {})
       if f.kind == tyBuiltInTypeClass:
         inc m.genericMatches
         put(m, f, lifted.typ)
@@ -2119,10 +2126,10 @@ proc prepareOperand(c: PContext; a: PNode): PNode =
     result = a
     considerGenSyms(c, result)
 
-proc prepareNamedParam(a: PNode; conf: ConfigRef) =
+proc prepareNamedParam(a: PNode; c: PContext) =
   if a.sons[0].kind != nkIdent:
     var info = a.sons[0].info
-    a.sons[0] = newIdentNode(considerQuotedIdent(conf, a.sons[0]), info)
+    a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0]), info)
 
 proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
@@ -2187,7 +2194,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
     elif n.sons[a].kind == nkExprEqExpr:
       # named param
       # check if m.callee has such a param:
-      prepareNamedParam(n.sons[a], c.config)
+      prepareNamedParam(n.sons[a], c)
       if n.sons[a].sons[0].kind != nkIdent:
         localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
         m.state = csNoMatch
@@ -2354,7 +2361,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
 proc argtypeMatches*(c: PContext, f, a: PType): bool =
   var m: TCandidate
   initCandidate(c, m, f)
-  let res = paramTypesMatch(m, f, a, ast.emptyNode, nil)
+  let res = paramTypesMatch(m, f, a, c.graph.emptyNode, nil)
   #instantiateGenericConverters(c, res, m)
   # XXX this is used by patterns.nim too; I think it's better to not
   # instantiate generic converters for that
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 23aecfa71..a21d64338 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,7 +32,7 @@
 
 # included from sigmatch.nim
 
-import algorithm, prefixmatches, configuration
+import algorithm, prefixmatches, lineinfos
 from wordrecg import wDeprecated
 
 when defined(nimsuggest):
@@ -41,31 +41,6 @@ when defined(nimsuggest):
 const
   sep = '\t'
 
-type
-  Suggest* = ref object
-    section*: IdeCmd
-    qualifiedPath*: seq[string]
-    name*: PIdent                # not used beyond sorting purposes; name is also
-                                 # part of 'qualifiedPath'
-    filePath*: string
-    line*: int                   # Starts at 1
-    column*: int                 # Starts at 0
-    doc*: string           # Not escaped (yet)
-    symkind*: TSymKind
-    forth*: string               # type
-    quality*: range[0..100]   # matching quality
-    isGlobal*: bool # is a global variable
-    contextFits*: bool # type/non-type context matches
-    prefix*: PrefixMatch
-    scope*, localUsages*, globalUsages*: int # more usages is better
-    tokenLen*: int
-  Suggestions* = seq[Suggest]
-
-var
-  suggestionResultHook*: proc (result: Suggest) {.closure.}
-  suggestVersion*: int
-  suggestMaxResults* = 10_000
-
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
 template origModuleName(m: PSym): string = m.name.s
@@ -104,7 +79,7 @@ proc cmpSuggestions(a, b: Suggest): int =
   cf globalUsages
   # if all is equal, sort alphabetically for deterministic output,
   # independent of hashing order:
-  result = cmp(a.name.s, b.name.s)
+  result = cmp(a.name[], b.name[])
 
 proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
@@ -117,14 +92,14 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
   result.prefix = prefix
   result.contextFits = inTypeContext == (s.kind in {skType, skGenericParam})
   result.scope = scope
-  result.name = s.name
+  result.name = addr s.name.s
   when defined(nimsuggest):
     result.globalUsages = s.allUsages.len
     var c = 0
     for u in s.allUsages:
       if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
-  result.symkind = s.kind
+  result.symkind = byte s.kind
   if optIdeTerse notin conf.globalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
@@ -140,23 +115,24 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
       result.forth = typeToString(s.typ)
     else:
       result.forth = ""
-    when not defined(noDocgen):
+    when defined(nimsuggest) and not defined(noDocgen):
       result.doc = s.extractDocComment
   let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info
-  result.filePath = toFullPath(infox)
+  result.filePath = toFullPath(conf, infox)
   result.line = toLinenumber(infox)
   result.column = toColumn(infox)
+  result.version = conf.suggestVersion
 
 proc `$`*(suggest: Suggest): string =
   result = $suggest.section
   result.add(sep)
   if suggest.section == ideHighlight:
-    if suggest.symkind == skVar and suggest.isGlobal:
+    if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
       result.add("skGlobalVar")
-    elif suggest.symkind == skLet and suggest.isGlobal:
+    elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
       result.add("skGlobalLet")
     else:
-      result.add($suggest.symkind)
+      result.add($suggest.symkind.TSymKind)
     result.add(sep)
     result.add($suggest.line)
     result.add(sep)
@@ -164,9 +140,9 @@ proc `$`*(suggest: Suggest): string =
     result.add(sep)
     result.add($suggest.tokenLen)
   else:
-    result.add($suggest.symkind)
+    result.add($suggest.symkind.TSymKind)
     result.add(sep)
-    if suggest.qualifiedPath != nil:
+    if suggest.qualifiedPath.len != 0:
       result.add(suggest.qualifiedPath.join("."))
     result.add(sep)
     result.add(suggest.forth)
@@ -177,20 +153,20 @@ proc `$`*(suggest: Suggest): string =
     result.add(sep)
     result.add($suggest.column)
     result.add(sep)
-    when not defined(noDocgen):
+    when defined(nimsuggest) and not defined(noDocgen):
       result.add(suggest.doc.escape)
-    if suggestVersion == 0:
+    if suggest.version == 0:
       result.add(sep)
       result.add($suggest.quality)
       if suggest.section == ideSug:
         result.add(sep)
         result.add($suggest.prefix)
 
-proc suggestResult(s: Suggest) =
-  if not isNil(suggestionResultHook):
-    suggestionResultHook(s)
+proc suggestResult(conf: ConfigRef; s: Suggest) =
+  if not isNil(conf.suggestionResultHook):
+    conf.suggestionResultHook(s)
   else:
-    suggestWriteln($s)
+    conf.suggestWriteln($s)
 
 proc produceOutput(a: var Suggestions; conf: ConfigRef) =
   if conf.ideCmd in {ideSug, ideCon}:
@@ -198,13 +174,13 @@ proc produceOutput(a: var Suggestions; conf: ConfigRef) =
   when defined(debug):
     # debug code
     writeStackTrace()
-  if a.len > suggestMaxResults: a.setLen(suggestMaxResults)
-  if not isNil(suggestionResultHook):
+  if a.len > conf.suggestMaxResults: a.setLen(conf.suggestMaxResults)
+  if not isNil(conf.suggestionResultHook):
     for s in a:
-      suggestionResultHook(s)
+      conf.suggestionResultHook(s)
   else:
     for s in a:
-      suggestWriteln($s)
+      conf.suggestWriteln($s)
 
 proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
   proc prefixMatch(s: PSym; n: PNode): PrefixMatch =
@@ -340,14 +316,14 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
   var typ = n.typ
   var pm: PrefixMatch
   when defined(nimsuggest):
-    if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0:
+    if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
-      let fullPath = findModule(c.config, n.sym.name.s, n.info.toFullPath)
+      let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info))
       if fullPath.len == 0:
         # error: no known module name:
         typ = nil
       else:
-        let m = gImportModule(c.graph, c.module, fileInfoIdx(c.config, fullpath), c.cache)
+        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):
@@ -397,17 +373,17 @@ type
   TCheckPointResult* = enum
     cpNone, cpFuzzy, cpExact
 
-proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
-  if current.fileIndex == gTrackPos.fileIndex:
-    if current.line == gTrackPos.line and
-        abs(current.col-gTrackPos.col) < 4:
+proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult =
+  if current.fileIndex == trackPos.fileIndex:
+    if current.line == trackPos.line and
+        abs(current.col-trackPos.col) < 4:
       return cpExact
-    if current.line >= gTrackPos.line:
+    if current.line >= trackPos.line:
       return cpFuzzy
 
-proc isTracked*(current: TLineInfo, tokenLen: int): bool =
-  if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
-    let col = gTrackPos.col
+proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
+  if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line:
+    let col = trackPos.col
     if col >= current.col and col <= current.col+tokenLen-1:
       return true
 
@@ -425,30 +401,27 @@ when defined(nimsuggest):
       if infoB.infoToInt == infoAsInt: return
     s.allUsages.add(info)
 
-var
-  lastLineInfo*: TLineInfo # XXX global here
-
 proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
-  if suggestVersion == 1:
-    if usageSym == nil and isTracked(info, s.name.s.len):
+  if conf.suggestVersion == 1:
+    if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len):
       usageSym = s
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
-      if lastLineInfo != info:
-        suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
-      lastLineInfo = info
+      if conf.lastLineInfo != info:
+        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      conf.lastLineInfo = info
 
 when defined(nimsuggest):
   proc listUsages*(conf: ConfigRef; s: PSym) =
     #echo "usages ", len(s.allUsages)
     for info in s.allUsages:
       let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
-      suggestResult(symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
 proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.isNil: return
-  if isTracked(info, s.name.s.len):
-    suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+  if isTracked(info, conf.m.trackPos, s.name.s.len):
+    suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -460,7 +433,7 @@ proc ensureSeq[T](x: var seq[T]) =
 proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
   when defined(nimsuggest):
-    if suggestVersion == 0:
+    if conf.suggestVersion == 0:
       if s.allUsages.isNil:
         s.allUsages = @[info]
       else:
@@ -471,14 +444,14 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
     elif conf.ideCmd == ideDef:
       findDefinition(conf, info, s)
     elif conf.ideCmd == ideDus and s != nil:
-      if isTracked(info, s.name.s.len):
-        suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+      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)
-    elif conf.ideCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
-    elif conf.ideCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
+    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))
+    elif conf.ideCmd == ideOutline and info.fileIndex == conf.m.trackPos.fileIndex and
         isDecl:
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
 proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.kind in routineKinds:
@@ -510,7 +483,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
   try:
     result = c.semExpr(c, n)
   except ERecoverableError:
-    result = ast.emptyNode
+    result = c.graph.emptyNode
 
 proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
   if n.kind == nkDotExpr:
@@ -519,14 +492,14 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
     # of the next line, so we check the 'field' is actually on the same
     # line as the object to prevent this from happening:
     let prefix = if n.len == 2 and n[1].info.line == n[0].info.line and
-       not gTrackPosAttached: n[1] else: nil
+       not c.config.m.trackPosAttached: n[1] else: nil
     suggestFieldAccess(c, obj, prefix, outputs)
 
     #if optIdeDebug in gGlobalOptions:
     #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
     #writeStackTrace()
   else:
-    let prefix = if gTrackPosAttached: nil else: n
+    let prefix = if c.config.m.trackPosAttached: nil else: n
     suggestEverything(c, n, prefix, outputs)
 
 proc suggestExprNoCheck*(c: PContext, n: PNode) =
@@ -555,10 +528,10 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
     suggestQuit()
 
 proc suggestExpr*(c: PContext, n: PNode) =
-  if exactEquals(gTrackPos, n.info): suggestExprNoCheck(c, n)
+  if exactEquals(c.config.m.trackPos, n.info): suggestExprNoCheck(c, n)
 
 proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
-  let attached = gTrackPosAttached
+  let attached = c.config.m.trackPosAttached
   if attached: inc(c.inTypeContext)
   defer:
     if attached: dec(c.inTypeContext)
@@ -574,7 +547,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
   if outputs.len > 0: suggestQuit()
 
 proc suggestSentinel*(c: PContext) =
-  if c.config.ideCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return
+  if c.config.ideCmd != ideSug or c.module.position != c.config.m.trackPos.fileIndex.int32: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
@@ -587,7 +560,9 @@ proc suggestSentinel*(c: PContext) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSymNoOpr(it, nil, pm):
-        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
+        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
+            newLineInfo(c.config.m.trackPos.fileIndex, -1, -1), 0,
+            PrefixMatch.None, false, scopeN))
 
   dec(c.compilesContextId)
   produceOutput(outputs, c.config)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4bc153e46..069f65eee 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -11,7 +11,7 @@
 
 import
   strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
-  filters, filter_tmpl, renderer, configuration
+  filters, filter_tmpl, renderer, lineinfos
 
 type
   TFilterKind* = enum
@@ -38,7 +38,6 @@ proc parseAll*(p: var TParsers): PNode =
     result = parser.parseAll(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc parseTopLevelStmt*(p: var TParsers): PNode =
   case p.skin
@@ -46,7 +45,6 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
     result = parser.parseTopLevelStmt(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc utf8Bom(s: string): int =
   if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
@@ -62,7 +60,7 @@ proc containsShebang(s: string, i: int): bool =
 
 proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
                config: ConfigRef): PNode =
-  result = ast.emptyNode
+  result = newNode(nkEmpty)
   var s = llStreamOpen(filename, fmRead)
   if s != nil:
     var line = newStringOfCap(80)
@@ -144,7 +142,7 @@ proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
   assert config != nil
   var s: PLLStream
   p.skin = skinStandard
-  let filename = fileIdx.toFullPathConsiderDirty
+  let filename = toFullPathConsiderDirty(config, fileIdx)
   var pipe = parsePipe(filename, inputstream, cache, config)
   p.config() = config
   if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
@@ -162,7 +160,7 @@ proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode
   var
     p: TParsers
     f: File
-  let filename = fileIdx.toFullPathConsiderDirty
+  let filename = toFullPathConsiderDirty(config, fileIdx)
   if not open(f, filename):
     rawMessage(config, errGenerated, "cannot open file: " & filename)
     return
diff --git a/compiler/transf.nim b/compiler/transf.nim
index a10e8a1e5..72bcb6d71 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -20,9 +20,9 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
-  idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
+  idents, renderer, types, passes, semfold, magicsys, cgmeth,
   lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals,
-  modulegraphs
+  modulegraphs, lineinfos
 
 type
   PTransNode* = distinct PNode
@@ -94,7 +94,7 @@ proc getCurrOwner(c: PTransf): PSym =
   else: result = c.module
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
-  let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
+  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info)
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
@@ -194,7 +194,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
         idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
         defs[j] = x.PTransNode
       assert(it.sons[L-2].kind == nkEmpty)
-      defs[L-2] = ast.emptyNode.PTransNode
+      defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
       defs[L-1] = transform(c, it.sons[L-1])
       result[i] = defs
 
@@ -221,7 +221,7 @@ proc hasContinue(n: PNode): bool =
 
 proc newLabel(c: PTransf, n: PNode): PSym =
   result = newSym(skLabel, nil, getCurrOwner(c), n.info)
-  result.name = getIdent(genPrefix & $result.id)
+  result.name = getIdent(c.graph.cache, genPrefix & $result.id)
 
 proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) =
   if n.kind in {nkBlockStmt, nkBlockExpr}:
@@ -393,7 +393,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   if c.graph.config.cmd == cmdCompileToJS: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
-  conv.add(emptyNode)
+  conv.add(newNodeI(nkEmpty, prc.info))
   conv.add(prc)
   if prc.kind == nkClosure:
     internalError(c.graph.config, prc.info, "closure to closure created")
@@ -410,8 +410,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
     if not isOrdinalType(source):
       # float -> int conversions. ugh.
       result = transformSons(c, n)
-    elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
-        lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
+    elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n.sons[1].typ) and
+        lastOrd(c.graph.config, n.sons[1].typ) <= lastOrd(c.graph.config, n.typ):
       # BUGFIX: simply leave n as it is; we need a nkConv node,
       # but no range check:
       result = transformSons(c, n)
@@ -423,8 +423,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
         result = newTransNode(nkChckRange, n, 3)
       dest = skipTypes(n.typ, abstractVar)
       result[0] = transform(c, n.sons[1])
-      result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode
-      result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode
+      result[1] = newIntTypeNode(nkIntLit, firstOrd(c.graph.config, dest), dest).PTransNode
+      result[2] = newIntTypeNode(nkIntLit, lastOrd(c.graph.config, dest), dest).PTransNode
   of tyFloat..tyFloat128:
     # XXX int64 -> float conversion?
     if skipTypes(n.typ, abstractVar).kind == tyRange:
@@ -598,7 +598,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       idNodeTablePut(newC.mapping, formal, temp)
 
   var body = iter.getBody.copyTree
-  pushInfoContext(n.info)
+  pushInfoContext(c.graph.config, n.info)
   # XXX optimize this somehow. But the check "c.inlining" is not correct:
   var symMap: TIdTable
   initIdTable symMap
@@ -608,7 +608,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   add(stmtList, transform(c, body))
   #findWrongOwners(c, stmtList.pnode)
   dec(c.inlining)
-  popInfoContext()
+  popInfoContext(c.graph.config)
   popTransCon(c)
   # echo "transformed: ", stmtList.PNode.renderTree
 
@@ -721,16 +721,16 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
     let actions = newTransNode(nkStmtListExpr, n[1], 2)
     # Generating `let exc = (excType)(getCurrentException())`
     # -> getCurrentException()
-    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode))
+    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info)))
     # -> (excType)
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
-    convNode[0] = PTransNode(ast.emptyNode)
+    convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
     convNode[1] = excCall
     PNode(convNode).typ = excTypeNode.typ.toRef()
     # -> let exc = ...
     let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
     identDefs[0] = PTransNode(n[0][2])
-    identDefs[1] = PTransNode(ast.emptyNode)
+    identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info))
     identDefs[2] = convNode
 
     let letSection = newTransNode(nkLetSection, n[1].info, 1)
@@ -863,7 +863,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
         # ensure that e.g. discard "some comment" gets optimized away
         # completely:
         result = PTransNode(newNode(nkCommentStmt))
-  of nkCommentStmt, nkTemplateDef:
+  of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt:
     return n.PTransNode
   of nkConstSection:
     # do not replace ``const c = 3`` with ``const 3 = 3``
@@ -916,7 +916,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
   # this step! We have to rely that the semantic pass transforms too errornous
   # nodes into an empty node.
-  if c.rd != nil or nfTransf in n.flags: return n
+  if nfTransf in n.flags: return n
   pushTransCon(c, newTransCon(owner))
   result = PNode(transform(c, n))
   popTransCon(c)
@@ -981,7 +981,7 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
     liftDefer(c, result)
     #result = liftLambdas(prc, result)
     when useEffectSystem: trackProc(g, prc, result)
-    result = liftLocalsIfRequested(prc, result, g.config)
+    result = liftLocalsIfRequested(prc, result, g.cache, g.config)
     if c.needsDestroyPass: #and newDestructors:
       result = injectDestructorCalls(g, prc, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/types.nim b/compiler/types.nim
index 7f9b8239f..5d3774eba 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -10,7 +10,8 @@
 # this module contains routines for accessing and iterating over types
 
 import
-  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options
+  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
+  lineinfos
 
 type
   TPreferedDesc* = enum
@@ -102,7 +103,7 @@ proc isIntLit*(t: PType): bool {.inline.} =
 proc isFloatLit*(t: PType): bool {.inline.} =
   result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
 
-proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
+proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
   result = sym.owner.name.s & '.' & sym.name.s & '('
   var n = sym.typ.n
   for i in countup(1, sonsLen(n) - 1):
@@ -118,7 +119,7 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
   if n.sons[0].typ != nil:
     result.add(": " & typeToString(n.sons[0].typ, prefer))
   result.add "[declared in "
-  result.add($sym.info)
+  result.add(conf$sym.info)
   result.add "]"
 
 proc elemType*(t: PType): PType =
@@ -589,18 +590,18 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     result = typeToStr[t.kind]
   result.addTypeFlags(t)
 
-proc firstOrd*(t: PType): BiggestInt =
+proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
   case t.kind
   of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
     result = 0
-  of tySet, tyVar: result = firstOrd(t.sons[0])
-  of tyArray: result = firstOrd(t.sons[0])
+  of tySet, tyVar: result = firstOrd(conf, t.sons[0])
+  of tyArray: result = firstOrd(conf, t.sons[0])
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n.sons[0])
   of tyInt:
-    if platform.intSize == 4: result = - (2147483646) - 2
+    if conf != nil and conf.target.intSize == 4: result = - (2147483646) - 2
     else: result = 0x8000000000000000'i64
   of tyInt8: result = - 128
   of tyInt16: result = - 32768
@@ -610,38 +611,38 @@ proc firstOrd*(t: PType): BiggestInt =
   of tyEnum:
     # if basetype <> nil then return firstOrd of basetype
     if sonsLen(t) > 0 and t.sons[0] != nil:
-      result = firstOrd(t.sons[0])
+      result = firstOrd(conf, t.sons[0])
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
-    result = firstOrd(lastSon(t))
+    result = firstOrd(conf, lastSon(t))
   of tyOrdinal:
-    if t.len > 0: result = firstOrd(lastSon(t))
-    else: internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
+    if t.len > 0: result = firstOrd(conf, lastSon(t))
+    else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
   else:
-    internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
+    internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
     result = 0
 
-proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
+proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
   case t.kind
   of tyBool: result = 1
   of tyChar: result = 255
-  of tySet, tyVar: result = lastOrd(t.sons[0])
-  of tyArray: result = lastOrd(t.sons[0])
+  of tySet, tyVar: result = lastOrd(conf, t.sons[0])
+  of tyArray: result = lastOrd(conf, t.sons[0])
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n.sons[1])
   of tyInt:
-    if platform.intSize == 4: result = 0x7FFFFFFF
+    if conf != nil and conf.target.intSize == 4: result = 0x7FFFFFFF
     else: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyInt8: result = 0x0000007F
   of tyInt16: result = 0x00007FFF
   of tyInt32: result = 0x7FFFFFFF
   of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyUInt:
-    if platform.intSize == 4: result = 0xFFFFFFFF
+    if conf != nil and conf.target.intSize == 4: result = 0xFFFFFFFF
     elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64
     else: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyUInt8: result = 0xFF
@@ -654,27 +655,27 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
     assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
     result = t.n.sons[sonsLen(t.n) - 1].sym.position
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
-    result = lastOrd(lastSon(t))
+    result = lastOrd(conf, lastSon(t))
   of tyProxy: result = 0
   of tyOrdinal:
-    if t.len > 0: result = lastOrd(lastSon(t))
-    else: internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
+    if t.len > 0: result = lastOrd(conf, lastSon(t))
+    else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
   else:
-    internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
+    internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
     result = 0
 
-proc lengthOrd*(t: PType): BiggestInt =
+proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
   case t.kind
-  of tyInt64, tyInt32, tyInt: result = lastOrd(t)
-  of tyDistinct: result = lengthOrd(t.sons[0])
+  of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
+  of tyDistinct: result = lengthOrd(conf, t.sons[0])
   else:
-    let last = lastOrd t
-    let first = firstOrd t
+    let last = lastOrd(conf, t)
+    let first = firstOrd(conf, t)
     # XXX use a better overflow check here:
     if last == high(BiggestInt) and first <= 0:
       result = last
     else:
-      result = lastOrd(t) - firstOrd(t) + 1
+      result = lastOrd(conf, t) - firstOrd(conf, t) + 1
 
 # -------------- type equality -----------------------------------------------
 
@@ -1209,81 +1210,24 @@ proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PTyp
 proc align(address, alignment: BiggestInt): BiggestInt =
   result = (address + (alignment - 1)) and not (alignment - 1)
 
-type
-  OptKind* = enum  ## What to map 'opt T' to internally.
-    oBool      ## opt[T] requires an additional 'bool' field
-    oNil       ## opt[T] has no overhead since 'nil'
-               ## is available
-    oEnum      ## We can use some enum value that is not yet
-               ## used for opt[T]
-    oPtr       ## opt[T] actually introduces a hidden pointer
-               ## in order for the type recursion to work
-
-proc optKind*(typ: PType): OptKind =
-  ## return true iff 'opt[T]' can be mapped to 'T' internally
-  ## because we have a 'nil' value available:
-  assert typ.kind == tyOpt
-  case typ.sons[0].skipTypes(abstractInst).kind
-  of tyRef, tyPtr, tyProc:
-    result = oNil
-  of tyArray, tyObject, tyTuple:
-    result = oPtr
-  of tyBool: result = oEnum
-  of tyEnum:
-    assert(typ.n.sons[0].kind == nkSym)
-    if typ.n.sons[0].sym.position != low(int):
-      result = oEnum
-    else:
-      result = oBool
-  else:
-    result = oBool
-
-proc optLowering*(typ: PType): PType =
-  case optKind(typ)
-  of oNil: result = typ.sons[0]
-  of oPtr:
-    result = newType(tyOptAsRef, typ.owner)
-    result.rawAddSon typ.sons[0]
-  of oBool:
-    result = newType(tyTuple, typ.owner)
-    result.rawAddSon newType(tyBool, typ.owner)
-    result.rawAddSon typ.sons[0]
-  of oEnum:
-    if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32):
-      result = newType(tyInt32, typ.owner)
-    else:
-      result = newType(tyInt64, typ.owner)
-
-proc optEnumValue*(typ: PType): BiggestInt =
-  assert typ.kind == tyOpt
-  assert optKind(typ) == oEnum
-  let elem = typ.sons[0].skipTypes(abstractInst).kind
-  if elem == tyBool:
-    result = 2
-  else:
-    assert elem == tyEnum
-    assert typ.n.sons[0].sym.position != low(int)
-    result = typ.n.sons[0].sym.position - 1
-
-
 const
   szNonConcreteType* = -3
   szIllegalRecursion* = -2
   szUnknownSize* = -1
 
-proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt
-proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
+proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
+proc computeRecSizeAux(conf: ConfigRef; n: PNode, a, currOffset: var BiggestInt): BiggestInt =
   var maxAlign, maxSize, b, res: BiggestInt
   case n.kind
   of nkRecCase:
     assert(n.sons[0].kind == nkSym)
-    result = computeRecSizeAux(n.sons[0], a, currOffset)
+    result = computeRecSizeAux(conf, n.sons[0], a, currOffset)
     maxSize = 0
     maxAlign = 1
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
-        res = computeRecSizeAux(lastSon(n.sons[i]), b, currOffset)
+        res = computeRecSizeAux(conf, lastSon(n.sons[i]), b, currOffset)
         if res < 0: return res
         maxSize = max(maxSize, res)
         maxAlign = max(maxAlign, b)
@@ -1296,20 +1240,20 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
     result = 0
     maxAlign = 1
     for i in countup(0, sonsLen(n) - 1):
-      res = computeRecSizeAux(n.sons[i], b, currOffset)
+      res = computeRecSizeAux(conf, n.sons[i], b, currOffset)
       if res < 0: return res
       currOffset = align(currOffset, b) + res
       result = align(result, b) + res
       if b > maxAlign: maxAlign = b
     a = maxAlign
   of nkSym:
-    result = computeSizeAux(n.sym.typ, a)
+    result = computeSizeAux(conf, n.sym.typ, a)
     n.sym.offset = int(currOffset)
   else:
     a = 1
     result = szNonConcreteType
 
-proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
+proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt =
   var res, maxAlign, length, currOffset: BiggestInt
   if typ.size == szIllegalRecursion:
     # we are already computing the size of the type
@@ -1323,7 +1267,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
   typ.size = szIllegalRecursion # mark as being computed
   case typ.kind
   of tyInt, tyUInt:
-    result = intSize
+    result = conf.target.intSize
     a = result
   of tyInt8, tyUInt8, tyBool, tyChar:
     result = 1
@@ -1341,30 +1285,30 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     result = 16
     a = result
   of tyFloat:
-    result = floatSize
+    result = conf.target.floatSize
     a = result
   of tyProc:
-    if typ.callConv == ccClosure: result = 2 * ptrSize
-    else: result = ptrSize
-    a = ptrSize
+    if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
+    else: result = conf.target.ptrSize
+    a = conf.target.ptrSize
   of tyString, tyNil:
-    result = ptrSize
+    result = conf.target.ptrSize
     a = result
   of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
     let base = typ.lastSon
     if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
       result = szIllegalRecursion
-    else: result = ptrSize
+    else: result = conf.target.ptrSize
     a = result
   of tyArray:
-    let elemSize = computeSizeAux(typ.sons[1], a)
+    let elemSize = computeSizeAux(conf, typ.sons[1], a)
     if elemSize < 0: return elemSize
-    result = lengthOrd(typ.sons[0]) * elemSize
+    result = lengthOrd(conf, typ.sons[0]) * elemSize
   of tyEnum:
-    if firstOrd(typ) < 0:
+    if firstOrd(conf, typ) < 0:
       result = 4              # use signed int32
     else:
-      length = lastOrd(typ)   # BUGFIX: use lastOrd!
+      length = lastOrd(conf, typ)   # BUGFIX: use lastOrd!
       if length + 1 < `shl`(1, 8): result = 1
       elif length + 1 < `shl`(1, 16): result = 2
       elif length + 1 < `shl`(BiggestInt(1), 32): result = 4
@@ -1374,7 +1318,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     if typ.sons[0].kind == tyGenericParam:
       result = szUnknownSize
     else:
-      length = lengthOrd(typ.sons[0])
+      length = lengthOrd(conf, typ.sons[0])
       if length <= 8: result = 1
       elif length <= 16: result = 2
       elif length <= 32: result = 4
@@ -1383,12 +1327,12 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
       else: result = align(length, 8) div 8 + 1
     a = result
   of tyRange:
-    result = computeSizeAux(typ.sons[0], a)
+    result = computeSizeAux(conf, typ.sons[0], a)
   of tyTuple:
     result = 0
     maxAlign = 1
     for i in countup(0, sonsLen(typ) - 1):
-      res = computeSizeAux(typ.sons[i], a)
+      res = computeSizeAux(conf, typ.sons[i], a)
       if res < 0: return res
       maxAlign = max(maxAlign, a)
       result = align(result, a) + res
@@ -1396,61 +1340,52 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     a = maxAlign
   of tyObject:
     if typ.sons[0] != nil:
-      result = computeSizeAux(typ.sons[0].skipTypes(skipPtrs), a)
+      result = computeSizeAux(conf, typ.sons[0].skipTypes(skipPtrs), a)
       if result < 0: return
       maxAlign = a
     elif isObjectWithTypeFieldPredicate(typ):
-      result = intSize
+      result = conf.target.intSize
       maxAlign = result
     else:
       result = 0
       maxAlign = 1
     currOffset = result
-    result = computeRecSizeAux(typ.n, a, currOffset)
+    result = computeRecSizeAux(conf, typ.n, a, currOffset)
     if result < 0: return
     if a < maxAlign: a = maxAlign
     result = align(result, a)
   of tyInferred:
     if typ.len > 1:
-      result = computeSizeAux(typ.lastSon, a)
+      result = computeSizeAux(conf, typ.lastSon, a)
   of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
-    result = computeSizeAux(lastSon(typ), a)
+    result = computeSizeAux(conf, lastSon(typ), a)
   of tyTypeClasses:
-    result = if typ.isResolvedUserTypeClass: computeSizeAux(typ.lastSon, a)
+    result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a)
              else: szUnknownSize
   of tyTypeDesc:
-    result = computeSizeAux(typ.base, a)
+    result = computeSizeAux(conf, typ.base, a)
   of tyForward: return szIllegalRecursion
   of tyStatic:
-    result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
+    result = if typ.n != nil: computeSizeAux(conf, typ.lastSon, a)
              else: szUnknownSize
-  of tyOpt:
-    case optKind(typ)
-    of oBool: result = computeSizeAux(lastSon(typ), a) + 1
-    of oEnum:
-      if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): result = 4
-      else: result = 8
-    of oNil: result = computeSizeAux(lastSon(typ), a)
-    of oPtr: result = ptrSize
   else:
     #internalError("computeSizeAux()")
     result = szUnknownSize
   typ.size = result
   typ.align = int16(a)
 
-proc computeSize*(typ: PType): BiggestInt =
+proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
   var a: BiggestInt = 1
-  result = computeSizeAux(typ, a)
+  result = computeSizeAux(conf, typ, a)
 
 proc getReturnType*(s: PSym): PType =
   # Obtains the return type of a iterator/proc/macro/template
   assert s.kind in skProcKinds
   result = s.typ.sons[0]
 
-proc getSize*(typ: PType): BiggestInt =
-  result = computeSize(typ)
-  #if result < 0: internalError("getSize: " & $typ.kind)
-  # XXX review all usages of 'getSize'
+proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
+  result = computeSize(conf, typ)
+  if result < 0: internalError(conf, "getSize: " & $typ.kind)
 
 proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
   case t.kind
diff --git a/compiler/vm.nim b/compiler/vm.nim
index cbd304caa..3e33e8256 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -19,7 +19,7 @@ import ast except getstr
 import
   strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
   parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
-  vmmarshal, gorgeimpl, configuration
+  vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl
 
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
@@ -66,7 +66,7 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
     stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
     var info = c.debug[pc]
     # we now use the same format as in system/except.nim
-    var s = substr(toFilename(info), 0)
+    var s = substr(toFilename(c.config, info), 0)
     # this 'substr' prevents a strange corruption. XXX This needs to be
     # investigated eventually but first attempts to fix it broke everything
     # see the araq-wip-fixed-writebarrier branch.
@@ -213,7 +213,7 @@ proc putIntoNode(n: var PNode; x: TFullReg) =
     if nfIsRef in x.node.flags:
       n = x.node
     else:
-      let destIsRef = nfIsRef in n.flags    
+      let destIsRef = nfIsRef in n.flags
       n[] = x.node[]
       # Ref-ness must be kept for the destination
       if destIsRef:
@@ -370,7 +370,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
         dest.intVal = int(src.floatVal)
       else:
         dest.intVal = src.intVal
-      if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp):
+      if dest.intVal < firstOrd(c.config, desttyp) or dest.intVal > lastOrd(c.config, desttyp):
         return true
     of tyUInt..tyUInt64:
       if dest.kind != rkInt:
@@ -700,12 +700,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ)
       addSon(b, regs[rb].regToNode)
-      var r = diffSets(regs[ra].node, b)
+      var r = diffSets(c.config, regs[ra].node, b)
       discardSons(regs[ra].node)
       for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i])
     of opcCard:
       decodeB(rkInt)
-      regs[ra].intVal = nimsets.cardSet(regs[rb].node)
+      regs[ra].intVal = nimsets.cardSet(c.config, regs[rb].node)
     of opcMulInt:
       decodeBC(rkInt)
       let
@@ -853,35 +853,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal)
     of opcLeSet:
       decodeBC(rkInt)
-      regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node))
+      regs[ra].intVal = ord(containsSets(c.config, regs[rb].node, regs[rc].node))
     of opcEqSet:
       decodeBC(rkInt)
-      regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node))
+      regs[ra].intVal = ord(equalSets(c.config, regs[rb].node, regs[rc].node))
     of opcLtSet:
       decodeBC(rkInt)
       let a = regs[rb].node
       let b = regs[rc].node
-      regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b))
+      regs[ra].intVal = ord(containsSets(c.config, a, b) and not equalSets(c.config, a, b))
     of opcMulSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-            nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
+            nimsets.intersectSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcPlusSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.unionSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.unionSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcMinusSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.diffSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcSymdiffSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcConcatStr:
       decodeBC(rkNode)
       createStr regs[ra]
@@ -972,7 +972,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         when hasFFI:
           let prcValue = c.globals.sons[prc.position-1]
           if prcValue.kind == nkEmpty:
-            globalError(c.config, c.debug[pc], "canot run " & prc.name.s)
+            globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
           let newValue = callForeignFunction(prcValue, prc.typ, tos.slots,
                                              rb+1, rc-1, c.debug[pc])
           if newValue.kind != nkEmpty:
@@ -1291,7 +1291,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
       of 1:
@@ -1305,14 +1305,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getTypeInst opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
       else:
         # getTypeImpl opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
     of opcNStrVal:
@@ -1336,14 +1336,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
                                      c.module, c.config)
     of opcGorge:
-      decodeBC(rkNode)
-      inc pc
-      let rd = c.code[pc].regA
-
-      createStr regs[ra]
-      regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
-                                     regs[rc].node.strVal, regs[rd].node.strVal,
-                                     c.debug[pc], c.config)[0]
+      when defined(nimcore):
+        decodeBC(rkNode)
+        inc pc
+        let rd = c.code[pc].regA
+
+        createStr regs[ra]
+        regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
+                                      regs[rc].node.strVal, regs[rd].node.strVal,
+                                      c.debug[pc], c.config)[0]
+      else:
+        globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support")
     of opcNError:
       decodeB(rkNode)
       let a = regs[ra].node
@@ -1358,7 +1361,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       # c.debug[pc].line.int - countLines(regs[rb].strVal) ?
       var error: string
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            c.debug[pc].toFullPath, c.debug[pc].line.int,
+                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.isNil and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
@@ -1373,7 +1376,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var error: string
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            c.debug[pc].toFullPath, c.debug[pc].line.int,
+                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.isNil and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
@@ -1392,7 +1395,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetFile:
       decodeB(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, n.info.toFilename)
+      regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info))
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
     of opcNGetLine:
@@ -1453,7 +1456,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
       else:
         regs[ra].node = newNodeI(nkIdent, c.debug[pc])
-        regs[ra].node.ident = getIdent(regs[rb].node.strVal)
+        regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
     of opcSetType:
       if regs[ra].kind != rkNode:
         internalError(c.config, c.debug[pc], "cannot set type")
@@ -1566,9 +1569,110 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
         internalError(c.config, c.debug[pc], "request to create symbol of invalid kind")
-      var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc])
+      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
+
+    of opcNccValue:
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey)
+    of opcNccInc:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let by = regs[instr.regB].intVal
+      let v = getOrDefault(g.cacheCounters, destKey)
+      g.cacheCounters[destKey] = v+by
+      recordInc(c, c.debug[pc], destKey, by)
+    of opcNcsAdd:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let val = regs[instr.regB].node
+      if not contains(g.cacheSeqs, destKey):
+        g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        # newNodeI(nkStmtList, c.debug[pc])
+      else:
+        g.cacheSeqs[destKey].add val
+      recordAdd(c, c.debug[pc], destKey, val)
+    of opcNcsIncl:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let val = regs[instr.regB].node
+      if not contains(g.cacheSeqs, destKey):
+        g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+      else:
+        block search:
+          for existing in g.cacheSeqs[destKey]:
+            if exprStructuralEquivalent(existing, val, strictSymEquality=true):
+              break search
+          g.cacheSeqs[destKey].add val
+      recordIncl(c, c.debug[pc], destKey, val)
+    of opcNcsLen:
+      let g = c.graph
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0
+    of opcNcsAt:
+      let g = c.graph
+      decodeBC(rkNode)
+      let idx = regs[rc].intVal
+      let destKey = regs[rb].node.strVal
+      if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len:
+        regs[ra].node = g.cacheSeqs[destKey][idx.int]
+      else:
+        stackTrace(c, tos, pc, errIndexOutOfBounds)
+    of opcNctPut:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let key = regs[instr.regB].node.strVal
+      let val = regs[instr.regC].node
+      if not contains(g.cacheTables, destKey):
+        g.cacheTables[destKey] = initBTree[string, PNode]()
+      if not contains(g.cacheTables[destKey], key):
+        g.cacheTables[destKey].add(key, val)
+        recordPut(c, c.debug[pc], destKey, key, val)
+      else:
+        stackTrace(c, tos, pc, "key already exists: " & key)
+    of opcNctLen:
+      let g = c.graph
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0
+    of opcNctGet:
+      let g = c.graph
+      decodeBC(rkNode)
+      let destKey = regs[rb].node.strVal
+      let key = regs[rc].node.strVal
+      if contains(g.cacheTables, destKey):
+        if contains(g.cacheTables[destKey], key):
+          regs[ra].node = getOrDefault(g.cacheTables[destKey], key)
+        else:
+          stackTrace(c, tos, pc, "key does not exist: " & key)
+      else:
+        stackTrace(c, tos, pc, "key does not exist: " & destKey)
+    of opcNctHasNext:
+      let g = c.graph
+      decodeBC(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if g.cacheTables.contains(destKey):
+          ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int))
+        else:
+          0
+    of opcNctNext:
+      let g = c.graph
+      decodeBC(rkNode)
+      let destKey = regs[rb].node.strVal
+      let index = regs[rc].intVal
+      if contains(g.cacheTables, destKey):
+        let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int)
+        regs[ra].node = newTree(nkTupleConstr, newStrNode(k, c.debug[pc]), v,
+                                newIntNode(nkIntLit, nextIndex))
+      else:
+        stackTrace(c, tos, pc, "key does not exist: " & destKey)
+
     of opcTypeTrait:
       # XXX only supports 'name' for now; we can use regC to encode the
       # type trait operation
@@ -1583,7 +1687,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       inc pc
       let typ = c.types[c.code[pc].regBx - wordExcess]
-      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.config))
+      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config))
     of opcMarshalStore:
       decodeB(rkNode)
       inc pc
@@ -1654,36 +1758,30 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
 
 include vmops
 
-# for now we share the 'globals' environment. XXX Coming soon: An API for
-# storing&loading the 'globals' environment to get what a component system
-# requires.
-var
-  globalCtx*: PCtx
-
-proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) =
-  if globalCtx.isNil:
-    globalCtx = newCtx(module, cache, graph)
-    registerAdditionalOps(globalCtx)
+proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
+  if graph.vm.isNil:
+    graph.vm = newCtx(module, graph.cache, graph)
+    registerAdditionalOps(PCtx graph.vm)
   else:
-    refresh(globalCtx, module)
+    refresh(PCtx graph.vm, module)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   #var c = newEvalContext(module, emRepl)
   #c.features = {allowCast, allowFFI, allowInfiniteLoops}
   #pushStackFrame(c, newStackFrame())
 
   # XXX produce a new 'globals' environment here:
-  setupGlobalCtx(module, cache, graph)
-  result = globalCtx
+  setupGlobalCtx(module, graph)
+  result = PCtx graph.vm
   when hasFFI:
-    globalCtx.features = {allowFFI, allowCast}
+    PCtx(graph.vm).features = {allowFFI, allowCast}
 
 proc myProcess(c: PPassContext, n: PNode): PNode =
   let c = PCtx(c)
   # don't eval errornous code:
   if c.oldErrorCount == c.config.errorCounter:
     evalStmt(c, n)
-    result = emptyNode
+    result = newNodeI(nkEmpty, n.info)
   else:
     result = n
   c.oldErrorCount = c.config.errorCounter
@@ -1691,19 +1789,19 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
 proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
   myProcess(c, n)
 
-const evalPass* = makePass(myOpen, nil, myProcess, myClose)
+const evalPass* = makePass(myOpen, myProcess, myClose)
 
-proc evalConstExprAux(module: PSym; cache: IdentCache;
+proc evalConstExprAux(module: PSym;
                       g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
   let n = transformExpr(g, module, n)
-  setupGlobalCtx(module, cache, g)
-  var c = globalCtx
+  setupGlobalCtx(module, g)
+  var c = PCtx g.vm
   let oldMode = c.mode
   defer: c.mode = oldMode
   c.mode = mode
   let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
-  if c.code[start].opcode == opcEof: return emptyNode
+  if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
   assert c.code[start].opcode != opcEof
   when debugEchoCode: c.echoCode start
   var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
@@ -1712,17 +1810,17 @@ proc evalConstExprAux(module: PSym; cache: IdentCache;
   result = rawExecute(c, start, tos).regToNode
   if result.info.col < 0: result.info = n.info
 
-proc evalConstExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode): PNode =
-  result = evalConstExprAux(module, cache, g, nil, e, emConst)
+proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode =
+  result = evalConstExprAux(module, g, nil, e, emConst)
 
-proc evalStaticExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym): PNode =
-  result = evalConstExprAux(module, cache, g, prc, e, emStaticExpr)
+proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode =
+  result = evalConstExprAux(module, g, prc, e, emStaticExpr)
 
-proc evalStaticStmt*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym) =
-  discard evalConstExprAux(module, cache, g, prc, e, emStaticStmt)
+proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) =
+  discard evalConstExprAux(module, g, prc, e, emStaticStmt)
 
-proc setupCompileTimeVar*(module: PSym; cache: IdentCache, g: ModuleGraph; n: PNode) =
-  discard evalConstExprAux(module, cache, g, nil, n, emStaticStmt)
+proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) =
+  discard evalConstExprAux(module, g, nil, n, emStaticStmt)
 
 proc setupMacroParam(x: PNode, typ: PType): TFullReg =
   case typ.kind
@@ -1748,13 +1846,12 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
 
 # to prevent endless recursion in macro instantiation
 const evalMacroLimit = 1000
-var evalMacroCounter: int
 
-proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
+proc evalMacroCall*(module: PSym; g: ModuleGraph;
                     n, nOrig: PNode, sym: PSym): PNode =
   # XXX globalError() is ugly here, but I don't know a better solution for now
-  inc(evalMacroCounter)
-  if evalMacroCounter > evalMacroLimit:
+  inc(g.config.evalMacroCounter)
+  if g.config.evalMacroCounter > evalMacroLimit:
     globalError(g.config, n.info, "macro instantiation too nested")
 
   # immediate macros can bypass any type and arity checking so we check the
@@ -1763,8 +1860,8 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
         n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
 
-  setupGlobalCtx(module, cache, g)
-  var c = globalCtx
+  setupGlobalCtx(module, g)
+  var c = PCtx g.vm
   c.comesFromHeuristic.line = 0'u16
 
   c.callsite = nOrig
@@ -1795,12 +1892,12 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
       if idx < n.len:
         tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
       else:
-        dec(evalMacroCounter)
+        dec(g.config.evalMacroCounter)
         c.callsite = nil
         localError(c.config, n.info, "expected " & $gp.len &
                    " generic parameter(s)")
     elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
-      dec(evalMacroCounter)
+      dec(g.config.evalMacroCounter)
       c.callsite = nil
       globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros")
   # temporary storage:
@@ -1808,5 +1905,5 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
   result = rawExecute(c, start, tos).regToNode
   if result.info.line < 0: result.info = n.info
   if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree")
-  dec(evalMacroCounter)
+  dec(g.config.evalMacroCounter)
   c.callsite = nil
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index e10a62006..cec61ade5 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -10,7 +10,8 @@
 ## This module contains the type definitions for the new evaluation engine.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-import ast, passes, msgs, idents, intsets, options, modulegraphs
+import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos,
+  tables, btrees
 
 const
   byteExcess* = 128 # we use excess-K for immediates
@@ -91,6 +92,9 @@ type
     opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
     opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
 
+    opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt,
+    opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext,
+
     opcSlurp,
     opcGorge,
     opcParseExprToAst,
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 2c92348a6..865ecd36e 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,11 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, streams, options, idents
+import ast, types, msgs, os, streams, options, idents, lineinfos
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string =
   try:
-    var filename = parentDir(info.toFullPath) / file
+    var filename = parentDir(toFullPath(conf, info)) / file
     if not fileExists(filename):
       filename = findFile(conf, file)
     result = readFile(filename)
@@ -23,8 +23,8 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
     localError(conf, info, "cannot open file: " & file)
     result = ""
 
-proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
-  let sym = newSym(skType, getIdent(name), t.owner, info)
+proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
+  let sym = newSym(skType, getIdent(cache, name), t.owner, info)
   sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
@@ -34,51 +34,51 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
   result = newSymNode(s)
   result.info = info
 
-proc mapTypeToAstX(t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
                    inst=false; allowRecursionX=false): PNode
 
-proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo;
+proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-  result.add atomicTypeX(name, m, t, info)
+  result.add atomicTypeX(cache, name, m, t, info)
   for i in 0 ..< t.len:
     if t.sons[i] == nil:
-      let void = atomicTypeX("void", mVoid, t, info)
+      let void = atomicTypeX(cache, "void", mVoid, t, info)
       void.typ = newType(tyVoid, t.owner)
       result.add void
     else:
-      result.add mapTypeToAstX(t.sons[i], info, inst)
+      result.add mapTypeToAstX(cache, t.sons[i], info, inst)
 
-proc objectNode(n: PNode): PNode =
+proc objectNode(cache: IdentCache; n: PNode): PNode =
   if n.kind == nkSym:
     result = newNodeI(nkIdentDefs, n.info)
     result.add n  # name
-    result.add mapTypeToAstX(n.sym.typ, n.info, true, false)  # type
-    result.add ast.emptyNode  # no assigned value
+    result.add mapTypeToAstX(cache, n.sym.typ, n.info, true, false)  # type
+    result.add newNodeI(nkEmpty, n.info)  # no assigned value
   else:
     result = copyNode(n)
     for i in 0 ..< n.safeLen:
-      result.add objectNode(n[i])
+      result.add objectNode(cache, n[i])
 
-proc mapTypeToAstX(t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
                    inst=false; allowRecursionX=false): PNode =
   var allowRecursion = allowRecursionX
-  template atomicType(name, m): untyped = atomicTypeX(name, m, t, info)
+  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
+  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, inst)
+  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, inst, true)
   template mapTypeToAst(t,i,info): untyped =
-    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
-    else: ast.emptyNode
+    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(cache, t.sons[i], info, inst)
+    else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
-    mapTypeToBracketX(name, m, t, info, inst)
+    mapTypeToBracketX(cache, name, m, t, info, inst)
   template newNodeX(kind): untyped =
     newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
   template newIdentDefs(n,t): untyped =
     var id = newNodeX(nkIdentDefs)
     id.add n  # name
     id.add mapTypeToAst(t, info)  # type
-    id.add ast.emptyNode  # no assigned value
+    id.add newNodeI(nkEmpty, info)  # no assigned value
     id
   template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
 
@@ -103,7 +103,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
     result.add atomicType("array", mArray)
     if inst and t.sons[0].kind == tyRange:
       var rng = newNodeX(nkInfix)
-      rng.add newIdentNode(getIdent(".."), info)
+      rng.add newIdentNode(getIdent(cache, ".."), info)
       rng.add t.sons[0].n.sons[0].copyTree
       rng.add t.sons[0].n.sons[1].copyTree
       result.add rng
@@ -132,14 +132,14 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         for i in 1 ..< t.len-1:
           result.add mapTypeToAst(t.sons[i], info)
     else:
-      result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
+      result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
   of tyGenericBody:
     if inst:
       result = mapTypeToAstR(t.lastSon, info)
     else:
       result = mapTypeToAst(t.lastSon, info)
   of tyAlias:
-    result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
+    result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
   of tyOrdinal:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
@@ -156,22 +156,22 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   of tyObject:
     if inst:
       result = newNodeX(nkObjectTy)
-      result.add ast.emptyNode  # pragmas not reconstructed yet
-      if t.sons[0] == nil: result.add ast.emptyNode  # handle parent object
+      result.add newNodeI(nkEmpty, info)  # pragmas not reconstructed yet
+      if t.sons[0] == nil: result.add newNodeI(nkEmpty, info)  # handle parent object
       else:
         var nn = newNodeX(nkOfInherit)
         nn.add mapTypeToAst(t.sons[0], info)
         result.add nn
       if t.n.len > 0:
-        result.add objectNode(t.n)
+        result.add objectNode(cache, t.n)
       else:
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
     else:
       if allowRecursion or t.sym == nil:
         result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
         if t.sons[0] == nil:
-          result.add ast.emptyNode
+          result.add newNodeI(nkEmpty, info)
         else:
           result.add mapTypeToAst(t.sons[0], info)
         result.add copyTree(t.n)
@@ -179,7 +179,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         result = atomicType(t.sym)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
-    result.add ast.emptyNode  # pragma node, currently always empty for enum
+    result.add newNodeI(nkEmpty, info)  # pragma node, currently always empty for enum
     for c in t.n.sons:
       result.add copyTree(c)
   of tyTuple:
@@ -223,13 +223,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result = newNodeX(nkProcTy)
       var fp = newNodeX(nkFormalParams)
       if t.sons[0] == nil:
-        fp.add ast.emptyNode
+        fp.add newNodeI(nkEmpty, info)
       else:
         fp.add mapTypeToAst(t.sons[0], t.n[0].info)
       for i in 1..<t.sons.len:
         fp.add newIdentDefs(t.n[i], t.sons[i])
       result.add fp
-      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+      result.add newNodeI(nkEmpty, info)  # pragmas aren't reconstructed yet
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
@@ -283,15 +283,15 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         result.add t.n.copyTree
   of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX")
 
-proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, false, true)
+proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, false, true)
 
 # the "Inst" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type declaration
-proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, true, false)
+proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, true, false)
 
 # the "Impl" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type implementation
-proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, true, true)
+proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, true, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 7ac3b5cf7..4c58ea789 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -29,7 +29,7 @@
 
 import
   strutils, ast, astalgo, types, msgs, renderer, vmdef,
-  trees, intsets, rodread, magicsys, options, lowerings
+  trees, intsets, magicsys, options, lowerings, lineinfos
 import platform
 from os import splitFile
 
@@ -40,8 +40,8 @@ type
   TGenFlag = enum gfAddrOf, gfFieldAccess
   TGenFlags = set[TGenFlag]
 
-proc debugInfo(info: TLineInfo): string =
-  result = info.toFilename.splitFile.name & ":" & $info.line
+proc debugInfo(c: PCtx; info: TLineInfo): string =
+  result = toFilename(c.config, info).splitFile.name & ":" & $info.line
 
 proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
   # first iteration: compute all necessary labels:
@@ -85,7 +85,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
     else:
       result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
     result.add("\t#")
-    result.add(debugInfo(c.debug[i]))
+    result.add(debugInfo(c, c.debug[i]))
     result.add("\n")
     inc i
 
@@ -550,7 +550,7 @@ proc genField(c: PCtx; n: PNode): TRegister =
   result = s.position
 
 proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
-  if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr);
+  if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr);
       x != 0):
     let tmp = c.genx(n)
     # freeing the temporary here means we can produce:  regA = regA - Imm
@@ -767,12 +767,12 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
   var unsignedIntegers = {tyUInt8..tyUInt32, tyChar}
   let src = n.sons[1].typ.skipTypes(abstractRange)#.kind
   let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind
-  let src_size = src.getSize
+  let src_size = getSize(c.config, src)
 
-  if platform.intSize < 8:
+  if c.config.target.intSize < 8:
     signedIntegers.incl(tyInt)
     unsignedIntegers.incl(tyUInt)
-  if src_size == dst.getSize and src.kind in allowedIntegers and
+  if src_size == getSize(c.config, dst) and src.kind in allowedIntegers and
                                  dst.kind in allowedIntegers:
     let tmp = c.genx(n.sons[1])
     var tmp2 = c.getTemp(n.sons[1].typ)
@@ -804,6 +804,17 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
   else:
     globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size")
 
+proc genVoidABC(c: PCtx, n: PNode, dest: TRegister, opcode: TOpcode) =
+  unused(c, n, dest)
+  var
+    tmp1 = c.genx(n[1])
+    tmp2 = c.genx(n[2])
+    tmp3 = c.genx(n[3])
+  c.gABC(n, opcode, tmp1, tmp2, tmp3)
+  c.freeTemp(tmp1)
+  c.freeTemp(tmp2)
+  c.freeTemp(tmp3)
+
 proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -965,7 +976,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp)
   of mSwap:
     unused(c, n, dest)
-    c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym))
+    c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mCopyStr:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1067,20 +1078,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
-  of mNSetChild, mNDel:
-    unused(c, n, dest)
-    var
-      tmp1 = c.genx(n.sons[1])
-      tmp2 = c.genx(n.sons[2])
-      tmp3 = c.genx(n.sons[3])
-    c.gABC(n, if m == mNSetChild: opcNSetChild else: opcNDel, tmp1, tmp2, tmp3)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    c.freeTemp(tmp3)
+  of mNSetChild: genVoidABC(c, n, dest, opcNSetChild)
+  of mNDel: genVoidABC(c, n, dest, opcNDel)
   of mNAdd: genBinaryABC(c, n, dest, opcNAdd)
   of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple)
   of mNKind: genUnaryABC(c, n, dest, opcNKind)
   of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind)
+
+  of mNccValue: genUnaryABC(c, n, dest, opcNccValue)
+  of mNccInc: genBinaryABC(c, n, dest, opcNccInc)
+  of mNcsAdd: genBinaryABC(c, n, dest, opcNcsAdd)
+  of mNcsIncl: genBinaryABC(c, n, dest, opcNcsIncl)
+  of mNcsLen: genUnaryABC(c, n, dest, opcNcsLen)
+  of mNcsAt: genBinaryABC(c, n, dest, opcNcsAt)
+  of mNctPut: genVoidABC(c, n, dest, opcNctPut)
+  of mNctLen: genUnaryABC(c, n, dest, opcNctLen)
+  of mNctGet: genBinaryABC(c, n, dest, opcNctGet)
+  of mNctHasNext: genBinaryABC(c, n, dest, opcNctHasNext)
+  of mNctNext: genBinaryABC(c, n, dest, opcNctNext)
+
   of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal)
   of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
   of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
@@ -1543,7 +1559,6 @@ proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
 
 proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange-{tyTypeDesc})
-  result = emptyNode
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
     result = newNodeIT(nkIntLit, info, t)
@@ -1575,7 +1590,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     getNullValueAux(t.n, result, conf)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
-    for i in countup(0, int(lengthOrd(t)) - 1):
+    for i in countup(0, int(lengthOrd(conf, t)) - 1):
       addSon(result, getNullValue(elemType(t), info, conf))
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
@@ -1589,6 +1604,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     result = newNodeIT(nkBracket, info, t)
   else:
     globalError(conf, info, "cannot create null element for: " & $t.kind)
+    result = newNodeI(nkEmpty, info)
 
 proc ldNullOpcode(t: PType): TOpcode =
   assert t != nil
@@ -1795,6 +1811,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       if s.magic != mNone:
         genMagic(c, n, dest, s.magic)
       elif matches(s, "stdlib", "marshal", "to"):
+        # XXX marshal load&store should not be opcodes, but use the
+        # general callback mechanisms.
         genMarshalLoad(c, n, dest)
       elif matches(s, "stdlib", "marshal", "$$"):
         genMarshalStore(c, n, dest)
@@ -2008,7 +2026,7 @@ proc genProc(c: PCtx; s: PSym): int =
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
     if x.kind == nkEmpty:
-      x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode)
+      x = newTree(nkBracket, newIntNode(nkIntLit, result), x)
     else:
       x.sons[0] = newIntNode(nkIntLit, result)
     s.ast.sons[miscPos] = x
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index f38be7c29..eb01b3514 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -10,7 +10,7 @@
 ## Implements marshaling for the VM.
 
 import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
-  options
+  options, lineinfos
 
 proc ptrToInt(x: PNode): int {.inline.} =
   result = cast[int](x) # don't skip alignment
@@ -140,6 +140,7 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
 
 proc loadAny(p: var JsonParser, t: PType,
              tab: var Table[BiggestInt, PNode];
+             cache: IdentCache;
              conf: ConfigRef): PNode =
   case t.kind
   of tyNone: assert false
@@ -174,7 +175,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkBracket)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.elemType, tab, conf)
+      result.add loadAny(p, t.elemType, tab, cache, conf)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tySequence:
@@ -186,7 +187,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       result = newNode(nkBracket)
       while p.kind != jsonArrayEnd and p.kind != jsonEof:
-        result.add loadAny(p, t.elemType, tab, conf)
+        result.add loadAny(p, t.elemType, tab, cache, conf)
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
@@ -202,7 +203,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       if i >= t.len:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
-      result.add loadAny(p, t.sons[i], tab, conf)
+      result.add loadAny(p, t.sons[i], tab, cache, conf)
       inc i
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -214,7 +215,7 @@ proc loadAny(p: var JsonParser, t: PType,
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
-      let ident = getIdent(p.str)
+      let ident = getIdent(cache, p.str)
       let field = lookupInRecord(t.n, ident)
       if field.isNil:
         raiseParseErr(p, "unknown field for object of type " & typeToString(t))
@@ -224,7 +225,7 @@ proc loadAny(p: var JsonParser, t: PType,
         setLen(result.sons, pos + 1)
       let fieldNode = newNode(nkExprColonExpr)
       fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo())))
-      fieldNode.addSon(loadAny(p, field.typ, tab, conf))
+      fieldNode.addSon(loadAny(p, field.typ, tab, cache, conf))
       result.sons[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -233,7 +234,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkCurly)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.lastSon, tab, conf)
+      result.add loadAny(p, t.lastSon, tab, cache, conf)
       next(p)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
@@ -252,7 +253,7 @@ proc loadAny(p: var JsonParser, t: PType,
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab, conf)
+        result = loadAny(p, t.lastSon, tab, cache, conf)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
@@ -280,14 +281,14 @@ proc loadAny(p: var JsonParser, t: PType,
       return
     raiseParseErr(p, "float expected")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    result = loadAny(p, t.lastSon, tab, conf)
+    result = loadAny(p, t.lastSon, tab, cache, conf)
   else:
     internalError conf, "cannot marshal at compile-time " & t.typeToString
 
-proc loadAny*(s: string; t: PType; conf: ConfigRef): PNode =
+proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode =
   var tab = initTable[BiggestInt, PNode]()
   var p: JsonParser
   open(p, newStringStream(s), "unknown file")
   next(p)
-  result = loadAny(p, t, tab, conf)
+  result = loadAny(p, t, tab, cache, conf)
   close(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 617295b0d..a7d47d7a3 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -107,15 +107,16 @@ proc registerAdditionalOps*(c: PCtx) =
   wrap1f_math(ceil)
   wrap2f_math(fmod)
 
-  wrap2s(getEnv, ospathsop)
-  wrap1s(existsEnv, ospathsop)
-  wrap2svoid(putEnv, ospathsop)
-  wrap1s(dirExists, osop)
-  wrap1s(fileExists, osop)
-  wrap2svoid(writeFile, systemop)
-  wrap1s(readFile, systemop)
-  systemop getCurrentExceptionMsg
-  registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
-    setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
-  systemop gorgeEx
+  when defined(nimcore):
+    wrap2s(getEnv, ospathsop)
+    wrap1s(existsEnv, ospathsop)
+    wrap2svoid(putEnv, ospathsop)
+    wrap1s(dirExists, osop)
+    wrap1s(fileExists, osop)
+    wrap2svoid(writeFile, systemop)
+    wrap1s(readFile, systemop)
+    systemop getCurrentExceptionMsg
+    registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
+    systemop gorgeEx
   macrosop getProjectPath
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index c0f7b7b20..1ea1deb2d 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -15,7 +15,8 @@
 ##   * Computing an aliasing relation based on the assignments. This relation
 ##     is then used to compute the 'writes' and 'escapes' effects.
 
-import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options
+import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options,
+  lineinfos
 
 const
   debug = false
diff --git a/doc/intern.txt b/doc/intern.txt
index dadb0eb05..b253cac42 100644
--- a/doc/intern.txt
+++ b/doc/intern.txt
@@ -38,10 +38,6 @@ Path           Purpose
 Bootstrapping the compiler
 ==========================
 
-As of version 0.8.5 the compiler is maintained in Nim. (The first versions
-have been implemented in Object Pascal.) The Python-based build system has
-been rewritten in Nim too.
-
 Compiling the compiler is a simple matter of running::
 
   nim c koch.nim
@@ -202,16 +198,131 @@ Compilation cache
 =================
 
 The implementation of the compilation cache is tricky: There are lots
-of issues to be solved for the front- and backend. In the following
-sections *global* means *shared between modules* or *property of the whole
-program*.
+of issues to be solved for the front- and backend.
+
+
+General approach: AST replay
+----------------------------
+
+We store a module's AST of a successful semantic check in a SQLite
+database. There are plenty of features that require a sub sequence
+to be re-applied, for example:
+
+.. code-block:: nim
+  {.compile: "foo.c".} # even if the module is loaded from the DB,
+                       # "foo.c" needs to be compiled/linked.
+
+The solution is to **re-play** the module's top level statements.
+This solves the problem without having to special case the logic
+that fills the internal seqs which are affected by the pragmas.
+
+In fact, this decribes how the AST should be stored in the database,
+as a "shallow" tree. Let's assume we compile module ``m`` with the
+following contents:
+
+.. code-block:: nim
+  import strutils
+
+  var x*: int = 90
+  {.compile: "foo.c".}
+  proc p = echo "p"
+  proc q = echo "q"
+  static:
+    echo "static"
+
+Conceptually this is the AST we store for the module:
+
+.. code-block:: nim
+  import strutils
+
+  var x*
+  {.compile: "foo.c".}
+  proc p
+  proc q
+  static:
+    echo "static"
+
+The symbol's ``ast`` field is loaded lazily, on demand. This is where most
+savings come from, only the shallow outer AST is reconstructed immediately.
+
+It is also important that the replay involves the ``import`` statement so
+that the dependencies are resolved properly.
+
+
+Shared global compiletime state
+-------------------------------
+
+Nim allows ``.global, compiletime`` variables that can be filled by macro
+invokations across different modules. This feature breaks modularity in a
+severe way. Plenty of different solutions have been proposed:
+
+- Restrict the types of global compiletime variables to ``Set[T]`` or
+  similar unordered, only-growable collections so that we can track
+  the module's write effects to these variables and reapply the changes
+  in a different order.
+- In every module compilation, reset the variable to its default value.
+- Provide a restrictive API that can load/save the compiletime state to
+  a file.
+
+(These solutions are not mutually exclusive.)
+
+Since we adopt the "replay the top level statements" idea, the natural
+solution to this problem is to emit pseudo top level statements that
+reflect the mutations done to the global variable. However, this is
+MUCH harder than it sounds, for example ``squeaknim`` uses this
+snippet:
+
+.. code-block:: nim
+  apicall.add(") module: '" & dllName & "'>\C" &
+              "\t^self externalCallFailed\C!\C\C")
+  stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall)
+
+We can "replay" ``stCode.add`` only if the values of ``st``
+and ``apicall`` are known. And even then a hash table's ``add`` with its
+hashing mechanism is too hard to replay.
+
+In practice, things are worse still, consider ``someGlobal[i][j].add arg``.
+We only know the root is ``someGlobal`` but the concrete path to the data
+is unknown as is the value that is added. We could compute a "diff" between
+the global states and use that to compute a symbol patchset, but this is
+quite some work, expensive to do at runtime (it would need to run after
+every module has been compiled) and also would break for hash tables.
+
+We need an API that hides the complex aliasing problems by not relying
+on Nim's global variables. The obvious solution is to use string keys
+instead of global variables:
+
+.. code-block:: nim
+
+  proc cachePut*(key: string; value: string)
+  proc cacheGet*(key: string): string
+
+However, the values being strings/json is quite problematic: Many
+lookup tables that are built at compiletime embed *proc vars* and
+types which have no obvious string representation... Seems like
+AST diffing is still the best idea as it will not require to use
+an alien API and works with some existing Nimble packages, at least.
+
+On the other hand, in Nim's future I would like to replace the VM
+by native code. A diff algorithm wouldn't work for that.
+Instead the native code would work with an API like ``put``, ``get``:
+
+.. code-block:: nim
+
+  proc cachePut*(key: string; value: NimNode)
+  proc cacheGet*(key: string): NimNode
+
+The API should embrace the AST diffing notion: See the
+module ``macrocache`` for the final details.
 
 
-Frontend issues
----------------
 
 Methods and type converters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+---------------------------
+
+In the following
+sections *global* means *shared between modules* or *property of the whole
+program*.
 
 Nim contains language features that are *global*. The best example for that
 are multi methods: Introducing a new method with the same name and some
@@ -238,20 +349,17 @@ If in the above example module ``B`` is re-compiled, but ``A`` is not then
 ``B`` needs to be aware of ``toBool`` even though  ``toBool`` is not referenced
 in ``B`` *explicitly*.
 
-Both the multi method and the type converter problems are solved by storing
-them in special sections in the ROD file that are loaded *unconditionally*
-when the ROD file is read.
+Both the multi method and the type converter problems are solved by the
+AST replay implementation.
+
 
 Generics
 ~~~~~~~~
 
-If we generate an instance of a generic, we'd like to re-use that
-instance if possible across module boundaries. However, this is not
-possible if the compilation cache is enabled. So we give up then and use
-the caching of generics only per module, not per project. This means that
-``--symbolFiles:on`` hurts a bit for efficiency. A better solution would
-be to persist the instantiations in a global cache per project. This might be
-implemented in later versions.
+We cache generic instantiations and need to ensure this caching works
+well with the incremental compilation feature. Since the cache is
+attached to the ``PSym`` datastructure, it should work without any
+special logic.
 
 
 Backend issues
@@ -259,13 +367,10 @@ Backend issues
 
 - Init procs must not be "forgotten" to be called.
 - Files must not be "forgotten" to be linked.
-- Anything that is contained in ``nim__dat.c`` is shared between modules
-  implicitly.
 - Method dispatchers are global.
 - DLL loading via ``dlsym`` is global.
 - Emulated thread vars are global.
 
-
 However the biggest problem is that dead code elimination breaks modularity!
 To see why, consider this scenario: The module ``G`` (for example the huge
 Gtk2 module...) is compiled with dead code elimination turned on. So none
@@ -274,25 +379,21 @@ of ``G``'s procs is generated at all.
 Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
 ``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
 
-Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B``
+Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B``
 and ``G`` are left unchanged. ``A`` requires ``G.P2``.
 
 So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
 loaded ``P1`` from the symbol file, nor do we want to because we then quickly
-would restore large parts of the whole program. But we also don't want to
-store ``P1`` in ``B.c`` because that would mean to store every symbol where
-it is referred from which ultimately means the main module and putting
-everything in a single C file.
+would restore large parts of the whole program.
 
-There is however another solution: The old file ``G.c`` containing ``P1`` is
-**merged** with the new file ``G.c`` containing ``P2``. This is the solution
-that is implemented in the C code generator (have a look at the ``ccgmerge``
-module). The merging may lead to *cruft* (aka dead code) in generated C code
-which can only be removed by recompiling a project with the compilation cache
-turned off. Nevertheless the merge solution is way superior to the
-cheap solution "turn off dead code elimination if the compilation cache is
-turned on".
+Solution
+~~~~~~~~ 
 
+The backend must have some logic so that if the currently processed module
+is from the compilation cache, the ``ast`` field is not accessed. Instead
+the generated C(++) for the symbol's body needs to be cached too and
+inserted back into the produced C file. This approach seems to deal with
+all the outlined problems above.
 
 
 Debugging Nim's memory management
@@ -317,7 +418,7 @@ Introduction
 
 I use the term *cell* here to refer to everything that is traced
 (sequences, refs, strings).
-This section describes how the new GC works.
+This section describes how the GC works.
 
 The basic algorithm is *Deferrent Reference Counting* with cycle detection.
 References on the stack are not counted for better performance and easier C
diff --git a/examples/allany.nim b/examples/allany.nim
index 6ff055aa6..8a5ab81f0 100644
--- a/examples/allany.nim
+++ b/examples/allany.nim
@@ -1,22 +1,20 @@
 # All and any
 
 template all(container, cond: untyped): bool =
-  block:
-    var result = true
-    for it in items(container):
-      if not cond(it):
-        result = false
-        break
-    result
+  var result = true
+  for it in items(container):
+    if not cond(it):
+      result = false
+      break
+  result
 
 template any(container, cond: untyped): bool =
-  block:
-    var result = false
-    for it in items(container):
-      if cond(it):
-        result = true
-        break
-    result
+  var result = false
+  for it in items(container):
+    if cond(it):
+      result = true
+      break
+  result
 
 if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
   echo "works"
diff --git a/examples/readme.txt b/examples/readme.txt
index 176bc8239..686271660 100644
--- a/examples/readme.txt
+++ b/examples/readme.txt
@@ -1,5 +1,2 @@
 In this directory you will find several examples for how to use the Nim
 library.
-
-Copyright (c) 2004-2012 Andreas Rumpf.
-All rights reserved.
diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim
new file mode 100644
index 000000000..bd48b5bd4
--- /dev/null
+++ b/lib/core/macrocache.nim
@@ -0,0 +1,47 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides an API for macros that need to collect compile
+## time information across module boundaries in global variables.
+## Starting with version 0.19 of Nim this is not directly supported anymore
+## as it breaks incremental compilations.
+## Instead the API here needs to be used. See XXX (wikipedia page) for a
+## theoretical foundation behind this.
+
+type
+  CacheSeq* = distinct string
+  CacheTable* = distinct string
+  CacheCounter* = distinct string
+
+proc value*(c: CacheCounter): int {.magic: "NccValue".}
+proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
+
+proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
+proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
+proc len*(s: CacheSeq): int {.magic: "NcsLen".}
+proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
+
+iterator items*(s: CacheSeq): NimNode =
+  for i in 0 ..< len(s): yield s[i]
+
+proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
+  ## 'key' has to be unique!
+
+proc len*(t: CacheTable): int {.magic: "NctLen".}
+proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
+
+proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
+proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
+
+iterator pairs*(t: CacheTable): (string, NimNode) =
+  var h = 0
+  while hasNext(t, h):
+    let (a, b, h2) = next(t, h)
+    yield (a, b)
+    h = h2
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index fa5cd67df..1f251b73e 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -126,9 +126,6 @@ type
     ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
     ## *ident*.
 
-{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
-    TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
-    TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
 
 const
   nnkLiterals* = {nnkCharLit..nnkNilLit}
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 03a27017a..ef456f093 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -188,13 +188,16 @@ proc addTexChar(dest: var string, c: char) =
   of '`': add(dest, "\\symbol{96}")
   else: add(dest, c)
 
-var splitter*: string = "<wbr />"
-
 proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
   case target
   of outHtml:  addXmlChar(dest, c)
   of outLatex: addTexChar(dest, c)
 
+proc addSplitter(target: OutputTarget; dest: var string) {.inline.} =
+  case target
+  of outHtml: add(dest, "<wbr />")
+  of outLatex: add(dest, "\\-")
+
 proc nextSplitPoint*(s: string, start: int): int =
   result = start
   while result < len(s) + 0:
@@ -215,9 +218,9 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
     var j = 0
     while j < len(s):
       var k = nextSplitPoint(s, j)
-      if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
-        partLen = 0
-        add(result, splitter)
+      #if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
+      partLen = 0
+      addSplitter(target, result)
       for i in countup(j, k): escChar(target, result, s[i])
       inc(partLen, k - j + 1)
       j = k + 1
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 3ff608cfc..9681d97b3 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -910,7 +910,7 @@ proc rawCreateDir(dir: string): bool =
     elif errno == EEXIST:
       result = false
     else:
-      echo res
+      #echo res
       raiseOSError(osLastError())
   else:
     when useWinUnicode:
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 664446d54..e8bca4bdd 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -832,7 +832,7 @@ elif not defined(useNimRtl):
 
     # Parent process. Copy process information.
     if poEchoCmd in options:
-      echo(command, " ", join(args, " "))
+      when declared(echo): echo(command, " ", join(args, " "))
     result.id = pid
     result.exitFlag = false
 
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index 97f97e8ae..b561cd3ba 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Platform detection for Nim. This module is included by the system module!
+## Platform detection for NimScript. This module is included by the system module!
 ## Do not import it directly!
 
 type
@@ -62,7 +62,7 @@ const
               elif defined(haiku): OsPlatform.haiku
               elif defined(android): OsPlatform.android
               elif defined(js): OsPlatform.js
-              elif defined(nimrodVM): OsPlatform.nimVM
+              elif defined(nimVM): OsPlatform.nimVM
               elif defined(standalone): OsPlatform.standalone
               else: OsPlatform.none
     ## the OS this program will run on.
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 5c505ddf7..9f9315080 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -9,6 +9,9 @@
 
 ## Nimsuggest is a tool that helps to give editors IDE like capabilities.
 
+when not defined(nimcore):
+  {.error: "nimcore MUST be defined for Nim's core tooling".}
+
 import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp
 # Do NOT import suggest. It will lead to wierd bugs with
 # suggestionResultHook, because suggest.nim is included by sigmatch.
@@ -17,7 +20,7 @@ import compiler / [options, commands, modules, sem,
   passes, passaux, msgs, nimconf,
   extccomp, condsyms,
   sigmatch, ast, scriptconfig,
-  idents, modulegraphs, vm, prefixmatches]
+  idents, modulegraphs, vm, prefixmatches, lineinfos]
 
 when defined(windows):
   import winlean
@@ -74,8 +77,8 @@ proc writelnToChannel(line: string) =
 proc sugResultHook(s: Suggest) =
   results.send(s)
 
-proc errorHook(info: TLineInfo; msg: string; sev: Severity) =
-  results.send(Suggest(section: ideChk, filePath: toFullPath(info),
+proc errorHook(conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
+  results.send(Suggest(section: ideChk, filePath: toFullPath(conf, info),
     line: toLinenumber(info), column: toColumn(info), doc: msg,
     forth: $sev))
 
@@ -109,7 +112,7 @@ proc sexp(s: Suggest): SexpNode =
   let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath
   result = convertSexp([
     s.section,
-    s.symkind,
+    TSymKind s.symkind,
     qp.map(newSString),
     s.filePath,
     s.forth,
@@ -141,49 +144,48 @@ proc listEpc(): SexpNode =
     methodDesc.add(docstring)
     result.add(methodDesc)
 
-proc findNode(n: PNode): PSym =
+proc findNode(n: PNode; trackPos: TLineInfo): PSym =
   #echo "checking node ", n.info
   if n.kind == nkSym:
-    if isTracked(n.info, n.sym.name.s.len): return n.sym
+    if isTracked(n.info, trackPos, n.sym.name.s.len): return n.sym
   else:
     for i in 0 ..< safeLen(n):
-      let res = n.sons[i].findNode
+      let res = findNode(n[i], trackPos)
       if res != nil: return res
 
-proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym =
-  let m = graph.getModule(gTrackPos.fileIndex)
-  #echo m.isNil, " I knew it ", gTrackPos.fileIndex
+proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
+  let m = graph.getModule(trackPos.fileIndex)
   if m != nil and m.ast != nil:
-    result = m.ast.findNode
+    result = findNode(m.ast, trackPos)
 
 proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
-             graph: ModuleGraph; cache: IdentCache) =
+             graph: ModuleGraph) =
   let conf = graph.config
   myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
         "[" & $line & ":" & $col & "]")
   conf.ideCmd = cmd
   if cmd == ideChk:
-    msgs.structuredErrorHook = errorHook
-    msgs.writelnHook = myLog
+    conf.structuredErrorHook = errorHook
+    conf.writelnHook = myLog
   else:
-    msgs.structuredErrorHook = nil
-    msgs.writelnHook = myLog
-  if cmd == ideUse and suggestVersion != 0:
+    conf.structuredErrorHook = nil
+    conf.writelnHook = myLog
+  if cmd == ideUse and conf.suggestVersion != 0:
     graph.resetAllModules()
   var isKnownFile = true
   let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
 
-  if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(dirtyIdx, nil)
+  if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
+  else: msgs.setDirtyFile(conf, dirtyIdx, nil)
 
-  gTrackPos = newLineInfo(dirtyIdx, line, col)
-  gTrackPosAttached = false
+  conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
+  conf.m.trackPosAttached = false
   conf.errorCounter = 0
-  if suggestVersion == 1:
+  if conf.suggestVersion == 1:
     graph.usageSym = nil
   if not isKnownFile:
-    graph.compileProject(cache)
-  if suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
+    graph.compileProject()
+  if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
       dirtyfile.len == 0:
     discard "no need to recompile anything"
   else:
@@ -191,16 +193,16 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
     graph.markDirty dirtyIdx
     graph.markClientsDirty dirtyIdx
     if conf.ideCmd != ideMod:
-      graph.compileProject(cache, modIdx)
+      graph.compileProject(modIdx)
   if conf.ideCmd in {ideUse, ideDus}:
-    let u = if suggestVersion != 1: graph.symFromInfo(gTrackPos) else: graph.usageSym
+    let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym
     if u != nil:
       listUsages(conf, u)
     else:
-      localError(conf, gTrackPos, "found no symbol at this position " & $gTrackPos)
+      localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))
 
 proc executeEpc(cmd: IdeCmd, args: SexpNode;
-                graph: ModuleGraph; cache: IdentCache) =
+                graph: ModuleGraph) =
   let
     file = args[0].getStr
     line = args[1].getNum
@@ -208,7 +210,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
   var dirtyfile = ""
   if len(args) > 3:
     dirtyfile = args[3].getStr(nil)
-  execute(cmd, file, dirtyfile, int(line), int(column), graph, cache)
+  execute(cmd, file, dirtyfile, int(line), int(column), graph)
 
 proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
                return_symbol = "return") =
@@ -377,7 +379,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
                          "unexpected call: " & epcAPI
       quit errMessage
 
-proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: CachedMsgs) =
+proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   let conf = graph.config
 
   template sentinel() =
@@ -432,20 +434,20 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
     results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig))))
   else:
     if conf.ideCmd == ideChk:
-      for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev)
-    execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache)
+      for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
+    execute(conf.ideCmd, orig, dirtyfile, line, col, graph)
   sentinel()
 
-proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
+proc recompileFullProject(graph: ModuleGraph) =
   #echo "recompiling full project"
   resetSystemArtifacts(graph)
-  vm.globalCtx = nil
+  graph.vm = nil
   graph.resetAllModules()
   GC_fullcollect()
-  compileProject(graph, cache)
+  compileProject(graph)
   #echo GC_getStatistics()
 
-proc mainThread(graph: ModuleGraph; cache: IdentCache) =
+proc mainThread(graph: ModuleGraph) =
   let conf = graph.config
   if gLogging:
     for it in conf.searchPaths:
@@ -457,17 +459,17 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) =
     else:
       writelnToChannel(line)
 
-  msgs.writelnHook = wrHook
-  suggestionResultHook = sugResultHook
+  conf.writelnHook = wrHook
+  conf.suggestionResultHook = sugResultHook
   graph.doStopCompile = proc (): bool = requests.peek() > 0
   var idle = 0
   var cachedMsgs: CachedMsgs = @[]
   while true:
     let (hasData, req) = requests.tryRecv()
     if hasData:
-      msgs.writelnHook = wrHook
-      suggestionResultHook = sugResultHook
-      execCmd(req, graph, cache, cachedMsgs)
+      conf.writelnHook = wrHook
+      conf.suggestionResultHook = sugResultHook
+      execCmd(req, graph, cachedMsgs)
       idle = 0
     else:
       os.sleep 250
@@ -475,21 +477,21 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) =
     if idle == 20 and gRefresh:
       # we use some nimsuggest activity to enable a lazy recompile:
       conf.ideCmd = ideChk
-      msgs.writelnHook = proc (s: string) = discard
+      conf.writelnHook = proc (s: string) = discard
       cachedMsgs.setLen 0
-      msgs.structuredErrorHook = proc (info: TLineInfo; msg: string; sev: Severity) =
+      conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
         cachedMsgs.add(CachedMsg(info: info, msg: msg, sev: sev))
-      suggestionResultHook = proc (s: Suggest) = discard
-      recompileFullProject(graph, cache)
+      conf.suggestionResultHook = proc (s: Suggest) = discard
+      recompileFullProject(graph)
 
 var
   inputThread: Thread[ThreadParams]
 
-proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
+proc mainCommand(graph: ModuleGraph) =
   let conf = graph.config
-  clearPasses()
-  registerPass verbosePass
-  registerPass semPass
+  clearPasses(graph)
+  registerPass graph, verbosePass
+  registerPass graph, semPass
   conf.cmd = cmdIdeTools
   incl conf.globalOptions, optCaasEnabled
   wantMainModule(conf)
@@ -502,12 +504,12 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
   # do not stop after the first error:
   conf.errorMax = high(int)
   # do not print errors, but log them
-  msgs.writelnHook = proc (s: string) = log(s)
-  msgs.structuredErrorHook = nil
+  conf.writelnHook = proc (s: string) = log(s)
+  conf.structuredErrorHook = nil
 
   # compile the project before showing any input so that we already
   # can answer questions right away:
-  compileProject(graph, cache)
+  compileProject(graph)
 
   open(requests)
   open(results)
@@ -520,7 +522,7 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
                             (gPort, "sug \"" & conf.projectFull & "\":" & gAddress))
   of mcmdcon: createThread(inputThread, replCmdline,
                             (gPort, "con \"" & conf.projectFull & "\":" & gAddress))
-  mainThread(graph, cache)
+  mainThread(graph)
   joinThread(inputThread)
   close(requests)
   close(results)
@@ -555,8 +557,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         gMode = mepc
         conf.verbosity = 0          # Port number gotta be first.
       of "debug": incl(conf.globalOptions, optIdeDebug)
-      of "v2": suggestVersion = 0
-      of "v1": suggestVersion = 1
+      of "v2": conf.suggestVersion = 0
+      of "v1": conf.suggestVersion = 1
       of "tester":
         gMode = mstdin
         gEmitEof = true
@@ -568,7 +570,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         else:
           gRefresh = true
       of "maxresults":
-        suggestMaxResults = parseInt(p.val)
+        conf.suggestMaxResults = parseInt(p.val)
       else: processSwitch(pass, p, conf)
     of cmdArgument:
       let a = unixToNativePath(p.key)
@@ -589,7 +591,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   else:
     processCmdLine(passCmd1, "", conf)
     if gMode != mstdin:
-      msgs.writelnHook = proc (msg: string) = discard
+      conf.writelnHook = proc (msg: string) = discard
     if conf.projectName != "":
       try:
         conf.projectFull = canonicalizePath(conf, conf.projectName)
@@ -628,8 +630,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     extccomp.initVars(conf)
     processCmdLine(passCmd2, "", conf)
 
-    let graph = newModuleGraph(conf)
+    let graph = newModuleGraph(cache, conf)
     graph.suggestMode = true
-    mainCommand(graph, cache)
+    mainCommand(graph)
 
 handleCmdline(newIdentCache(), newConfigRef())
diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg
index 38e74b3c7..820db0dba 100644
--- a/nimsuggest/nimsuggest.nim.cfg
+++ b/nimsuggest/nimsuggest.nim.cfg
@@ -8,6 +8,8 @@ path:"$lib/packages/docutils"
 
 define:useStdoutAsStdmsg
 define:nimsuggest
+define:nimcore
+
 # die when nimsuggest uses more than 4GB:
 @if cpu32:
   define:"nimMaxHeap=2000"
@@ -15,7 +17,6 @@ define:nimsuggest
   define:"nimMaxHeap=4000"
 @end
 
-#cs:partial
 #define:useNodeIds
 #define:booting
 #define:noDocgen
diff --git a/tests/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim
new file mode 100644
index 000000000..73becd93d
--- /dev/null
+++ b/tests/compilerapi/exposed.nim
@@ -0,0 +1,3 @@
+
+proc addFloats*(x, y, z: float): float =
+  discard "implementation overriden by tcompilerapi.nim"
diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim
new file mode 100644
index 000000000..539b07de1
--- /dev/null
+++ b/tests/compilerapi/myscript.nim
@@ -0,0 +1,9 @@
+
+import exposed
+
+echo "top level statements are executed!"
+
+proc hostProgramRunsThis*(a, b: float): float =
+  result = addFloats(a, b, 1.0)
+
+let hostProgramWantsThis* = "my secret"
diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim
new file mode 100644
index 000000000..90d343264
--- /dev/null
+++ b/tests/compilerapi/tcompilerapi.nim
@@ -0,0 +1,47 @@
+discard """
+  output: '''top level statements are executed!
+2.0
+my secret
+'''
+"""
+
+## Example program that demonstrates how to use the
+## compiler as an API to embed into your own projects.
+
+import "../../compiler" / [ast, vmdef, vm, nimeval]
+import std / [os]
+
+proc main() =
+  let std = findNimStdLib()
+  if std.len == 0:
+    quit "cannot find Nim's standard library"
+
+  var intr = createInterpreter("myscript.nim", [std, getAppDir()])
+  intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) =
+    setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2))
+  )
+
+  intr.evalScript()
+
+  let foreignProc = selectRoutine(intr, "hostProgramRunsThis")
+  if foreignProc == nil:
+    quit "script does not export a proc of the name: 'hostProgramRunsThis'"
+  let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9),
+                                           newFloatNode(nkFloatLit, 0.1)])
+  if res.kind == nkFloatLit:
+    echo res.floatVal
+  else:
+    echo "bug!"
+
+  let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis")
+  if foreignValue == nil:
+    quit "script does not export a global of the name: hostProgramWantsThis"
+  let val = intr.getGlobalValue(foreignValue)
+  if val.kind in {nkStrLit..nkTripleStrLit}:
+    echo val.strVal
+  else:
+    echo "bug!"
+
+  destroyInterpreter(intr)
+
+main()
\ No newline at end of file
diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim
index 396f17b0b..386eddfde 100644
--- a/tools/nimpretty.nim
+++ b/tools/nimpretty.nim
@@ -12,7 +12,7 @@
 when not defined(nimpretty):
   {.error: "This needs to be compiled with --define:nimPretty".}
 
-import ../compiler / [idents, msgs, ast, syntaxes, renderer]
+import ../compiler / [idents, msgs, ast, syntaxes, renderer, options]
 
 import parseopt, strutils, os
 
@@ -40,8 +40,9 @@ proc writeVersion() =
   quit(0)
 
 proc prettyPrint(infile: string) =
-  let fileIdx = fileInfoIdx(infile)
-  let tree = parseFile(fileIdx, newIdentCache())
+  let conf = newConfigRef()
+  let fileIdx = fileInfoIdx(conf, infile)
+  let tree = parseFile(fileIdx, newIdentCache(), conf)
   let outfile = changeFileExt(infile, ".pretty.nim")
   renderModule(tree, infile, outfile, {}, fileIdx)