123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- #
- #
- # 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 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]
- opcAddInt,
- opcAddImmInt,
- opcSubInt,
- opcSubImmInt,
- opcLenSeq,
- opcLenStr,
- 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, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
- opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr,
- opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
- opcIsNil, opcOf, opcIs,
- opcSubStr, 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, opcNSetType, 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, opcNSetLineInfo,
- 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,
- opcMarshalLoad, opcMarshalStore,
- 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
- slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
- maxSlots*: int
- 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[tuple[key: string, value: VmCallback]]
- errorFlag*: string
- cache*: IdentCache
- config*: ConfigRef
- graph*: ModuleGraph
- oldErrorCount*: int
- TPosition* = distinct int
- PEvalContext* = PCtx
- proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx =
- PCtx(code: @[], debug: @[],
- globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
- prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
- comesFromHeuristic: unknownLineInfo, callbacks: @[], errorFlag: "",
- cache: cache, config: g.config, graph: g)
- proc refresh*(c: PCtx, module: PSym) =
- c.module = module
- c.prc = PProc(blocks: @[])
- c.loopIterations = c.config.maxLoopIterationsVM
- proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
- result = c.callbacks.len
- c.callbacks.add((name, callback))
- const
- firstABxInstr* = opcTJmp
- largeInstrs* = { # instructions which use 2 int32s instead of 1:
- opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
- opcMarshalLoad, opcMarshalStore}
- 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
|