summary refs log tree commit diff stats
path: root/compiler/vm.nim
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 /compiler/vm.nim
parent84534ce4b8d2717f672e870921cab46dc2397c28 (diff)
downloadNim-b30e237a3746b4c4c421a87277a6458642c9f13a.tar.gz
first steps to a new evaluation engine
Diffstat (limited to 'compiler/vm.nim')
-rw-r--r--compiler/vm.nim584
1 files changed, 584 insertions, 0 deletions
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)
+