summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-07-24 21:39:39 +0200
committerAraq <rumpf_a@web.de>2013-07-24 21:39:39 +0200
commitb30e237a3746b4c4c421a87277a6458642c9f13a (patch)
treeaa695bb65169ed47c0e1f30e46a83b38b98c7e94
parent84534ce4b8d2717f672e870921cab46dc2397c28 (diff)
downloadNim-b30e237a3746b4c4c421a87277a6458642c9f13a.tar.gz
first steps to a new evaluation engine
-rw-r--r--compiler/nimeval.nim33
-rw-r--r--compiler/vm.nim584
-rw-r--r--compiler/vmdef.nim149
-rw-r--r--compiler/vmgen.nim1002
4 files changed, 1768 insertions, 0 deletions
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
new file mode 100644
index 000000000..a239d4ef2
--- /dev/null
+++ b/compiler/nimeval.nim
@@ -0,0 +1,33 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## exposes the Nimrod VM to clients.
+
+import
+  ast, modules, passes, passaux, condsyms, 
+  options, nimconf, lists, sem, semdata, llstream, vm
+
+proc execute*(program: string) =
+  passes.gIncludeFile = includeModule
+  passes.gImportModule = importModule
+  initDefines()
+  LoadConfigs(DefaultConfig)
+
+  initDefines()
+  DefineSymbol("nimrodvm")
+  when hasFFI: DefineSymbol("nimffi")
+  registerPass(verbosePass)
+  registerPass(semPass)
+  registerPass(vmPass)
+
+  appendStr(searchPaths, options.libpath)
+  compileSystemModule()
+  var m = makeStdinModule()
+  incl(m.flags, sfMainModule)
+  processModule(m, LLStreamOpen(program), nil)
diff --git a/compiler/vm.nim b/compiler/vm.nim
new file mode 100644
index 000000000..72e0cb5b1
--- /dev/null
+++ b/compiler/vm.nim
@@ -0,0 +1,584 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This file implements the new evaluation engine for Nimrod code.
+## An instruction is 1-2 int32s in memory, it is a register based VM.
+
+import ast, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned
+
+type
+  PStackFrame* = ref TStackFrame
+  TStackFrame* = object
+    prc: PSym                 # current prc; proc that is evaluated
+    slots: TNodeSeq           # parameters passed to the proc + locals;
+                              # parameters come first
+    next: PStackFrame         # for stacking
+    comesFrom: int
+    safePoints: seq[int]      # used for exception handling
+                              # XXX 'break' should perform cleanup actions
+                              # What does the C backend do for it?
+
+proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) =
+  if x != nil:
+    stackTraceAux(c, x.next, x.comesFrom)
+    var info = c.debug[pc]
+    # we now use the same format as in system/except.nim
+    var s = toFilename(info)
+    var line = toLineNumber(info)
+    if line > 0:
+      add(s, '(')
+      add(s, $line)
+      add(s, ')')
+    if x.prc != nil:
+      for k in 1..max(1, 25-s.len): add(s, ' ')
+      add(s, x.prc.name.s)
+    MsgWriteln(s)
+
+proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
+                msg: TMsgKind, arg = "") =
+  MsgWriteln("stack trace: (most recent call last)")
+  stackTraceAux(c, tos, pc)
+  LocalError(c.debug[pc], msg, arg)
+
+proc bailOut(c: PCtx; tos: PStackFrame) =
+  stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
+             c.currentExceptionA.sons[2].strVal)
+
+when not defined(nimHasInterpreterLoop):
+  {.pragma: interpreterLoop.}
+
+template inc(pc: ptr TInstr, diff = 1) =
+  inc cast[TAddress](pc), TInstr.sizeof * diff
+
+template ensureKind(k: expr) {.immediate, dirty.} =
+  if regs[ra].kind != k:
+    myreset(regs[ra])
+    regs[ra].kind = k
+
+template decodeB(k: expr) {.immediate, dirty.} =
+  let rb = instr.regB
+  ensureKind(k)
+
+template decodeBC(k: expr) {.immediate, dirty.} =
+  let rb = instr.regB
+  let rc = instr.regC
+  ensureKind(k)
+
+template decodeBImm(k: expr) {.immediate, dirty.} =
+  let rb = instr.regB
+  let imm = instr.regC - byteExcess
+  ensureKind(k)
+
+template decodeBx(k: expr) {.immediate, dirty.} =
+  let rbx = instr.regBx - wordExcess
+  ensureKind(k)
+
+proc compile(c: PCtx, s: PSym): int = vmgen.genProc(c, s)
+
+proc myreset(n: PNode) =
+  when defined(system.reset): 
+    var oldInfo = n.info
+    reset(n[])
+    n.info = oldInfo
+
+template move(a, b: expr) = system.shallowCopy(a, b)
+# XXX fix minor 'shallowCopy' overloading bug in compiler
+
+proc asgnRef(x, y: PNode) =
+  myreset(x)
+  x.kind = y.kind
+  x.typ = y.typ
+  case x.kind
+  of nkCharLit..nkInt64Lit: x.intVal = y.intVal
+  of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
+  of nkStrLit..nkTripleStrLit: x.strVal = y.strVal
+  of nkIdent: x.ident = y.ident
+  of nkSym: x.sym = y.sym
+  else:
+    if x.kind notin {nkEmpty..nkNilLit}:
+      move(x.sons, y.sons)
+
+proc asgnComplex(x, y: PNode) =
+  myreset(x)
+  x.kind = y.kind
+  x.typ = y.typ
+  case x.kind
+  of nkCharLit..nkInt64Lit: x.intVal = y.intVal
+  of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
+  of nkStrLit..nkTripleStrLit: x.strVal = y.strVal
+  of nkIdent: x.ident = y.ident
+  of nkSym: x.sym = y.sym
+  else:
+    if x.kind notin {nkEmpty..nkNilLit}:
+      let y = y.copyTree
+      for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i])
+
+template getstr(a: expr): expr =
+  (if a.kind == nkStrLit: a.strVal else: $chr(int(a.intVal)))
+
+proc pushSafePoint(f: PStackFrame; pc: int) =
+  if f.safePoints.isNil: f.safePoints = @[]
+  f.safePoints.add(pc)
+
+proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
+
+proc nextSafePoint(f: PStackFrame): int =
+  var f = f
+  while f.safePoints.isNil or f.safePoints.len == 0:
+    f = f.next
+    if f.isNil: return -1
+  result = f.safePoints.pop
+
+proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int =
+  let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
+  while true:
+    var pc2 = tos.nextSafePoint
+    if pc2 == -1: return -1
+
+    var nextExceptOrFinally = -1
+    if c.code[pc2].opcode == opcExcept:
+      nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
+      inc pc2
+    while c.code[pc2].opcode == opcExcept:
+      let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes(
+                          abstractPtrs)
+      if inheritanceDiff(exceptType, raisedType) <= 0:
+        # mark exception as handled but keep it in B for 
+        # the getCurrentException() builtin:
+        c.currentExceptionB = c.currentExceptionA
+        c.currentExceptionA = nil
+        # execute the corresponding handler:
+        return pc2
+      inc pc2
+    if nextExceptOrFinally >= 0:
+      pc2 = nextExceptOrFinally
+    if c.code[pc2].opcode == opcFinally:
+      # execute the corresponding handler, but don't quit walking the stack:
+      return pc2
+
+proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
+  if f.safePoints.isNil: return -1
+  for s in f.safePoints:
+    var pc = s
+    while c.code[pc].opcode == opcExcept:
+      pc = pc + c.code[pc].regBx - wordExcess
+    if c.code[pc].opcode == opcFinally:
+      return pc
+  return -1
+
+proc execute(c: PCtx, start: int) =
+  var pc = start
+  var regs: TNodeSeq # alias to tos.slots for performance
+  var tos: PStackFrame
+  newSeq(regs, c.prc.maxSlots)
+  while true:
+    {.interpreterLoop.}
+    let instr = c.code[pc]
+    let ra = instr.regA
+    echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
+    case instr.opcode
+    of opcEof: break
+    of opcRet:
+      # XXX perform any cleanup actions
+      tos = tos.next
+      if tos.isNil: return
+      let retVal = regs[0]
+      move(regs, tos.slots)
+      pc = tos.comesFrom
+      assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
+      if c.code[pc].opcode == opcIndCallAsgn:
+        regs[c.code[pc].regA] = retVal
+    of opcYldYoid: assert false
+    of opcYldVal: assert false
+    of opcAsgnInt:
+      echo ra, " ", instr.regB, " ", regs.len, tos.prc.name.s
+      decodeB(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal
+    of opcAsgnStr:
+      decodeB(nkStrLit)
+      debug regs[rb]
+      echo rb
+      Message(c.debug[pc], warnUser, " here")
+      regs[ra].strVal = regs[rb].strVal
+    of opcAsgnFloat:
+      decodeB(nkFloatLit)
+      regs[ra].floatVal = regs[rb].floatVal
+    of opcAsgnComplex:
+      asgnComplex(regs[ra], regs[instr.regB])
+    of opcAsgnRef:
+      asgnRef(regs[ra], regs[instr.regB])
+    of opcWrGlobalRef:
+      asgnRef(c.globals[instr.regBx-wordExcess-1], regs[ra])
+    of opcWrGlobal:
+      asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
+    of opcLdArr:
+      # a = b[c]
+      let rb = instr.regB
+      let rc = instr.regC
+      let idx = regs[rc].intVal
+      # XXX what if the array is not 0-based? -> codegen should insert a sub
+      regs[ra] = regs[rb].sons[idx.int]
+    of opcWrArr:
+      # a[b] = c
+      let rb = instr.regB
+      let rc = instr.regC
+      let idx = regs[rb].intVal
+      asgnComplex(regs[ra].sons[idx.int], regs[rc])
+    of opcWrArrRef:
+      let rb = instr.regB
+      let rc = instr.regC
+      let idx = regs[rb].intVal
+      asgnRef(regs[ra].sons[idx.int], regs[rc])
+    of opcLdObj:
+      # a = b.c
+      let rb = instr.regB
+      let rc = instr.regC
+      # XXX this creates a wrong alias
+      asgnComplex(regs[ra], regs[rb].sons[rc])
+    of opcWrObj:
+      # a.b = c
+      let rb = instr.regB
+      let rc = instr.regC
+      asgnComplex(regs[ra].sons[rb], regs[rc])
+    of opcWrObjRef:
+      let rb = instr.regB
+      let rc = instr.regC
+      asgnRef(regs[ra].sons[rb], regs[rc])
+    of opcWrStrIdx:
+      decodeBC(nkStrLit)
+      let idx = regs[rb].intVal.int
+      regs[ra].strVal[idx] = chr(regs[rc].intVal)
+    of opcAddr:
+      decodeB(nkRefTy)
+      if regs[ra].len == 0: regs[ra].add regs[rb]
+      else: regs[ra].sons[0] = regs[rb]
+    of opcDeref:
+      # a = b[]
+      let rb = instr.regB
+      if regs[rb].kind == nkNilLit:
+        stackTrace(c, tos, pc, errNilAccess)
+      assert regs[rb].kind == nkRefTy
+      regs[ra] = regs[rb].sons[0]
+    of opcAddInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal + regs[rc].intVal
+    of opcAddImmInt:
+      decodeBImm(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal + imm
+    of opcSubInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal - regs[rc].intVal
+    of opcSubImmInt:
+      decodeBImm(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal - imm
+    of opcLenSeq:
+      decodeBImm(nkIntLit)
+      assert regs[rb].kind == nkBracket
+      regs[ra].intVal = regs[rb].len - imm
+    of opcLenStr:
+      decodeBImm(nkIntLit)
+      assert regs[rb].kind == nkStrLit
+      regs[ra].intVal = regs[rb].strVal.len - imm
+    of opcIncl:
+      decodeB(nkCurly)
+      if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb]))
+    of opcExcl:
+      decodeB(nkCurly)
+      # XXX arg we need types here :-(
+      var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ)
+      addSon(b, regs[rb])
+      var r = diffSets(regs[ra], b)
+      discardSons(regs[ra])
+      for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i])
+    of opcCard:
+      decodeB(nkIntLit)
+      regs[ra].intVal = nimsets.cardSet(regs[rb])
+    of opcMulInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal * regs[rc].intVal
+    of opcDivInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal div regs[rc].intVal
+    of opcModInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal
+    of opcAddFloat:
+      decodeBC(nkFloatLit)
+      regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal
+    of opcSubFloat:
+      decodeBC(nkFloatLit)
+      regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal
+    of opcMulFloat:
+      decodeBC(nkFloatLit)
+      regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal
+    of opcDivFloat:
+      decodeBC(nkFloatLit)
+      regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal
+    of opcShrInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal
+    of opcShlInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
+    of opcBitandInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
+    of opcBitorInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal or regs[rc].intVal
+    of opcBitxorInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal
+    of opcAddu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal
+    of opcSubu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal
+    of opcMulu: 
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal
+    of opcDivu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal
+    of opcModu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal
+    of opcEqInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal)
+    of opcLeInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal)
+    of opcLtInt:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal)
+    of opcEqFloat:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal)
+    of opcLeFloat:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal)
+    of opcLtFloat:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal)
+    of opcLeu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal)
+    of opcLtu:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
+    of opcEqRef:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb] == regs[rc])
+      # XXX is this correct? nope ...
+    of opcXor:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
+    of opcNot:
+      decodeB(nkIntLit)
+      assert regs[rb].kind == nkIntLit
+      regs[ra].intVal = 1 - regs[rb].intVal
+    of opcUnaryMinusInt:
+      decodeB(nkIntLit)
+      assert regs[rb].kind == nkIntLit
+      regs[ra].intVal = -regs[rb].intVal
+    of opcUnaryMinusFloat:
+      decodeB(nkFloatLit)
+      assert regs[rb].kind == nkFloatLit
+      regs[ra].floatVal = -regs[rb].floatVal
+    of opcBitnotInt:
+      decodeB(nkIntLit)
+      assert regs[rb].kind == nkIntLit
+      regs[ra].intVal = not regs[rb].intVal
+    of opcEqStr:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(regs[rb].strVal == regs[rc].strVal)
+    of opcLeStr:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(regs[rb].strVal <= regs[rc].strVal)
+    of opcLtStr:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(regs[rb].strVal < regs[rc].strVal)
+    of opcLeSet:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(containsSets(regs[rb], regs[rc]))
+    of opcEqSet: 
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(equalSets(regs[rb], regs[rc]))
+    of opcLtSet:
+      decodeBC(nkIntLit)
+      let a = regs[rb]
+      let b = regs[rc]
+      regs[ra].intVal = Ord(containsSets(a, b) and not equalSets(a, b))
+    of opcMulSet:
+      decodeBC(nkCurly)
+      move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons)
+    of opcPlusSet: 
+      decodeBC(nkCurly)
+      move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons)
+    of opcMinusSet:
+      decodeBC(nkCurly)
+      move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons)
+    of opcSymDiffSet:
+      decodeBC(nkCurly)
+      move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons)    
+    of opcConcatStr:
+      decodeBC(nkStrLit)
+      regs[ra].strVal = getstr(regs[rb])
+      for i in rb+1..rb+rc-1:
+        regs[ra].strVal.add getstr(regs[i])
+    of opcEcho:
+      echo regs[ra].strVal
+    of opcContainsSet:
+      decodeBC(nkIntLit)
+      regs[ra].intVal = Ord(inSet(regs[rb], regs[rc]))
+    of opcSubStr:
+      decodeBC(nkStrLit)
+      inc pc
+      assert c.code[pc].opcode == opcSubStr
+      let rd = c.code[pc].regA
+      regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int, 
+                               regs[rd].intVal.int)
+    of opcIndCall, opcIndCallAsgn:
+      # dest = call regStart, n; where regStart = fn, arg1, ...
+      let rb = instr.regB
+      let rc = instr.regC
+      let prc = regs[rb].sym
+      let newPc = compile(c, prc)
+      var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
+      newSeq(newFrame.slots, prc.position)
+      if not isEmptyType(prc.typ.sons[0]):
+        newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
+      # pass every parameter by var (the language definition allows this):
+      for i in 1 .. rc-1:
+        newFrame.slots[i] = regs[rb+i]
+      # allocate the temporaries:
+      for i in rc .. <prc.position:
+        newFrame.slots[i] = newNode(nkEmpty)
+      tos = newFrame
+      move(regs, newFrame.slots)
+      pc = newPc
+    of opcTJmp:
+      # jump Bx if A != 0
+      let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
+      if regs[ra].intVal != 0:
+        inc pc, rbx
+    of opcFJmp:
+      # jump Bx if A == 0
+      let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
+      if regs[ra].intVal == 0:
+        inc pc, rbx
+    of opcJmp:
+      # jump Bx
+      let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
+      inc pc, rbx
+    of opcBranch:
+      # we know the next instruction is a 'jmp':
+      let branch = c.constants[instr.regBx-wordExcess]
+      var cond = false
+      for j in countup(0, sonsLen(branch) - 2): 
+        if overlap(regs[ra], branch.sons[j]): 
+          cond = true
+          break
+      assert c.code[pc+1].opcode == opcJmp
+      inc pc 
+      # we skip this instruction so that the final 'inc(pc)' skips
+      # the following jump
+      if cond:
+        let instr2 = c.code[pc]
+        let rbx = instr2.regBx - wordExcess - 1 # -1 for the following 'inc pc'
+        inc pc, rbx
+    of opcTry:
+      let rbx = instr.regBx - wordExcess
+      tos.pushSafePoint(pc + rbx)
+    of opcExcept:
+      # just skip it; it's followed by a jump;
+      # we'll execute in the 'raise' handler
+      discard
+    of opcFinally:
+      # just skip it; it's followed by the code we need to execute anyway
+      tos.popSafePoint()
+    of opcFinallyEnd:
+      if c.currentExceptionA != nil:
+        # we are in a cleanup run:
+        pc = cleanupOnException(c, tos, regs)-1
+        if pc < 0: 
+          bailOut(c, tos)
+          return
+    of opcRaise:
+      let raised = regs[ra]
+      c.currentExceptionA = raised
+      c.exceptionInstr = pc
+      # -1 because of the following 'inc'
+      pc = cleanupOnException(c, tos, regs) - 1
+      if pc < 0:
+        bailOut(c, tos)
+        return
+    of opcNew:
+      let typ = c.types[instr.regBx - wordExcess]
+      regs[ra] = getNullValue(typ, regs[ra].info)
+    of opcNewSeq:
+      let typ = c.types[instr.regBx - wordExcess]
+      inc pc
+      ensureKind(nkBracket)
+      let instr2 = c.code[pc]
+      let rb = instr2.regA
+      regs[ra].typ = typ
+      newSeq(regs[ra].sons, rb)
+      for i in 0 .. <rb:
+        regs[ra].sons[i] = getNullValue(typ, regs[ra].info)
+    of opcNewStr:
+      decodeB(nkStrLit)
+      regs[ra].strVal = newString(regs[rb].intVal.int)
+    of opcLdImmInt:
+      # dest = immediate value
+      decodeBx(nkIntLit)
+      regs[ra].intVal = rbx
+    of opcLdNull:
+      let typ = c.types[instr.regBx - wordExcess]
+      regs[ra] = getNullValue(typ, c.debug[pc])
+    of opcLdConst:
+      regs[ra] = c.constants.sons[instr.regBx - wordExcess]
+    of opcNBindSym:
+      # trivial implementation:
+      let rb = instr.regB
+      regs[ra] = regs[rb].sons[1]
+    else:
+      InternalError(c.debug[pc], "unknown opcode " & $instr.opcode)
+    inc pc
+
+proc eval*(c: PCtx, n: PNode): PNode =
+  ## eval never returns nil! This simplifies the code a lot and
+  ## makes it faster too.
+  let start = genStmt(c, n)
+  # execute new instructions; this redundant opcEof check saves us lots
+  # of allocations in 'execute':
+  if c.code[start].opcode != opcEof:
+    execute(c, start)
+  result = emptyNode
+
+proc myOpen(module: PSym): PPassContext =
+  #var c = newEvalContext(module, emRepl)
+  #c.features = {allowCast, allowFFI, allowInfiniteLoops}
+  #pushStackFrame(c, newStackFrame())
+  result = newCtx()
+
+var oldErrorCount: int
+
+proc myProcess(c: PPassContext, n: PNode): PNode =
+  # don't eval errornous code:
+  if oldErrorCount == msgs.gErrorCounter:
+    result = eval(PCtx(c), n)
+  else:
+    result = n
+  oldErrorCount = msgs.gErrorCounter
+
+const vmPass* = makePass(myOpen, nil, myProcess, myProcess)
+
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
new file mode 100644
index 000000000..cbad16516
--- /dev/null
+++ b/compiler/vmdef.nim
@@ -0,0 +1,149 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains the type definitions for the new evaluation engine.
+## An instruction is 1-2 int32s in memory, it is a register based VM.
+
+import ast, passes, msgs, intsets
+
+const
+  byteExcess* = 128 # we use excess-K for immediates
+  wordExcess* = 32768
+
+type
+  TRegister* = range[0..255]
+  TDest* = range[-1 .. 255]
+  TInstr* = distinct uint32
+
+  TInstrFormat = enum
+    ifABC,  # three registers
+    ifABx,  # A + extended B
+
+  TOpcode* = enum
+    opcEof,         # end of code
+    opcRet,         # return
+    opcYldYoid,     # yield with no value
+    opcYldVal,      # yield with a value
+    
+    opcAsgnInt,
+    opcAsgnStr,
+    opcAsgnFloat,
+    opcAsgnRef,
+    opcAsgnComplex,
+
+    opcLdArr,  # a = b[c]
+    opcWrArr,  # a[b] = c
+    opcWrArrRef,
+    opcLdObj,  # a = b.c
+    opcWrObj,  # a.b = c
+    opcWrObjRef,
+    opcAddr,
+    opcDeref,
+    opcWrStrIdx,
+    
+    opcAddInt, 
+    opcAddImmInt,
+    opcSubInt,
+    opcSubImmInt,
+    opcLenSeq,
+    opcLenStr,
+
+    opcIncl, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
+    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
+    opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, 
+    opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, 
+    opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcXor, 
+    opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, 
+    opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
+    opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
+    opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
+    opcSwap, opcIsNil, opcOf,
+    opcSubStr, opcConv, opcCast, opcQuit, opcReset,
+    
+    opcEcho,
+    opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
+    opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
+
+    opcRaise,
+    opcNBindSym, # opcodes for the AST manipulation following
+  
+    opcTJmp,  # jump Bx if A != 0
+    opcFJmp,  # jump Bx if A == 0
+    opcJmp,   # jump Bx
+    opcBranch,  # branch for 'case'
+    opcTry,
+    opcExcept,
+    opcFinally,
+    opcFinallyEnd,
+    opcNew,
+    opcNewSeq,
+    opcNewStr,
+    opcLdNull,    # dest = nullvalue(types[Bx])
+    opcLdConst,   # dest = constants[Bx]
+    opcLdGlobal,  # dest = globals[Bx]
+    opcLdImmInt,  # dest = immediate value
+    opcWrGlobal,
+    opcWrGlobalRef
+
+  TBlock* = object
+    label*: PSym
+    fixups*: seq[TPosition]
+
+  TSlotKind* = enum   # We try to re-use slots in a smart way to
+                      # minimize allocations; however the VM supports arbitrary
+                      # temporary slot usage. This is required for the parameter
+                      # passing implementation.
+    slotEmpty,        # slot is unused
+    slotFixed,        # slot is used for a fixed var/param/result
+    slotTempUnknown,  # slot but type unknown (argument of proc call)
+    slotTempInt,      # some temporary int
+    slotTempFloat,    # some temporary float
+    slotTempStr,      # some temporary string
+    slotTempComplex   # some complex temporary (n.sons field is used)
+
+  PProc* = ref object
+    blocks*: seq[TBlock]    # blocks; temp data structure
+    slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
+    maxSlots*: int
+    
+  PCtx* = ref TCtx
+  TCtx* = object of passes.TPassContext # code gen context
+    code*: seq[TInstr]
+    debug*: seq[TLineInfo]  # line info for every instruction; kept separate
+                            # to not slow down interpretation
+    jumpTargets*: TIntSet   # we need to mark instructions that are
+                            # jump targets;
+                            # we must not optimize over a jump target and we
+                            # need to generate a label for a jump target when
+                            # producing a VM listing
+    globals*: PNode         # 
+    constants*: PNode       # constant data
+    types*: seq[PType]      # some instructions reference types (e.g. 'except')
+    currentExceptionA*, currentExceptionB*: PNode
+    exceptionInstr*: int # index of instruction that raised the exception
+    prc*: PProc
+
+  TPosition* = distinct int
+  
+proc newCtx*(): PCtx =
+  PCtx(code: @[], debug: @[], jumpTargets: initIntSet(),
+    globals: newNode(nkStmtList), constants: newNode(nkStmtList), types: @[],
+    prc: PProc(blocks: @[]))
+
+const
+  firstABxInstr* = opcTJmp
+  largeInstrs* = { # instructions which use 2 int32s instead of 1:
+    opcSubstr, opcConv, opcCast, opcNewSeq, opcOf}
+  slotSomeTemp* = slotTempUnknown
+
+template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32)
+template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
+template regB*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
+template regC*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 24'u32)
+template regBx*(x: TInstr): int {.immediate.} = (x.uint32 shr 16'u32).int
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
new file mode 100644
index 000000000..048a89b8c
--- /dev/null
+++ b/compiler/vmgen.nim
@@ -0,0 +1,1002 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the code generator for the VM.
+
+import
+  unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, 
+  trees, intsets, rodread
+
+proc codeListing(c: PCtx, result: var string) =
+  # for debugging purposes
+  var i = 0
+  while i < c.code.len:
+    if i in c.jumpTargets: result.addf("L$1:\n", i)
+    let x = c.code[i]
+
+    let opc = opcode(x)
+    if opc < firstABxInstr:
+      result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, 
+                  x.regB, x.regC)
+    else:
+      result.addf("\t$#\tr$#, r$#", ($opc).substr(3), x.regA, x.regBx)
+    result.add(" #")
+    result.add(toFileLine(c.debug[i]))
+    result.add("\n")
+    inc i
+
+proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
+  assert opc.ord < 255
+  let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
+                           (b.uint32 shl 16'u32) or
+                           (c.uint32 shl 24'u32)).TInstr
+  ctx.code.add(ins)
+  ctx.debug.add(n.info)
+
+proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: biggestInt) =
+  let ins = (opc.ord or a shl 8 or b shl 16 or (imm+byteExcess) shl 24).TInstr
+  c.code.add(ins)
+  c.debug.add(n.info)
+
+proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
+  let ins = (opc.ord or a shl 8 or (bx+wordExcess) shl 16).TInstr
+  c.code.add(ins)
+  c.debug.add(n.info)
+
+proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
+  #assert opc in {opcJmp, opcFJmp, opcTJmp}
+  gABx(c, n, opc, a, 0)
+  result = TPosition(c.code.len)
+
+proc genLabel(c: PCtx): TPosition =
+  result = TPosition(c.code.len)
+  c.jumpTargets.incl(c.code.len)
+
+proc jmp(c: PCtx, n: PNode, opc: TOpcode, p = TPosition(0)) =
+  let dist = p.int - c.code.len
+  InternalAssert(-0x7fff < dist and dist < 0x7fff)
+  gABx(c, n, opc, 0, dist)
+
+proc patch(c: PCtx, p: TPosition) =
+  # patch with current index
+  let p = p.int
+  let diff = c.code.len - p
+  c.jumpTargets.incl(c.code.len)
+  InternalAssert(-0x7fff < diff and diff < 0x7fff)
+  let oldInstr = c.code[p]
+  # opcode and regA stay the same:
+  c.code[p] = ((oldInstr.int and 0xffff) or (diff+wordExcess)).TInstr
+
+proc getSlotKind(t: PType): TSlotKind =
+  case t.skipTypes(abstractRange).kind
+  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
+    slotTempInt
+  of tyString, tyCString:
+    slotTempStr
+  of tyFloat..tyFloat128:
+    slotTempFloat
+  else:
+    slotTempComplex
+
+const
+  HighRegisterPressure = 40
+
+proc getTemp(c: PCtx; typ: PType): TRegister =
+  let c = c.prc
+  # we prefer the same slot kind here for efficiency:
+  let k = typ.getSlotKind
+  for i in 0 .. c.maxSlots-1:
+    if c.slots[i].kind == k and not c.slots[i].inUse:
+      c.slots[i].inUse = true
+      return TRegister(i)
+      
+  # if register pressure is high, we re-use more aggressively:
+  if c.maxSlots >= HighRegisterPressure:
+    for i in 0 .. c.maxSlots-1:
+      if not c.slots[i].inUse:
+        c.slots[i] = (inUse: true, kind: k)
+        return TRegister(i)
+  result = TRegister(c.maxSlots)
+  c.slots[c.maxSlots] = (inUse: true, kind: k)
+  inc c.maxSlots
+
+proc freeTemp(c: PCtx; r: TRegister) =
+  let c = c.prc
+  if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false
+
+proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister =
+  # if register pressure is high, we re-use more aggressively:
+  let c = c.prc
+  if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister):
+    for i in 0 .. c.maxSlots-1:
+      block search:
+        if not c.slots[i].inUse:
+          for j in i+1 .. i+n-1:
+            if c.slots[j].inUse: break search
+        result = TRegister(i)
+        for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
+        return
+  if c.maxSlots+n >= high(TRegister):
+    InternalError("cannot generate code; too many registers required")
+  result = TRegister(c.maxSlots)
+  inc c.maxSlots, n
+  for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
+  
+proc freeTempRange(c: PCtx; start: TRegister, n: int) =
+  for i in start .. start+n-1: c.freeTemp(TRegister(i))
+
+template withTemp(tmp, typ: expr, body: stmt) {.immediate, dirty.} =
+  var tmp = getTemp(c, typ)
+  body
+  c.freeTemp(tmp)
+
+proc popBlock(c: PCtx; oldLen: int) =  
+  for f in c.prc.blocks[oldLen].fixups:
+    c.patch(f)
+  c.prc.blocks.setLen(oldLen)
+
+template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} =
+  var oldLen {.gensym.} = c.prc.blocks.len
+  c.prc.blocks.add TBlock(label: labl, fixups: @[])
+  body
+  popBlock(c, oldLen)
+
+proc gen(c: PCtx; n: PNode; dest: var TDest)
+proc gen(c: PCtx; n: PNode; dest: TRegister) =
+  var d: TDest = dest
+  gen(c, n, d)
+  InternalAssert d == dest
+
+proc gen(c: PCtx; n: PNode) =
+  var tmp: TDest = -1
+  gen(c, n, tmp)
+  InternalAssert tmp < 0
+
+proc genx(c: PCtx; n: PNode): TRegister =
+  var tmp: TDest = -1
+  gen(c, n, tmp)
+  result = TRegister(tmp)
+
+proc genWhile(c: PCtx; n: PNode) =
+  # L1:
+  #   cond, tmp
+  #   fjmp tmp, L2
+  #   body
+  #   jmp L1
+  # L2:
+  let L1 = c.genLabel
+  withBlock(nil):
+    var tmp = c.genx(n.sons[0])
+    let L2 = c.xjmp(n, opcFJmp, tmp)
+    c.freeTemp(tmp)
+    c.gen(n.sons[1])
+    c.jmp(n, opcJmp, L1)
+    c.patch(L2)
+
+proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
+  withBlock(n.sons[0].sym):
+    c.gen(n.sons[1], dest)
+
+proc genBreak(c: PCtx; n: PNode) =
+  let L1 = c.xjmp(n, opcJmp)
+  if n.sons[0].kind == nkSym:
+    for i in countdown(c.prc.blocks.len-1, 0):
+      if c.prc.blocks[i].label == n.sons[0].sym:
+        c.prc.blocks[i].fixups.add L1
+        break
+    InternalError(n.info, "cannot find 'break' target")
+  else:
+    c.prc.blocks[c.prc.blocks.high].fixups.add L1
+
+proc genIf(c: PCtx, n: PNode; dest: var TDest) =
+  #  if (!expr1) goto L1;
+  #    thenPart
+  #    goto LEnd
+  #  L1:
+  #  if (!expr2) goto L2;
+  #    thenPart2
+  #    goto LEnd
+  #  L2:
+  #    elsePart
+  #  Lend:
+  if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
+  var endings: seq[TPosition] = @[]
+  for i in countup(0, len(n) - 1):
+    var it = n.sons[i]
+    if it.len == 2:
+      withTemp(tmp, it.sons[0].typ):
+        c.gen(it.sons[0], tmp)
+        let elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false
+      c.gen(n.sons[1], dest) # then part
+      if i < sonsLen(n)-1:
+        endings.add(c.xjmp(it.sons[1], opcJmp, 0))
+      c.patch(elsePos)
+    else:
+      c.gen(it.sons[0], dest)
+  for endPos in endings: c.patch(endPos)
+
+proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
+  #   asgn dest, a
+  #   tjmp|fjmp L1
+  #   asgn dest, b
+  # L1:
+  if dest < 0: dest = getTemp(c, n.typ)
+  c.gen(n.sons[0], dest)
+  let L1 = c.xjmp(n, opc)
+  c.gen(n.sons[1], dest)
+  c.patch(L1)
+
+proc rawGenLiteral(c: PCtx; n: PNode): int =
+  result = c.constants.len
+  c.constants.add n
+  InternalAssert result < 0x7fff
+
+proc sameConstant*(a, b: PNode): bool =
+  result = false
+  if a == b:
+    result = true
+  elif a != nil and b != nil and a.kind == b.kind:
+    case a.kind
+    of nkSym: result = a.sym == b.sym
+    of nkIdent: result = a.ident.id == b.ident.id
+    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
+    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
+    of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
+    of nkEmpty, nkNilLit, nkType: result = true
+    else: 
+      if sonsLen(a) == sonsLen(b): 
+        for i in countup(0, sonsLen(a) - 1): 
+          if not sameConstant(a.sons[i], b.sons[i]): return 
+        result = true
+
+proc genLiteral(c: PCtx; n: PNode): int =
+  # types do not matter here:
+  for i in 0 .. <c.constants.len:
+    if sameConstant(c.constants[i], n): return i
+  result = rawGenLiteral(c, n)
+
+proc genCase(c: PCtx; n: PNode; dest: var TDest) =
+  #  if (!expr1) goto L1;
+  #    thenPart
+  #    goto LEnd
+  #  L1:
+  #  if (!expr2) goto L2;
+  #    thenPart2
+  #    goto LEnd
+  #  L2:
+  #    elsePart
+  #  Lend:
+  if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
+  var endings: seq[TPosition] = @[]
+  withTemp(tmp, n.sons[0].typ):
+    c.gen(n.sons[0], tmp)
+    # branch tmp, codeIdx
+    # fjmp   elseLabel
+    for i in 1 .. <n.len:
+      let it = n.sons[i]
+      if it.len == 1:
+        # else stmt:
+        c.gen(it.sons[0], dest)
+      else:
+        let b = rawGenLiteral(c, it)
+        c.gABx(it, opcBranch, tmp, b)
+        let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp)
+        c.gen(it.lastSon, dest)
+        if i < sonsLen(n)-1:
+          endings.add(c.xjmp(it.lastSon, opcJmp, 0))
+        c.patch(elsePos)
+  for endPos in endings: c.patch(endPos)
+
+proc genType(c: PCtx; typ: PType): int =
+  for i, t in c.types:
+    if sameType(t, typ): return i
+  result = c.types.len
+  c.types.add(typ)
+  internalAssert(result <= 0x7fff)
+
+proc genTry(c: PCtx; n: PNode; dest: var TDest) =
+  if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
+  var endings: seq[TPosition] = @[]
+  let elsePos = c.xjmp(n, opcTry, 0)
+  c.gen(n.sons[0], dest)
+  c.patch(elsePos)
+  for i in 1 .. <n.len:
+    let it = n.sons[i]
+    if it.kind != nkFinally:
+      var blen = len(it)
+      # first opcExcept contains the end label of the 'except' block:
+      let endExcept = c.xjmp(it, opcExcept, 0)
+      for j in countup(0, blen - 2): 
+        assert(it.sons[j].kind == nkType)
+        let typ = it.sons[j].typ.skipTypes(abstractPtrs)
+        c.gABx(it, opcExcept, 0, c.genType(typ))
+      if blen == 1: 
+        # general except section:
+        c.gABx(it, opcExcept, 0, 0)
+      c.gen(it.lastSon, dest)
+      if i < sonsLen(n)-1:
+        endings.add(c.xjmp(it, opcJmp, 0))
+      c.patch(endExcept)
+  for endPos in endings: c.patch(endPos)
+  let fin = lastSon(n)
+  # we always generate an 'opcFinally' as that pops the safepoint
+  # from the stack
+  c.gABx(fin, opcFinally, 0, 0)
+  if fin.kind == nkFinally:
+    c.gen(fin.sons[0], dest)
+  c.gABx(fin, opcFinallyEnd, 0, 0)
+
+proc genRaise(c: PCtx; n: PNode) =
+  let dest = genx(c, n.sons[0])
+  c.gABC(n, opcRaise, dest)
+  c.freeTemp(dest)
+
+proc genReturn(c: PCtx; n: PNode) =
+  if n.sons[0].kind != nkEmpty:
+    gen(c, n.sons[0])
+  c.gABC(n, opcRet)
+
+proc genCall(c: PCtx; n: PNode; dest: var TDest) =
+  if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
+  let x = c.getTempRange(n.len, slotTempUnknown)
+  for i in 0.. <n.len: 
+    var r: TRegister = x+i
+    c.gen(n.sons[i], r)
+  if dest < 0:
+    c.gABC(n, opcIndCall, 0, x, n.len)
+  else:
+    c.gABC(n, opcIndCallAsgn, dest, x, n.len)
+  c.freeTempRange(x, n.len)
+
+proc genNew(c: PCtx; n: PNode) =
+  let dest = c.genx(n.sons[1])
+  c.gABx(n, opcNew, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar)))
+  c.freeTemp(dest)
+
+proc genNewSeq(c: PCtx; n: PNode) =
+  let dest = c.genx(n.sons[1])
+  c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar)))
+  let tmp = c.genx(n.sons[2])
+  c.gABx(n, opcNewSeq, tmp, 0)
+  c.freeTemp(dest)
+  c.freeTemp(tmp)
+
+proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  let tmp = c.genx(n.sons[1])
+  if dest < 0: dest = c.getTemp(n.typ)
+  c.gABC(n, opc, dest, tmp)
+  c.freeTemp(tmp)
+
+proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  let
+    tmp = c.genx(n.sons[1])
+    tmp2 = c.genx(n.sons[2])
+  if dest < 0: dest = c.getTemp(n.typ)
+  c.gABC(n, opc, dest, tmp, tmp2)
+  c.freeTemp(tmp)
+  c.freeTemp(tmp2)
+
+proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  if dest < 0: dest = getTemp(c, n.typ)
+  var x = c.getTempRange(n.len-1, slotTempStr)
+  for i in 1..n.len-1: 
+    var r: TRegister = x+i-1
+    c.gen(n.sons[i], r)
+  c.gABC(n, opc, dest, x, n.len-1)
+  c.freeTempRange(x, n.len)
+
+proc isInt8Lit(n: PNode): bool =
+  if n.kind in {nkCharLit..nkUInt64Lit}:
+    result = n.intVal >= low(int8) and n.intVal <= high(int8)
+
+proc isInt16Lit(n: PNode): bool =
+  if n.kind in {nkCharLit..nkUInt64Lit}:
+    result = n.intVal >= low(int16) and n.intVal <= high(int16)
+
+proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  if n.sons[2].isInt8Lit:
+    let tmp = c.genx(n.sons[1])
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABI(n, succ(opc), dest, tmp, n.sons[2].intVal)
+    c.freeTemp(tmp)
+  else:
+    genBinaryABC(c, n, dest, opc)
+
+proc unused(n: PNode; x: TDest) {.inline.} =
+  if x >= 0: InternalError(n.info, "not unused")
+
+proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
+  let tmp = c.genx(arg)
+  let t = genType(c, n.typ)
+  if dest < 0: dest = c.getTemp(n.typ)
+  c.gABC(n, opc, dest, tmp)
+  c.gABx(n, opc, 0, t)
+  c.freeTemp(tmp)
+
+proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
+  let m = n.sons[0].sym.magic
+  case m
+  of mAnd: c.genAndOr(n, opcFJmp, dest)
+  of mOr:  c.genAndOr(n, opcTJmp, dest)
+  of mUnaryLt:
+    let tmp = c.genx(n.sons[1])
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABI(n, opcSubImmInt, dest, tmp, 1)
+    c.freeTemp(tmp)
+  of mPred, mSubI, mSubI64:
+    c.genAddSubInt(n, dest, opcSubInt)
+  of mSucc, mAddI, mAddI64:
+    c.genAddSubInt(n, dest, opcAddInt)
+  of mInc, mDec:
+    unused(n, dest)
+    var d = c.genx(n.sons[1]).TDest
+    c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt)
+    c.freeTemp(d.TRegister)
+  of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
+  of mNew, mNewFinalize:
+    unused(n, dest)
+    c.genNew(n)
+  of mNewSeq:
+    unused(n, dest)
+    c.genNewSeq(n)
+  of mNewString:
+    genUnaryABC(c, n, dest, opcNewStr)
+  of mNewStringOfCap:
+    # we ignore the 'cap' argument and translate it as 'newString(0)'.
+    # eval n.sons[1] for possible side effects:
+    var tmp = c.genx(n.sons[1])
+    c.gABx(n, opcLdImmInt, tmp, 0)
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABC(n, opcNewStr, dest, tmp)
+    c.freeTemp(tmp)
+  of mLengthOpenArray, mLengthArray, mLengthSeq:
+    genUnaryABC(c, n, dest, opcLenSeq)
+  of mLengthStr:
+    genUnaryABC(c, n, dest, opcLenStr)
+  of mIncl, mExcl:
+    unused(n, dest)
+    var d = c.genx(n.sons[1])
+    var tmp = c.genx(n.sons[2])
+    c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp)
+    c.freeTemp(d)
+    c.freeTemp(tmp)
+  of mCard: genUnaryABC(c, n, dest, opcCard)
+  of mMulI, mMulI64: genBinaryABC(c, n, dest, opcMulInt)
+  of mDivI, mDivI64: genBinaryABC(c, n, dest, opcDivInt)
+  of mModI, mModI64: genBinaryABC(c, n, dest, opcModInt)
+  of mAddF64: genBinaryABC(c, n, dest, opcAddFloat)
+  of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
+  of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
+  of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
+  of mShrI, mShrI64: genBinaryABC(c, n, dest, opcShrInt)
+  of mShlI, mShlI64: genBinaryABC(c, n, dest, opcShlInt)
+  of mBitandI, mBitandI64: genBinaryABC(c, n, dest, opcBitandInt)
+  of mBitorI, mBitorI64: genBinaryABC(c, n, dest, opcBitorInt)
+  of mBitxorI, mBitxorI64: genBinaryABC(c, n, dest, opcBitxorInt)
+  of mAddU: genBinaryABC(c, n, dest, opcAddu)
+  of mSubU: genBinaryABC(c, n, dest, opcSubu)
+  of mMulU: genBinaryABC(c, n, dest, opcMulu)
+  of mDivU: genBinaryABC(c, n, dest, opcDivu)
+  of mModU: genBinaryABC(c, n, dest, opcModu)
+  of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
+    genBinaryABC(c, n, dest, opcEqInt)
+  of mLeI, mLeI64, mLeEnum, mLeCh, mLeB:
+    genBinaryABC(c, n, dest, opcLeInt)
+  of mLtI, mLtI64, mLtEnum, mLtCh, mLtB:
+    genBinaryABC(c, n, dest, opcLtInt)
+  of mEqF64: genBinaryABC(c, n, dest, opcEqFloat)
+  of mLeF64: genBinaryABC(c, n, dest, opcLeFloat)
+  of mLtF64: genBinaryABC(c, n, dest, opcLtFloat)
+  of mLePtr, mLeU, mLeU64: genBinaryABC(c, n, dest, opcLeu)
+  of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu)
+  of mEqProc, mEqRef, mEqUntracedRef: genBinaryABC(c, n, dest, opcEqRef)
+  of mXor: genBinaryABC(c, n, dest, opcXor)
+  of mNot: genUnaryABC(c, n, dest, opcNot)
+  of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt)
+  of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
+  of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
+  of mBitnotI, mBitnotI64: genUnaryABC(c, n, dest, opcBitnotInt)
+  of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
+     mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, 
+     mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, 
+     mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
+    genConv(c, n, n.sons[1], dest)
+  of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
+  of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
+  of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
+  of mEqSet: genBinaryABC(c, n, dest, opcEqSet)
+  of mLeSet: genBinaryABC(c, n, dest, opcLeSet)
+  of mLtSet: genBinaryABC(c, n, dest, opcLtSet)
+  of mMulSet: genBinaryABC(c, n, dest, opcMulSet)
+  of mPlusSet: genBinaryABC(c, n, dest, opcPlusSet)
+  of mMinusSet: genBinaryABC(c, n, dest, opcMinusSet)
+  of mSymDiffSet: genBinaryABC(c, n, dest, opcSymdiffSet)
+  of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr)
+  of mInSet: genBinaryABC(c, n, dest, opcContainsSet)
+  of mRepr: genUnaryABC(c, n, dest, opcRepr)
+  of mExit:
+    unused(n, dest)
+    var tmp = c.genx(n.sons[1])
+    c.gABC(n, opcQuit, tmp)
+    c.freeTemp(tmp)
+  of mSetLengthStr, mSetLengthSeq:
+    unused(n, dest)
+    var d = c.genx(n.sons[1])
+    var tmp = c.genx(n.sons[2])
+    c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp)
+    c.freeTemp(tmp)
+  of mSwap: 
+    unused(n, dest)
+    var d = c.genx(n.sons[1])
+    var tmp = c.genx(n.sons[2])
+    c.gABC(n, opcSwap, d, tmp)
+    c.freeTemp(tmp)
+  of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
+  of mCopyStr:
+    if dest < 0: dest = c.getTemp(n.typ)
+    var
+      tmp1 = c.genx(n.sons[1])
+      tmp2 = c.genx(n.sons[2])
+      tmp3 = c.getTemp(n.sons[2].typ)
+    c.gABC(n, opcLenStr, tmp3, tmp1)
+    c.gABC(n, opcSubstr, dest, tmp1, tmp2)
+    c.gABC(n, opcSubstr, tmp3)
+    c.freeTemp(tmp1)
+    c.freeTemp(tmp2)
+    c.freeTemp(tmp3)
+  of mCopyStrLast:
+    if dest < 0: dest = c.getTemp(n.typ)
+    var
+      tmp1 = c.genx(n.sons[1])
+      tmp2 = c.genx(n.sons[2])
+      tmp3 = c.genx(n.sons[3])
+    c.gABC(n, opcSubstr, dest, tmp1, tmp2)
+    c.gABC(n, opcSubstr, tmp3)
+    c.freeTemp(tmp1)
+    c.freeTemp(tmp2)
+    c.freeTemp(tmp3)
+  of mReset:
+    unused(n, dest)
+    var d = c.genx(n.sons[1])
+    c.gABC(n, opcReset, d)
+  of mOf: 
+    if dest < 0: dest = c.getTemp(n.typ)
+    var tmp = c.genx(n.sons[1])
+    c.gABC(n, opcOf, dest, tmp)
+    c.gABx(n, opcOf, 0, c.genType(n.sons[2].typ.skipTypes(abstractPtrs)))
+    c.freeTemp(tmp)
+  of mSizeOf:
+    GlobalError(n.info, errCannotInterpretNodeX, renderTree(n))
+  of mHigh:
+    if dest < 0: dest = c.getTemp(n.typ)
+    let tmp = c.genx(n.sons[1])
+    if n.sons[1].typ.skipTypes(abstractVar).kind == tyString:
+      c.gABI(n, opcLenStr, dest, tmp, 1)
+    else:
+      c.gABI(n, opcLenSeq, dest, tmp, 1)
+    c.freeTemp(tmp)
+  of mEcho:
+    unused(n, dest)
+    for i in 1.. <n.len:
+      var d = c.genx(n.sons[i])
+      c.gABC(n, opcEcho, d)
+      c.freeTemp(d)
+  of mAppendStrCh: InternalError(n.info, "cannot generate code for: " & $m)
+  of mAppendStrStr: InternalError(n.info, "cannot generate code for: " & $m)
+  of mAppendSeqElem: InternalError(n.info, "cannot generate code for: " & $m)
+  of mParseExprToAst: InternalError(n.info, "cannot generate code for: " & $m)
+  of mParseStmtToAst: InternalError(n.info, "cannot generate code for: " & $m)
+  of mExpandToAst: InternalError(n.info, "cannot generate code for: " & $m)
+  of mTypeTrait: InternalError(n.info, "cannot generate code for: " & $m)
+  of mIs: InternalError(n.info, "cannot generate code for: " & $m)
+  of mSlurp: InternalError(n.info, "cannot generate code for: " & $m)
+  of mStaticExec: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNLen: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNChild: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetChild: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNAdd: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNAddMultiple: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNDel: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNKind: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNIntVal: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNFloatVal: InternalError(n.info, "cannot generate code for: " & $m) 
+  of mNSymbol: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNIdent: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNGetType: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNStrVal: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetIntVal: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetFloatVal: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetSymbol: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetIdent: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetType: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNSetStrVal: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNNewNimNode: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNCopyNimNode: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNCopyNimTree: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNBindSym: InternalError(n.info, "cannot generate code for: " & $m)
+  of mStrToIdent: InternalError(n.info, "cannot generate code for: " & $m)
+  of mIdentToStr: InternalError(n.info, "cannot generate code for: " & $m)
+  of mEqIdent: InternalError(n.info, "cannot generate code for: " & $m)
+  of mEqNimrodNode: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNLineInfo: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNHint: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNWarning: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNError: InternalError(n.info, "cannot generate code for: " & $m)
+  of mNCallSite: InternalError(n.info, "cannot generate code for: " & $m)  
+  else:
+    # XXX get rid of these: mMinI, mMaxI, mMinI64, mMaxI64, mMinF64, mMaxF64
+    # mGCref, mGCunref, mEqCString, mAbsI, mAbsI64, mAbsF64
+    InternalError(n.info, "cannot generate code for: " & $m)
+
+const
+  atomicTypes = {tyBool, tyChar,
+    tyExpr, tyStmt, tyTypeDesc,
+    tyEnum,
+    tyOrdinal,
+    tyRange,
+    tyProc,
+    tyPointer, tyOpenArray,
+    tyString, tyCString,
+    tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
+    tyFloat, tyFloat32, tyFloat64, tyFloat128,
+    tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
+
+proc requiresCopy(n: PNode): bool =
+  if n.typ.skipTypes(abstractInst).kind in atomicTypes:
+    result = false
+  elif n.kind in ({nkCurly, nkBracket, nkPar, nkObjConstr}+nkCallKinds):
+    result = false
+  else:
+    result = true
+
+proc unnecessaryIndirection(n: PNode): bool =
+  n.typ.skipTypes(abstractInst).kind == tyRef
+
+proc skipDeref(n: PNode): PNode =
+  if n.kind in {nkDerefExpr, nkHiddenDeref} and unnecessaryIndirection(n):
+    result = n.sons[0]
+  else:
+    result = n
+
+proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = 
+  # a nop for certain types
+  if unnecessaryIndirection(n):
+    gen(c, n.sons[0], dest)
+  else:
+    let tmp = c.genx(n.sons[0])
+    if dest < 0: dest = c.getTemp(n.typ)
+    gABC(c, n, opc, dest, tmp)
+    c.freeTemp(tmp)
+
+proc whichAsgnOpc(n: PNode): TOpcode =
+  case n.typ.skipTypes(abstractRange).kind
+  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
+    opcAsgnInt
+  of tyString, tyCString:
+    opcAsgnStr
+  of tyFloat..tyFloat128:
+    opcAsgnFloat
+  of tyRef, tyNil:
+    opcAsgnRef
+  else:
+    opcAsgnComplex
+
+proc isRef(t: PType): bool = t.skipTypes(abstractRange).kind == tyRef
+
+proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode =
+  if isRef(n.typ): succ(opc) else: opc
+
+proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
+  let tmp = c.genx(ri)
+  assert dest >= 0
+  gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
+  c.freeTemp(tmp)
+
+proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
+  case le.kind
+  of nkBracketExpr:
+    let dest = c.genx(le.sons[0])
+    let idx = c.genx(le.sons[1])
+    let tmp = c.genx(ri)
+    if le.typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}:
+      c.gABC(le, opcWrStrIdx, dest, idx, tmp)
+    else:
+      c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp)
+    c.freeTemp(tmp)
+  of nkDotExpr:
+    let dest = c.genx(le.sons[0])
+    let idx = c.genx(le.sons[1])
+    let tmp = c.genx(ri)
+    c.gABC(le, whichAsgnOpc(le, opcWrObj), dest, idx, tmp)
+    c.freeTemp(tmp)
+  of nkSym:
+    let s = le.sym
+    if sfGlobal in s.flags:
+      withTemp(tmp, le.typ):
+        gen(c, ri, tmp)
+        c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position)
+    else:
+      InternalAssert s.position > 0 or (s.position == 0 and
+                                        s.kind in {skParam, skResult})
+      var dest: TRegister = s.position + ord(s.kind == skParam)
+      gen(c, ri, dest)
+  else:
+    let dest = c.genx(le)
+    genAsgn(c, dest, ri, requiresCopy)
+
+proc genLit(c: PCtx; n: PNode; dest: var TDest) =
+  if dest < 0: dest = c.getTemp(n.typ)
+  let lit = genLiteral(c, n)
+  c.gABx(n, opcLdConst, dest, lit)
+
+proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
+  let s = n.sym
+  if sfGlobal in s.flags:
+    if dest < 0: dest = c.getTemp(s.typ)
+    if s.position == 0:
+      c.globals.add(s.ast)
+      s.position = c.globals.len
+      # XXX var g = codeHere() ?
+    c.gABx(n, opcLdGlobal, dest, s.position)
+  else:
+    if s.position > 0 or (s.position == 0 and s.kind in {skParam, skResult}):
+      if dest < 0:
+        dest = s.position + ord(s.kind == skParam)
+      else:
+        # we need to generate an assignment:
+        genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp)
+    else:
+      InternalError(n.info, s.name.s & " " & $s.position)
+
+proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  let a = c.genx(n.sons[0])
+  let b = c.genx(n.sons[1])
+  if dest < 0: dest = c.getTemp(n.typ)
+  c.gABC(n, opc, dest, a, b)
+  c.freeTemp(a)
+  c.freeTemp(b)
+
+proc genObjAccess(c: PCtx; n: PNode; dest: var TDest) =
+  genAccess(c, n, dest, opcLdObj)
+
+proc genArrAccess(c: PCtx; n: PNode; dest: var TDest) =
+  genAccess(c, n, dest, opcLdArr)
+
+proc getNullValue*(typ: PType, info: TLineInfo): PNode
+proc getNullValueAux(obj: PNode, result: PNode) = 
+  case obj.kind
+  of nkRecList:
+    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result)
+  of nkRecCase:
+    getNullValueAux(obj.sons[0], result)
+    for i in countup(1, sonsLen(obj) - 1): 
+      getNullValueAux(lastSon(obj.sons[i]), result)
+  of nkSym:
+    addSon(result, getNullValue(obj.sym.typ, result.info))
+  else: InternalError(result.info, "getNullValueAux")
+  
+proc getNullValue(typ: PType, info: TLineInfo): PNode = 
+  var t = skipTypes(typ, abstractRange-{tyTypeDesc})
+  result = emptyNode
+  case t.kind
+  of tyBool, tyEnum, tyChar, tyInt..tyInt64: 
+    result = newNodeIT(nkIntLit, info, t)
+  of tyUInt..tyUInt64:
+    result = newNodeIT(nkUIntLit, info, t)
+  of tyFloat..tyFloat128: 
+    result = newNodeIt(nkFloatLit, info, t)
+  of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, 
+     tyStmt, tyTypeDesc, tyProc, tyRef:
+    result = newNodeIT(nkNilLit, info, t)
+  of tyObject: 
+    result = newNodeIT(nkPar, info, t)
+    getNullValueAux(t.n, result)
+    # initialize inherited fields:
+    var base = t.sons[0]
+    while base != nil:
+      getNullValueAux(skipTypes(base, skipPtrs).n, result)
+      base = base.sons[0]
+  of tyArray, tyArrayConstr: 
+    result = newNodeIT(nkBracket, info, t)
+    for i in countup(0, int(lengthOrd(t)) - 1): 
+      addSon(result, getNullValue(elemType(t), info))
+  of tyTuple:
+    result = newNodeIT(nkPar, info, t)
+    for i in countup(0, sonsLen(t) - 1):
+      addSon(result, getNullValue(t.sons[i], info))
+  of tySet:
+    result = newNodeIT(nkCurly, info, t)
+  else: InternalError("getNullValue: " & $t.kind)
+
+proc setSlot(c: PCtx; v: PSym) =
+  # XXX generate type initialization here?
+  if v.position == 0:
+    v.position = c.prc.maxSlots
+    c.prc.slots[v.position] = (inUse: true, kind: slotFixed)
+    inc c.prc.maxSlots
+    echo v.name.s, " has position ", v.position
+
+proc genVarSection(c: PCtx; n: PNode) =
+  for a in n:
+    if a.kind == nkCommentStmt: continue
+    #assert(a.sons[0].kind == nkSym) can happen for transformed vars
+    if a.kind == nkVarTuple:
+      let tmp = c.genx(a.lastSon)
+      for i in 0 .. a.len-3:
+        setSlot(c, a[i].sym)
+        # v = t[i]
+        var v: TDest = -1
+        genRdVar(c, a[i], v)
+        c.gABC(n, opcLdObj, v, tmp, i)
+        # XXX globals?
+      c.freeTemp(tmp)
+    elif a.sons[0].kind == nkSym:
+      let s = a.sons[0].sym
+      if sfGlobal in s.flags:
+        if s.position == 0:
+          let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
+          c.globals.add(sa)
+          s.position = c.globals.len
+        if a.sons[2].kind == nkEmpty:
+          when false:
+            withTemp(tmp, s.typ):
+              c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
+              c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
+        else:
+          let tmp = genx(c, a.sons[2])
+          c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
+          c.freeTemp(tmp)
+      else:
+        setSlot(c, s)
+        if a.sons[2].kind == nkEmpty:
+          c.gABx(a, opcLdNull, s.position, c.genType(s.typ))
+        else:
+          gen(c, a.sons[2], s.position.TRegister)
+    else:
+      # assign to a.sons[0]; happens for closures
+      if a.sons[2].kind == nkEmpty:
+        let tmp = genx(c, a.sons[0])
+        c.gABx(a, opcLdNull, tmp, c.genType(a.sons[0].typ))
+        c.freeTemp(tmp)
+      else:
+        genAsgn(c, a.sons[0], a.sons[2], true)
+
+proc genProc*(c: PCtx; s: PSym): int
+
+proc gen(c: PCtx; n: PNode; dest: var TDest) =
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    case s.kind
+    of skVar, skForVar, skTemp, skLet, skParam, skResult:
+      genRdVar(c, n, dest)
+    of skProc, skConverter, skMacro, skMethod, skIterator:
+      genLit(c, n, dest)
+    of skConst:
+      gen(c, s.ast, dest)
+    of skEnumField:
+      if dest < 0: dest = c.getTemp(n.typ)
+      if s.position >= low(int16) and s.position <= high(int16):
+        c.gABx(n, opcLdImmInt, dest, s.position)
+      else:
+        var lit = genLiteral(c, newIntNode(nkIntLit, s.position))
+        c.gABx(n, opcLdConst, dest, lit)
+    of skField:
+      InternalAssert dest < 0
+      if s.position > high(dest):
+        InternalError(n.info, 
+          "too large offset! cannot generate code for: " & s.name.s)
+      dest = s.position
+    else:
+      InternalError(n.info, "cannot generate code for: " & s.name.s)
+  of nkCallKinds:
+    if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
+      genMagic(c, n, dest)
+    else:
+      genCall(c, n, dest)
+  of nkCharLit..nkInt64Lit:
+    if isInt16Lit(n):
+      if dest < 0: dest = c.getTemp(n.typ)
+      c.gABx(n, opcLdImmInt, dest, n.intVal.int)
+    else:
+      genLit(c, n, dest)
+  of nkUIntLit..nkNilLit: genLit(c, n, dest)
+  of nkAsgn, nkFastAsgn: 
+    unused(n, dest)
+    genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
+  of nkDotExpr: genObjAccess(c, n, dest)
+  of nkBracketExpr: genArrAccess(c, n, dest)
+  of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref)
+  of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr)
+  of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
+  of nkCaseStmt: genCase(c, n, dest)
+  of nkWhileStmt:
+    unused(n, dest)
+    genWhile(c, n)
+  of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest)
+  of nkReturnStmt:
+    unused(n, dest)
+    genReturn(c, n)
+  of nkRaiseStmt:
+    unused(n, dest)
+    genRaise(c, n)
+  of nkBreakStmt:
+    unused(n, dest)
+    genBreak(c, n)
+  of nkTryStmt: genTry(c, n, dest)
+  of nkStmtList:
+    unused(n, dest)
+    for x in n: gen(c, x)
+  of nkStmtListExpr:
+    let L = n.len-1
+    for i in 0 .. <L: gen(c, n.sons[i])
+    gen(c, n.sons[L], dest)
+  of nkDiscardStmt:
+    unused(n, dest)
+    gen(c, n.sons[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    genConv(c, n, n.sons[1], dest)
+  of nkVarSection, nkLetSection:
+    unused(n, dest)
+    genVarSection(c, n)
+  of declarativeDefs:
+    unused(n, dest)
+  of nkLambdaKinds:
+    let s = n.sons[namePos].sym
+    discard genProc(c, s)
+    genLit(c, n.sons[namePos], dest)
+  of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
+     nkTemplateDef, nkIncludeStmt, nkImportStmt:
+    unused(n, dest)
+  else:
+    #of nkCurly, nkBracket, nkPar:
+    InternalError n.info, "too implement " & $n.kind
+
+proc genStmt*(c: PCtx; n: PNode): int =
+  result = c.code.len
+  var d: TDest = -1
+  gen(c, n, d)
+  let last = c.code.len-1
+  if last >= 0 and c.code[last].opcode == opcEof:
+    # since we can re-use the EOF nothing happened:
+    result = last
+  else:
+    gABC(c, n, opcEof)
+  InternalAssert d < 0
+
+proc genParams(c: PCtx; params: PNode) =
+  # res.sym.position is already 0
+  c.prc.slots[0] = (inUse: true, kind: slotFixed)
+  for i in 1.. <params.len:
+    let param = params.sons[i].sym
+    c.prc.slots[i] = (inUse: true, kind: slotFixed)
+  c.prc.maxSlots = max(params.len, 1)
+
+proc genProc(c: PCtx; s: PSym): int =
+  let x = s.ast.sons[optimizedCodePos]
+  if x.kind == nkEmpty:
+    result = c.code.len+1 # skip the jump instruction
+    s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result)
+    # thanks to the jmp we can add top level statements easily and also nest
+    # procs easily:
+    let body = s.getBody
+    let procStart = c.xjmp(body, opcJmp, 0)
+    var p = PProc(blocks: @[])
+    let oldPrc = c.prc
+    c.prc = p
+    # iterate over the parameters and allocate space for them:
+    genParams(c, s.typ.n)
+    gen(c, body)
+    # generate final 'return' statement:
+    c.gABC(body, opcRet)
+    c.patch(procStart)
+    s.position = c.prc.maxSlots
+    c.prc = oldPrc
+  else:
+    result = x.intVal.int