This file implements the new evaluation engine for Nim code. An instruction is 1-3 int32s in memory, it is a register based VM. # TODO: update the above -- it doesn't reflect reality anymore
To use the VM once has to first set up an execution environment, which is represented by TCtx. In order to execute something, a VmThread instance is required and can be created with initVmThread. Only a single thread may exist for a given TCtx at a time -- creating multiple ones, even if the others are not used, is not allowed.
Imports
-
../ast/ast, ../ast/lineinfos, ../ast/renderer, ../ast/trees, ../ast/idents, ../ast/typesrenderer, ../ast/types, ../modules/modulegraphs, ../front/options, ../front/msgs, ../utils/idioms, ../utils/int128, ../utils/btrees, ../utils/bitsets, ../sem/sighashes, ../sem/macrocacheimpl, ../sem/transf, ../sem/evaltempl, vmprofiler, vmchecks, vmcompilerserdes, vmdef, vmdeps, vmerrors, vmmemory, vmobjects, vmtypes, ../ast/reports_vm, ../ast/report_enums, ../ast/reports
Types
VmThread = object pc: int ## the program counter. Points to the instruction to execute next regs*: seq[TFullReg] ## all registers beloning to the thread. Each frame owns a slice of it sframes: seq[TStackFrame] ## stack frames loopIterations: int ## the number of remaining jumps backwards currentException: HeapSlotHandle ## the exception ref that's returned when querying the current exception ehStack: seq[tuple[ex: VmException, pc: uint32]] ## the stack of currently executed EH threads. A stack is needed since ## exceptions can be raised while another exception is in flight
- This is beginning of splitting up TCtx. A VmThread is meant to encapsulate the state that makes up a single execution. This includes things like the program counter, stack frames, active exception, etc. Source Edit
YieldReason = object case kind*: YieldReasonKind of yrkDone: reg*: Option[TRegister] ## the register that holds the result, or ## 'none', if there is no result of yrkError: error*: VmEvent of yrkQuit: exitCode*: int of yrkMissingProcedure: entry*: FunctionIndex ## the entry of the procedure that is a stub of yrkEcho: strs*: seq[string] ## strings to be echo'd, at least one item
- The result of a single execution step (i.e. a call to execute) Source Edit
YieldReasonKind = enum yrkDone, ## execution is done. There is no more code to execute yrkError, ## an error occurred. No clear distinction between fatal (the thread ## must not be resumed) and non-fatal (the thread may be resumed) is ## made yet, so all errors should be treated as fatal yrkQuit, ## a call to ``quit`` was executed. The thread can't be resumed yrkMissingProcedure, ## a procedure stub was called. The stub has to be resolved before ## ## continuing execution yrkEcho ## "syscall" of echo, the VM expects the code executing it to handle the ## echo and then resume execution
- Source Edit
Procs
proc createStackTrace(c: TCtx; thread: VmThread; recursionLimit: int = 100): VmStackTrace {. ...raises: [], tags: [].}
- Generates a stack-trace report starting at frame sframe (inclusive). The amount of entries in the trace is limited to recursionLimit, further entries are skipped, though the entry function is always included in the trace Source Edit
proc dispose(c: var TCtx; t: sink VmThread) {....raises: [Exception], tags: [RootEffect].}
- Cleans up and frees all VM data owned by t. Source Edit
func ensureKind(n: var TFullReg; k: TRegisterKind; mm: var VmMemoryManager) {. inline, ...raises: [Exception], tags: [RootEffect].}
- Source Edit
proc execute(c: var TCtx; thread: var VmThread): YieldReason {.inline, ...raises: [ Exception, IOError, ResultErrorRef[system.void], KeyError, ERecoverableError, ValueError, ResultErrorRef[vm.VmException]], tags: [RootEffect, TimeEffect, ReadDirEffect, ReadIOEffect].}
- Executes the VM thread until it yields. The reason for yielding together with associated data is returned as a YieldReason object Source Edit
func initLocReg(r: var TFullReg; typ: PVmType; mm: var VmMemoryManager) {. ...raises: [Exception], tags: [RootEffect].}
- Transitions r to a location register storing a location of type typ Source Edit
proc initVmThread(c: var TCtx; pc: PrgCtr; numRegisters: int; sym: PSym): VmThread {. ...raises: [], tags: [].}
- Sets up a VmThread instance that will start execution at pc and that has numRegisters as the initial amount of registers. sym is the symbol to associate the initial stack-frame with. It may be nil. Source Edit
func loadEmptyReg(r: var TFullReg; typ: PVmType; info: TLineInfo; mm: var VmMemoryManager): bool {....raises: [Exception], tags: [RootEffect].}
- If a value of typ fits into a register, transitions r to the correct state, loads the default value and returns true. Returns false otherwise Source Edit
proc regToNode(c: TCtx; x: TFullReg; typ: PType; info: TLineInfo): PNode {. ...raises: [Exception], tags: [RootEffect].}
- Deserializes the value stored by x to a PNode of type typ Source Edit
func vmEventToAstDiagVmError(evt: VmEvent): AstDiagVmError {.inline, ...raises: [], tags: [].}
- Source Edit