diff options
Diffstat (limited to 'compiler/vmdef.nim')
-rw-r--r-- | compiler/vmdef.nim | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim new file mode 100644 index 000000000..bdb0aeed1 --- /dev/null +++ b/compiler/vmdef.nim @@ -0,0 +1,335 @@ +# +# +# The Nim 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-3 int32s in memory, it is a register based VM. + +import std/[tables, strutils] + +import ast, idents, options, modulegraphs, lineinfos + +type TInstrType* = uint64 + +const + regOBits = 8 # Opcode + regABits = 16 + regBBits = 16 + regCBits = 16 + regBxBits = 24 + + byteExcess* = 128 # we use excess-K for immediates + +# Calculate register shifts, masks and ranges + +const + regOShift* = 0.TInstrType + regAShift* = (regOShift + regOBits) + regBShift* = (regAShift + regABits) + regCShift* = (regBShift + regBBits) + regBxShift* = (regAShift + regABits) + + regOMask* = ((1.TInstrType shl regOBits) - 1) + regAMask* = ((1.TInstrType shl regABits) - 1) + regBMask* = ((1.TInstrType shl regBBits) - 1) + regCMask* = ((1.TInstrType shl regCBits) - 1) + regBxMask* = ((1.TInstrType shl regBxBits) - 1) + + wordExcess* = 1 shl (regBxBits-1) + regBxMin* = -wordExcess+1 + regBxMax* = wordExcess-1 + +type + TRegister* = range[0..regAMask.int] + TDest* = range[-1..regAMask.int] + TInstr* = distinct TInstrType + + TOpcode* = enum + opcEof, # end of code + opcRet, # return + opcYldYoid, # yield with no value + opcYldVal, # yield with a value + + opcAsgnInt, + opcAsgnFloat, + opcAsgnRef, + opcAsgnComplex, + opcCastIntToFloat32, # int and float must be of the same byte size + opcCastIntToFloat64, # int and float must be of the same byte size + opcCastFloatToInt32, # int and float must be of the same byte size + opcCastFloatToInt64, # int and float must be of the same byte size + opcCastPtrToInt, + opcCastIntToPtr, + opcFastAsgnComplex, + opcNodeToReg, + + opcLdArr, # a = b[c] + opcLdArrAddr, # a = addr(b[c]) + opcWrArr, # a[b] = c + opcLdObj, # a = b.c + opcLdObjAddr, # a = addr(b.c) + opcWrObj, # a.b = c + opcAddrReg, + opcAddrNode, + opcLdDeref, + opcWrDeref, + opcWrStrIdx, + opcLdStrIdx, # a = b[c] + opcLdStrIdxAddr, # a = addr(b[c]) + opcSlice, # toOpenArray(collection, left, right) + + opcAddInt, + opcAddImmInt, + opcSubInt, + opcSubImmInt, + opcLenSeq, + opcLenStr, + opcLenCstring, + + opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, + opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, + opcShrInt, opcShlInt, opcAshrInt, + opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, + opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, + opcLeFloat, opcLtFloat, opcLeu, opcLtu, + opcEqRef, opcEqNimNode, opcSameNodeType, + opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, + opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, + opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, + opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, + opcIsNil, opcOf, opcIs, + opcParseFloat, opcConv, opcCast, + opcQuit, opcInvalidField, + opcNarrowS, opcNarrowU, + opcSignExtend, + + opcAddStrCh, + opcAddStrStr, + opcAddSeqElem, + opcRangeChck, + + opcNAdd, + opcNAddMultiple, + opcNKind, + opcNSymKind, + opcNIntVal, + opcNFloatVal, + opcNSymbol, + opcNIdent, + opcNGetType, + opcNStrVal, + opcNSigHash, + opcNGetSize, + + opcNSetIntVal, + opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetStrVal, + opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym, + + opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt, + opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext, opcNodeId, + + opcSlurp, + opcGorge, + opcParseExprToAst, + opcParseStmtToAst, + opcQueryErrorFlag, + opcNError, + opcNWarning, + opcNHint, + opcNGetLineInfo, opcNCopyLineInfo, opcNSetLineInfoLine, + opcNSetLineInfoColumn, opcNSetLineInfoFile + opcEqIdent, + opcStrToIdent, + opcGetImpl, + opcGetImplTransf + + opcEcho, + opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ... + opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ... + + opcRaise, + opcNChild, + opcNSetChild, + opcCallSite, + opcNewStr, + + opcTJmp, # jump Bx if A != 0 + opcFJmp, # jump Bx if A == 0 + opcJmp, # jump Bx + opcJmpBack, # jump Bx; resulting from a while loop + opcBranch, # branch for 'case' + opcTry, + opcExcept, + opcFinally, + opcFinallyEnd, + opcNew, + opcNewSeq, + opcLdNull, # dest = nullvalue(types[Bx]) + opcLdNullReg, + opcLdConst, # dest = constants[Bx] + opcAsgnConst, # dest = copy(constants[Bx]) + opcLdGlobal, # dest = globals[Bx] + opcLdGlobalAddr, # dest = addr(globals[Bx]) + opcLdGlobalDerefFFI, # dest = globals[Bx][] + opcLdGlobalAddrDerefFFI, # globals[Bx][] = ... + + opcLdImmInt, # dest = immediate value + opcNBindSym, opcNDynBindSym, + opcSetType, # dest.typ = types[Bx] + opcTypeTrait, + opcSymOwner, + opcSymIsInstantiationOf + + TBlock* = object + label*: PSym + fixups*: seq[TPosition] + + TEvalMode* = enum ## reason for evaluation + emRepl, ## evaluate because in REPL mode + emConst, ## evaluate for 'const' according to spec + emOptimize, ## evaluate for optimization purposes (same as + ## emConst?) + emStaticExpr, ## evaluate for enforced compile time eval + ## ('static' context) + emStaticStmt ## 'static' as an expression + + TSandboxFlag* = enum ## what the evaluation engine should allow + allowCast, ## allow unsafe language feature: 'cast' + allowInfiniteLoops ## allow endless loops + TSandboxFlags* = set[TSandboxFlag] + + 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 + slotFixedVar, # slot is used for a fixed var/result (requires copy then) + slotFixedLet, # slot is used for a fixed param/let + 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 (s.node field is used) + slotTempPerm # slot is temporary but permanent (hack) + + TRegisterKind* = enum + rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr + TFullReg* = object # with a custom mark proc, we could use the same + # data representation as LuaJit (tagged NaNs). + case kind*: TRegisterKind + of rkNone: nil + of rkInt: intVal*: BiggestInt + of rkFloat: floatVal*: BiggestFloat + of rkNode: node*: PNode + of rkRegisterAddr: regAddr*: ptr TFullReg + of rkNodeAddr: nodeAddr*: ptr PNode + + PProc* = ref object + blocks*: seq[TBlock] # blocks; temp data structure + sym*: PSym + regInfo*: seq[tuple[inUse: bool, kind: TSlotKind]] + + VmArgs* = object + ra*, rb*, rc*: Natural + slots*: ptr UncheckedArray[TFullReg] + currentException*: PNode + currentLineInfo*: TLineInfo + VmCallback* = proc (args: VmArgs) {.closure.} + + PCtx* = ref TCtx + TCtx* = object of TPassContext # code gen context + code*: seq[TInstr] + debug*: seq[TLineInfo] # line info for every instruction; kept separate + # to not slow down interpretation + 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 + module*: PSym + callsite*: PNode + mode*: TEvalMode + features*: TSandboxFlags + traceActive*: bool + loopIterations*: int + comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces + callbacks*: seq[VmCallback] + callbackIndex*: Table[string, int] + errorFlag*: string + cache*: IdentCache + config*: ConfigRef + graph*: ModuleGraph + oldErrorCount*: int + profiler*: Profiler + templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst + vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC) + procToCodePos*: Table[int, int] + + PStackFrame* = ref TStackFrame + TStackFrame* {.acyclic.} = object + prc*: PSym # current prc; proc that is evaluated + slots*: seq[TFullReg] # 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? + Profiler* = object + tEnter*: float + tos*: PStackFrame + + TPosition* = distinct int + + PEvalContext* = PCtx + +proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator): PCtx = + PCtx(code: @[], debug: @[], + globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], + prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM, + comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "", + cache: cache, config: g.config, graph: g, idgen: idgen) + +proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) = + c.module = module + c.prc = PProc(blocks: @[]) + c.loopIterations = c.config.maxLoopIterationsVM + c.idgen = idgen + +proc reverseName(s: string): string = + result = newStringOfCap(s.len) + let y = s.split('.') + for i in 1..y.len: + result.add y[^i] + if i != y.len: + result.add '.' + +proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} = + result = c.callbacks.len + c.callbacks.add(callback) + c.callbackIndex[reverseName(name)] = result + +const + firstABxInstr* = opcTJmp + largeInstrs* = { # instructions which use 2 int32s instead of 1: + opcConv, opcCast, opcNewSeq, opcOf + } + slotSomeTemp* = slotTempUnknown + relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} + +# flag is used to signal opcSeqLen if node is NimNode. +const nimNodeFlag* = 16 + +template opcode*(x: TInstr): TOpcode = TOpcode(x.TInstrType shr regOShift and regOMask) +template regA*(x: TInstr): TRegister = TRegister(x.TInstrType shr regAShift and regAMask) +template regB*(x: TInstr): TRegister = TRegister(x.TInstrType shr regBShift and regBMask) +template regC*(x: TInstr): TRegister = TRegister(x.TInstrType shr regCShift and regCMask) +template regBx*(x: TInstr): int = (x.TInstrType shr regBxShift and regBxMask).int + +template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess |