vm

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2026 License: BSD-3-Clause Imports: 25 Imported by: 0

README

vm

vm is a bytecode based stack machine.

The purpose of vm is to provide a simple, fast, embeddable and portable Go execution environment.

graph LR
s[ ] --> |source| a(scanner)
--> |tokens| b(parser)
--> |AST| c(codegen)
--> |bytecode| d[vm]
subgraph vm
    d
end
style s height:0px;

The bytecode consists of a dozen of instructions, each taking 0 or 1 immediate argument (non-immediate arguments are only passed through the stack). Only a few operators for a few types are implemented. I expect to have 1 instruction per operator per numerical type, all with the same pattern, which would be generated from a template. Estimate is around 20 operators and 10 numerical types, so around 200 instructions in final.

Structurally, the vm implements logical and arithmetic operators, condional jumps for if, for and switch control flow, and function call, return and frame management.

the memory state of the vm is a slice of ([]Value), where Value is similar to a reflect.Value, containing pointers to value data and type.

The whole vm is coded in a single function of 80 lines with no dependencies. The size will grow as we add missing instructions, but the code complexity will remain the same.

the vm1 package is totally standalone and could be used for any purpose outside of mvm and/or gno.

Instructions

Opcode Parameters Stack (before : after) Description
Get local, index : v Get value at address and push it on the stack
Set local, index v : Pop stack and set value at address
Add a b : c Pop a, b and push (a+b)

Memory addresses passed to instructions are computed from a tuple of integers: local and index. local can be 0 or 1. if 0, index is the offset from the start of memory, else index is the offset from the frame pointer (fp register).

Documentation

Overview

Package vm implement a stack based virtual machine.

Index

Constants

View Source
const (
	Global = 0
	Local  = 1
)

Memory attributes.

View Source
const CallSpreadFlag int32 = 1 << 15

CallSpreadFlag is set in the B operand of Call to indicate a spread call (f(s...)), so the VM uses reflect.CallSlice instead of reflect.Call for native variadic functions.

View Source
const DeferSpreadFlag = 4

DeferSpreadFlag marks that the call's final argument is a variadic slice, so native defers use reflect.CallSlice. Set in the DeferPush B operand alongside isX. Defer header layout: isX bits 0-1, spread bit 2, narg bits 3-62, started 63.

View Source
const ValueSize = int(unsafe.Sizeof(Value{}))

ValueSize is the in-memory footprint of a Value on the current platform. Used by stat reporters to convert slot counts to bytes.

Variables

View Source
var (
	TypeOf        = mtype.TypeOf
	FuncOf        = mtype.FuncOf
	StructOf      = mtype.StructOf
	NewStructType = mtype.NewStructType
)

Symbolic type constructors re-exported from mtype. The derived-type constructors and reserve/fill helpers (SymPtr/SymSlice/..., PointerTo, CanonicalType, AttachPtrDerived, ...) are runtime materialization and live in vm (derive.go), not mtype.

View Source
var (
	SymFunc   = mtype.SymFunc
	SymStruct = mtype.SymStruct
	SymBasic  = mtype.SymBasic
)

Symbolic (Rtype-nil) leaf/aggregate constructors goparser uses post-flip; comp materializes the rtype later (see MaterializeRtype). The derived constructors (SymPtr/SymSlice/SymArray/SymChan/SymMap) live in derive.go: they memoize and register the derived *Type in the cache, leaving Rtype nil for comp to fill.

View Source
var AnyRtype = mtype.AnyRtype

AnyRtype is the empty-interface rtype, re-exported from mtype.

View Source
var ErrGoroutineFault = errors.New("vm: unrecovered goroutine panic")

ErrGoroutineFault aborts a Run that was waiting on a channel when a goroutine panicked elsewhere; recoverPanic returns it and the top-level Eval maps it to a non-zero exit.

View Source
var NumKindOffset [reflect.Float64 + 1]int

NumKindOffset maps a reflect.Kind to a 0-based offset into per-type opcode blocks. Returns -1 for non-numeric kinds.

View Source
var OpaqueRtype = reflect.TypeFor[Opaque]()

OpaqueRtype is the reflect.Type for Opaque.

Functions

func AttachPtrDerived added in v0.4.0

func AttachPtrDerived(t *mtype.Type, newPtrRT reflect.Type)

AttachPtrDerived records newPtrRT (a *T-with-methods rtype) as t's derived pointer type, materializing the slot if absent so a later PointerTo(t) returns it instead of a fresh methodless *T. The reserve path wires the *T identity once at materialize, so an existing slot just adopts newPtrRT.

func CanonicalType added in v0.4.0

func CanonicalType(t *mtype.Type) *mtype.Type

CanonicalType walks the Base chain to the source *Type a struct-field copy derived from (t itself if Base is nil); depth-capped against cyclic Base. Used by symbol resolution to match a clone against its defining identity.

func DumpFrame

func DumpFrame(w io.Writer, mem []Value, code Code, fp, sp, narg, nret int, di *DebugInfo)

DumpFrame decodes and pretty-prints the call frame at the given fp. di is optional (may be nil); when set, slots are annotated with variable names.

func DumpFrameStderr

func DumpFrameStderr(mem []Value, code Code, fp, sp, narg, nret int, di *DebugInfo)

DumpFrameStderr is a convenience wrapper that prints to stderr.

func Exportable added in v0.2.0

func Exportable(rv reflect.Value) reflect.Value

Exportable returns rv with its read-only flag cleared so that .Interface() and .Call() do not panic on values obtained from unexported struct fields.

func MaterializeRtype added in v0.4.0

func MaterializeRtype(t *mtype.Type) reflect.Type

MaterializeRtype builds and caches t.Rtype from t's symbolic graph (Kind + ElemType/KeyType/Fields/Params/Returns + ArrayLen/ChanDir/Variadic/Tags) when it is not already set, recursing into children first. This is the comp-side materialization that lets goparser build a *Type without an rtype.

A named leaf (a primitive or struct that carries methods) must already hold its rtype so an un-materialized leaf here yields nil.

func NewRuntimeFuncSentinel added in v0.2.0

func NewRuntimeFuncSentinel() *runtime.Func

NewRuntimeFuncSentinel returns a fresh *runtime.Func whose address is unique. Use it together with RegisterRuntimeFunc to mark a PC as virtualized.

func PointerTo

func PointerTo(t *mtype.Type) *mtype.Type

PointerTo returns the canonical pointer type with element t, memoized.

func RegisterNativeMethodHook added in v0.3.0

func RegisterNativeMethodHook(recvInstance any, methodName string, hook NativeMethodHook)

RegisterNativeMethodHook installs hook for recvInstance's named method. recvInstance is a typed nil (e.g. (*testing.T)(nil)); the function keys on reflect.TypeOf(recvInstance).

func RegisterRuntimeFunc added in v0.2.0

func RegisterRuntimeFunc(rf *runtime.Func, name, file string, line int)

RegisterRuntimeFunc associates Name/File/Line metadata with rf so that interpreted code calling rf.Name() / rf.FileLine() observes the recorded values instead of the host runtime's lookup. rf must be a non-nil pointer obtained from NewRuntimeFuncSentinel so the address is distinct from any other registered sentinel.

func RegisterSynthIfaceTargetFunc added in v0.4.0

func RegisterSynthIfaceTargetFunc(fn reflect.Value)

RegisterSynthIfaceTargetFunc allowlists fn for synth-interface target retyping. Call from package init.

func SymArray added in v0.4.0

func SymArray(n int, t *mtype.Type) *mtype.Type

SymArray returns the canonical [n]t, registered in t's derived cache, Rtype nil.

func SymChan added in v0.4.0

func SymChan(dir reflect.ChanDir, t *mtype.Type) *mtype.Type

SymChan returns the canonical chan-t, registered in t's derived cache, Rtype nil.

func SymMap added in v0.4.0

func SymMap(k, e *mtype.Type) *mtype.Type

SymMap returns the canonical map[k]e, registered in k's derived cache, Rtype nil.

func SymPtr added in v0.4.0

func SymPtr(t *mtype.Type) *mtype.Type

SymPtr returns the canonical *t, registered in t's derived cache, Rtype nil.

func SymSlice added in v0.4.0

func SymSlice(t *mtype.Type) *mtype.Type

SymSlice returns the canonical []t, registered in t's derived cache, Rtype nil.

func Vstring

func Vstring(lv []Value) string

Vstring returns the string representation of a list of values.

Types

type CleanExit added in v0.3.0

type CleanExit interface {
	CleanExit()
}

CleanExit marks an error value as an intentional, non-crash program termination signal. Such a value bypasses interpreted recover(), and propagates to the top-level Run.

type Closure

type Closure struct {
	Code int      // code address (same as the plain-int function value)
	Heap []*Value // heap-allocated cells, one per captured variable
}

Closure bundles a function code address with its captured variables.

type Code

type Code []Instruction

Code represents the virtual machine byte code.

type DebugInfo

type DebugInfo struct {
	Sources scan.Sources          // source position registry (multi-file / REPL)
	Labels  map[int]string        // code address -> label/function name
	Funcs   []FuncRange           // function bytecode ranges, used by FuncAt to pick the innermost frame
	Globals map[int]string        // data index -> symbol name
	Locals  map[string][]LocalVar // function name -> local variable list
}

DebugInfo holds symbolic information for annotating debug output. Built by the compiler from the symbol table and source registry.

func NewDebugInfo

func NewDebugInfo() *DebugInfo

NewDebugInfo returns an empty DebugInfo ready to be populated.

func (*DebugInfo) FuncAt added in v0.2.0

func (d *DebugInfo) FuncAt(ip int) string

FuncAt returns the name of the innermost function whose bytecode range contains ip. Falls back to a "largest start <= ip" scan over Labels when Funcs is empty (e.g. snapshots produced before BuildDebugInfo started populating ranges). Returns "" when no function qualifies.

func (*DebugInfo) LocalName

func (d *DebugInfo) LocalName(funcName string, offset int) string

LocalName returns the variable name for a local slot offset within func funcName.

func (*DebugInfo) PosToLine

func (d *DebugInfo) PosToLine(pos Pos) string

PosToLine converts a global byte offset to a human-readable location string. Returns "" if no sources are registered or pos is out of range.

type EmbeddedField

type EmbeddedField = mtype.EmbeddedField

EmbeddedField is re-exported from mtype.

type FuncRange added in v0.2.0

type FuncRange struct {
	Start int
	End   int
	Name  string
}

FuncRange describes the bytecode range of a function (or anonymous closure). End is one past the last instruction of the body (i.e. an exclusive bound). Closures are emitted inline within their outer function so ranges nest; FuncAt picks the innermost containing range.

type Iface

type Iface struct {
	Typ *Type // concrete mvm type (carries Name for method lookup)
	Val Value // the concrete value
}

Iface represents a boxed interface value at runtime. It preserves the concrete mvm type identity for dynamic method dispatch.

func (Iface) Format added in v0.2.0

func (i Iface) Format(s fmt.State, verb rune)

Format routes fmt verbs to the concrete value so an Iface boxed in an interface{} slot renders like the raw Go value.

type IfaceMethod

type IfaceMethod = mtype.IfaceMethod

IfaceMethod is re-exported from mtype.

type Instruction

type Instruction struct {
	Op   Op
	A, B int32
	Pos  Pos
}

Instruction represents a virtual machine bytecode instruction (16 bytes). Fields A, B hold up to 2 immediate operands (0 when unused).

func (Instruction) String

func (i Instruction) String() (s string)

type LocalVar

type LocalVar struct {
	Offset int    // offset from fp (1-based, as in Get Local N)
	Name   string // variable name (short, without scope prefix)
}

LocalVar describes a local variable within a function frame.

type Machine

type Machine struct {
	MethodNames     []string       // names by global method ID
	MethodFuncTypes []reflect.Type // bound-method func type (no receiver) by global method ID
	// contains filtered or unexported fields
}

Machine is a stack-based virtual machine that executes bytecode instructions.

func ActiveMachine added in v0.2.0

func ActiveMachine() *Machine

ActiveMachine returns the Machine currently set via SetActiveMachine on the calling goroutine, or nil if none. Prefer reaching the Machine through an explicit parameter or closure capture; ActiveMachine is reserved for native bridge closures installed at package patch time with no other route to the runtime.

func NewMachine

func NewMachine() *Machine

NewMachine returns a pointer on a new Machine.

func SetActiveMachine added in v0.2.0

func SetActiveMachine(m *Machine) (prev *Machine)

SetActiveMachine records m as the running Machine for the current goroutine and returns the previous value (nil if none). Pair with `defer SetActiveMachine(prev)` to restore on return. Passing m == nil (the restore step at goroutine top of stack) deletes the slot so the map doesn't accumulate stale entries from short-lived goroutines.

func (*Machine) AttachSynthMethods added in v0.4.0

func (m *Machine) AttachSynthMethods(t *Type) error

AttachSynthMethods fills t's interpreted methods into the synth rtype that was reserved for t at materialize (via runtype + stdlib/stubs), in place -- t.Rtype and any composite that captured it keep their identity. Native code that asserts the rtype to an interface (fmt.Stringer, error, json.Marshaler, json.Unmarshaler, etc.) then dispatches the method directly, with no bridge proxy.

Handles any combination of the supported method shapes (see detectShape) on any supported kind, plus pointer-receiver variants on *T via attachPtrRecv. Up to synth's per-attach method cap (currently 16); excess methods of the same receiver kind are silently dropped.

func (*Machine) CallFunc

func (m *Machine) CallFunc(fval Value, funcType reflect.Type, args []reflect.Value) ([]reflect.Value, error)

CallFunc executes a mvm function value with the given arguments and returns the results. It saves and restores per-frame execution state so it can be called from native Go callbacks (reflect.MakeFunc wrappers) even while Run is in progress (single-threaded re-entrancy). Globals are NOT isolated: a callback's package-var write is visible to the outer Run, matching Go callback semantics and the goroutine model documented in ADR-008.

func (*Machine) CallSitePos added in v0.3.0

func (m *Machine) CallSitePos() Pos

CallSitePos returns the source Pos of the instruction that triggered the currently executing native call. Returns 0 when the IP is out of range.

func (*Machine) DebugInfo added in v0.2.0

func (m *Machine) DebugInfo() *DebugInfo

DebugInfo returns the current DebugInfo, or nil if no builder was registered with SetDebugInfo.

func (*Machine) DumpCallStack

func (m *Machine) DumpCallStack(w io.Writer, di *DebugInfo)

DumpCallStack walks the frame pointer chain and prints every frame.

func (*Machine) DumpCallStackStderr

func (m *Machine) DumpCallStackStderr(di *DebugInfo)

DumpCallStackStderr is a convenience wrapper that prints to stderr.

func (*Machine) EnableGoroutineFaults added in v0.4.0

func (m *Machine) EnableGoroutineFaults()

EnableGoroutineFaults arms goroutine-panic capture on the root machine before it runs, so runner machines and goroutine children share the sink. Idempotent.

func (*Machine) GoroutineFault added in v0.4.0

func (m *Machine) GoroutineFault() error

GoroutineFault reports the first recorded goroutine panic regardless of policy. Under the continue policy the suite still runs to the end; the driver calls this afterward to fail the run.

func (*Machine) HeapSize added in v0.3.0

func (m *Machine) HeapSize() int

HeapSize returns the number of heap-allocated cells currently held by the machine's active closure context. Typically 0 between Run() calls; nonzero only mid-execution. Reported by FormatStats when nonzero.

func (*Machine) MakeMethodCallable

func (m *Machine) MakeMethodCallable(ifc Iface, method Method) Value

MakeMethodCallable returns a mvm func Value suitable for Machine.CallFunc. The receiver cell is constructed with method.Path applied.

func (*Machine) MethodByName

func (m *Machine) MethodByName(t *Type, name string) (Method, bool)

MethodByName returns the first resolved method named `name` reachable from t. For pointer types, methods declared on the element type are also searched. Returns (Method, true) on hit.

func (*Machine) Out

func (m *Machine) Out() io.Writer

Out returns the machine's standard output writer.

func (*Machine) PendingGoroutineFault added in v0.4.0

func (m *Machine) PendingGoroutineFault() error

PendingGoroutineFault reports a recorded goroutine panic that should propagate (nil when none, or under the continue policy).

func (*Machine) PopExit

func (m *Machine) PopExit()

PopExit removes the last machine code instruction if is Exit.

func (*Machine) Push

func (m *Machine) Push(v ...Value) (l int)

Push pushes data values into the machine's global storage. Globals are always loaded via Push before Run is called.

func (*Machine) PushCode

func (m *Machine) PushCode(code ...Instruction) (p int)

PushCode adds instructions to the machine code (with zero source positions).

func (*Machine) Run

func (m *Machine) Run() (err error)

Run runs a program.

func (*Machine) SetDebugIO

func (m *Machine) SetDebugIO(in io.Reader, out io.Writer)

SetDebugIO sets the I/O streams for the interactive debug mode.

func (*Machine) SetDebugInfo

func (m *Machine) SetDebugInfo(fn func() *DebugInfo)

SetDebugInfo registers a function that builds DebugInfo on demand and invalidates the trace-step cache so the next traceStep call rebuilds.

func (*Machine) SetGoroutineFaultContinue added in v0.4.0

func (m *Machine) SetGoroutineFaultContinue(v bool)

SetGoroutineFaultContinue picks the policy for a later goroutine panic: false (default) propagates it as a non-zero exit; true records+logs only and lets execution continue (mvm test keeps running the suite). Call before the run.

func (*Machine) SetIO

func (m *Machine) SetIO(in io.Reader, out, err io.Writer)

SetIO sets the I/O streams for the machine.

func (*Machine) SetIP

func (m *Machine) SetIP(ip int)

SetIP sets the value of machine instruction pointer to given index.

func (*Machine) SetTraceOps added in v0.2.0

func (m *Machine) SetTraceOps(on bool)

SetTraceOps enables or disables bytecode-level tracing.

func (*Machine) SetTracing added in v0.2.0

func (m *Machine) SetTracing(on bool)

SetTracing enables or disables `set -x`-style line tracing. Toggles take effect at the next Run().

func (*Machine) StackLen added in v0.4.0

func (m *Machine) StackLen() int

StackLen returns the number of values left on the data stack.

func (*Machine) Top

func (m *Machine) Top() (v Value)

Top returns (but not remove) the value on the top of machine stack.

func (*Machine) TraceOps added in v0.2.0

func (m *Machine) TraceOps() bool

TraceOps reports whether bytecode-level tracing is enabled.

func (*Machine) Tracing added in v0.2.0

func (m *Machine) Tracing() bool

Tracing reports whether line tracing is enabled.

func (*Machine) TrimStack

func (m *Machine) TrimStack()

TrimStack removes leftover stack values from a previous Run. Call before pushing new global data on re-entry.

func (*Machine) WalkCallStack added in v0.2.0

func (m *Machine) WalkCallStack(yield func(StackFrame) bool)

WalkCallStack invokes yield for each call frame from innermost (the currently running function) to outermost. The first yielded frame's IP is m.ip-1 (the just-executed or about-to-execute instruction); each subsequent frame's IP is the call instruction in the caller (retIP-1 of the inner frame). yield returns false to stop early.

type Method

type Method = mtype.Method

Method is re-exported from mtype.

type MvmFunc

type MvmFunc struct {
	Val Value         // mvm func (int code addr or Closure)
	GF  reflect.Value // reflect.MakeFunc wrapper for native Go callbacks
}

MvmFunc bundles a mvm func value with its native Go reflect.MakeFunc wrapper. Stored when a mvm func is assigned to a struct field of func type: GF is callable from native Go (HTTP handlers, callbacks, etc.); Val is the original mvm func dispatched directly by the VM.

type NativeMethodHook added in v0.3.0

type NativeMethodHook = func(m *Machine, recv reflect.Value, args []reflect.Value) []reflect.Value

NativeMethodHook intercepts a native method call. The returned values are the replacement results. Whether a hook is installed for the (rtype, name) pair is reported separately by hasNativeMethodHook.

type Op

type Op int32

Op is a VM opcode (bytecode instruction).

const (
	// Instruction effect on stack: values consumed -- values produced.
	Nop          Op = iota // --
	Addr                   // a -- &a ;
	AddrLocal              // -- &local ; push pointer to mem[fp-1+$1]; promotes slot to addressable storage so writes via the pointer propagate back; $2!=0 retypes a func slot to func type globals[$2-1]
	Append                 // slice [v0..vn-1] -- slice' ; append $0 values to slice
	AppendSlice            // slice [v0..vn-1] -- slice' ; pack $0 values into []T, reflect.AppendSlice; elem type at mem[$1]; $0=0 means spread mode: append(a, b...)
	Call                   // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...); B bit 15 = spread flag
	CallImm                // [a1 .. ai] -- [r1 .. rj] ; $1=dataIdx of func, $2=narg<<16|nret
	CallImmFast            // like CallImm; emitted when no callee param has reflect Kind Struct or Array so detachByValueArgs can be elided
	Cap                    // -- x ; x = cap(mem[sp-$0])
	Clear                  // x -- ; clear(x): delete all map entries or zero all slice elements
	Convert                // v -- v' ; v' = convert(v, type at mem[$1]); optional $2 = stack depth offset
	CopySlice              // dst src -- n ; n = copy(dst, src)
	DeferPush              // func [a0..an-1] -- func [a0..an-1] [packed prevHead retIP] ; register deferred call on stack; $0=narg, $1=1 if native
	DeferRet               // -- ; sentinel: restore outer frame after a deferred call returns
	DeleteMap              // map key -- ; delete(map, key)
	Deref                  // x -- *x ;
	DerefSet               // ptr val -- ; *ptr = val
	Equal                  // n1 n2 -- cond ; cond = n1 == n2
	EqualSet               // n1 n2 -- n1 cond ; cond = n1 == n2
	Exit                   // -- ;
	Field                  // s -- f ; f = s.FieldIndex($1, ...)
	FieldFset              // s i v -- s; s.FieldIndex(i) = v
	FieldRefSet            // fref v -- ; fref = v (via setFuncField)
	FieldSet               // s d -- s ; s.FieldIndex($1, ...) = d
	Fnew                   // -- x; x = new mem[$1]
	FnewE                  // -- x; x = new mem[$1].Elem()
	Get                    // addr -- value ; value = mem[addr]
	Grow                   // -- ; sp += $1
	HeapAlloc              // -- &cell ; cell = new(Value), push its pointer
	HeapGet                // -- v    ; v = *State.Heap[$1]
	HeapPtr                // -- &cell ; push State.Heap[$1] itself (transitive capture)
	HeapSet                // v --    ; *State.Heap[$1] = v
	CellGet                // -- v    ; cell = mem[fp-1+$1].(*Value); push *cell
	CellSet                // v --    ; cell = mem[fp-1+$1].(*Value); *cell = v
	IfaceCall              // iface -- closure ; dynamic dispatch method $1 on iface
	IfaceWrap              // v -- iface ; wrap v in Iface{type at $1, v}
	Index                  // a i -- a[i] ;
	IndexAddr              // a i -- &a[i] ; pointer to element
	IndexSet               // a i v -- a; a[i] = v
	Jump                   // -- ; ip += $1
	JumpFalse              // cond -- ; if cond { ip += $1 }
	JumpSetFalse           //
	JumpSetTrue            //
	JumpTrue               // cond -- ; if cond { ip += $1 }
	Len                    // -- x; x = mem[sp-$1]
	MapIndex               // a i -- a[i]
	MapIndexOk             // a i -- v ok ; v, ok = a[i]
	MapSet                 // a i v -- a; a[i] = v
	MkClosure              // code [&c0..&cn-1] -- clo ; clo = Closure{code, heap}
	MkMap                  // -- map ; create map[K]V, key type at mem[$0], val type at mem[$1]
	MkSlice                // [v0..vn-1] -- slice ; collect $0 values into []T, elem type at mem[$1]
	New                    // -- x; mem[fp+$1] = new mem[$2]
	Next                   // -- ; iterator next, set K
	Next0                  // -- ; iterator next, no variable
	Next2                  // -- ; iterator next, set K V
	Not                    // c -- r ; r = !c
	Panic                  // v -- ; pop value, start stack unwinding
	PanicUnwind            // -- ; sentinel: handle panic stack unwinding
	Pop                    // v --
	PtrNew                 // -- ptr ; ptr = new(T), type at mem[$0]
	Pull                   // a -- a s n; pull iterator next and stop function
	Pull2                  // a -- a s n; pull iterator next and stop function
	Push                   // -- v
	Recover                // -- v ; push recovered value (or nil if not panicking in a deferred call)
	Return                 // [r1 .. ri] -- ; exit frame, nret and frameBase from frames
	SetGlobal              // v -- ; mem[$1] = v (globals)
	SetLocal               // v -- ; mem[fp-1+$1] = v
	SetS                   // dest val -- ; dest.Set(val)
	Slice                  // a l h -- a; a = a [l:h]
	Slice3                 // a l h m -- a; a = a[l:h:m]
	Stop                   // -- iterator stop; sp -= 3 + $1
	Swap                   // --
	Trap                   // -- ; pause VM execution and enter debug mode
	TypeAssert             // iface -- v [ok] ; assert iface holds type at mem[$1]; $2=0 panics, $2=1 ok form
	TypeBranch             // iface -- ; pop iface; if iface doesn't hold type at mem[$2] (or $2==-1 for nil), ip += $1
	WrapFunc               // mvmFuncVal -- MvmFunc ; wrap mvm func in reflect.MakeFunc for native callbacks; $0=typeIdx, $1=depth from sp (0=top)
	MkMethodExpr           // -- f ; push func value for interpreted method expression T.M; $0=method code global, $1=method-expr (recv-first) typeIdx

	// Goroutine and channel opcodes.
	GoCall     // f [a1..ai] -- ; spawn goroutine; $0=narg
	GoCallImm  // [a1..ai] -- ; spawn goroutine to known func; $0=dataIdx, $1=narg
	MkChan     // -- ch ; create channel; $0=elemTypeIdx, $1=bufsize (-1=from stack)
	ChanSend   // ch v -- ; send to channel
	ChanRecv   // ch -- v [ok] ; receive from channel; $0=1 for ok-form
	ChanClose  // ch -- ; close channel
	SelectExec // ch0 [v0] .. chN [vN] -- chosenIdx ; $0=metaIdx, $1=ncase; calls reflect.Select

	Print   // [v0..vn-1] -- ; print $0 values to m.out
	Println // [v0..vn-1] -- ; println $0 values to m.out, space-separated, trailing newline

	Min // [v0..vn-1] -- min ; find min of $0 values; $1 = reflect.Kind for dispatch
	Max // [v0..vn-1] -- max ; find max of $0 values; $1 = reflect.Kind for dispatch

	Complex // f1 f2 -- c ; c = complex(f1, f2); $0 = reflect.Kind for dispatch
	Real    // c -- f ; f = real(c); $0 = reflect.Kind for dispatch
	Imag    // c -- f ; f = imag(c); $0 = reflect.Kind for dispatch

	AddStr     // s1 s2 -- s ; s = s1 + s2 (string concatenation)
	GreaterStr // s1 s2 -- cond ; cond = s1 > s2
	LowerStr   // s1 s2 -- cond ; cond = s1 < s2

	AddInt // n1 n2 -- sum
	AddInt8
	AddInt16
	AddInt32
	AddInt64
	AddUint
	AddUint8
	AddUint16
	AddUint32
	AddUint64
	AddFloat32
	AddFloat64

	SubInt // n1 n2 -- diff
	SubInt8
	SubInt16
	SubInt32
	SubInt64
	SubUint
	SubUint8
	SubUint16
	SubUint32
	SubUint64
	SubFloat32
	SubFloat64

	MulInt // n1 n2 -- prod
	MulInt8
	MulInt16
	MulInt32
	MulInt64
	MulUint
	MulUint8
	MulUint16
	MulUint32
	MulUint64
	MulFloat32
	MulFloat64

	NegInt // n -- -n
	NegInt8
	NegInt16
	NegInt32
	NegInt64
	NegUint
	NegUint8
	NegUint16
	NegUint32
	NegUint64
	NegFloat32
	NegFloat64

	GreaterInt // n1 n2 -- cond
	GreaterInt8
	GreaterInt16
	GreaterInt32
	GreaterInt64
	GreaterUint
	GreaterUint8
	GreaterUint16
	GreaterUint32
	GreaterUint64
	GreaterFloat32
	GreaterFloat64

	LowerInt // n1 n2 -- cond
	LowerInt8
	LowerInt16
	LowerInt32
	LowerInt64
	LowerUint
	LowerUint8
	LowerUint16
	LowerUint32
	LowerUint64
	LowerFloat32
	LowerFloat64

	DivInt // n1 n2 -- quot
	DivInt8
	DivInt16
	DivInt32
	DivInt64
	DivUint
	DivUint8
	DivUint16
	DivUint32
	DivUint64
	DivFloat32
	DivFloat64

	RemInt // n1 n2 -- rem (integer only)
	RemInt8
	RemInt16
	RemInt32
	RemInt64
	RemUint
	RemUint8
	RemUint16
	RemUint32
	RemUint64
	RemFloat32 // unused, but keeps NumTypes alignment
	RemFloat64 // unused, but keeps NumTypes alignment

	// Bitwise opcodes (generic, operate on raw uint64 bits).
	BitAnd    // n1 n2 -- n1 & n2
	BitOr     // n1 n2 -- n1 | n2
	BitXor    // n1 n2 -- n1 ^ n2
	BitAndNot // n1 n2 -- n1 &^ n2
	BitShl    // n1 n2 -- n1 << n2
	BitShr    // n1 n2 -- n1 >> n2 (arithmetic for signed)
	BitComp   // n -- ^n

	// Bit manipulation opcodes (32-bit and 64-bit variants).
	Clz32    // n -- count ; count leading zeros (32-bit)
	Clz64    // n -- count ; count leading zeros (64-bit)
	Ctz32    // n -- count ; count trailing zeros (32-bit)
	Ctz64    // n -- count ; count trailing zeros (64-bit)
	Popcnt32 // n -- count ; population count (32-bit)
	Popcnt64 // n -- count ; population count (64-bit)
	Rotl32   // n k -- result ; rotate left (32-bit)
	Rotl64   // n k -- result ; rotate left (64-bit)
	Rotr32   // n k -- result ; rotate right (32-bit)
	Rotr64   // n k -- result ; rotate right (64-bit)

	// Float math opcodes (unary: 1 operand; binary: 2 operands).
	AbsFloat32      // n -- |n|
	AbsFloat64      // n -- |n|
	SqrtFloat32     // n -- sqrt(n)
	SqrtFloat64     // n -- sqrt(n)
	CeilFloat32     // n -- ceil(n)
	CeilFloat64     // n -- ceil(n)
	FloorFloat32    // n -- floor(n)
	FloorFloat64    // n -- floor(n)
	TruncFloat32    // n -- trunc(n)
	TruncFloat64    // n -- trunc(n)
	NearestFloat32  // n -- nearest(n)
	NearestFloat64  // n -- nearest(n)
	MinFloat32      // a b -- min(a,b)
	MinFloat64      // a b -- min(a,b)
	MaxFloat32      // a b -- max(a,b)
	MaxFloat64      // a b -- max(a,b)
	CopysignFloat32 // a b -- copysign(a,b)
	CopysignFloat64 // a b -- copysign(a,b)

	// Immediate operand variants: fold Push+BinOp into one instruction.
	// Arg[0] holds the right-hand constant (int, sign-extended to int64).
	AddIntImm      // n -- n+$1
	SubIntImm      // n -- n-$1
	MulIntImm      // n -- n*$1
	GreaterIntImm  // n -- n>$1  (signed)
	GreaterUintImm // n -- n>$1 (unsigned)
	LowerIntImm    // n -- n<$1  (signed)
	LowerUintImm   // n -- n<$1  (unsigned)

	GetGlobal    // -- value ; value = mem[$1] (global variable, syncs num from ref if needed)
	GetLocal     // -- value ; value = mem[$1+fp-1] (local variable, no scope check)
	GetLocalSync // -- value ; value = mem[$1+fp-1] and re-read num from ref (used after AddrLocal)
	NextLocal    // -- ; iterator next, set K (local scope); like Next but scope is always Local
	Next2Local   // -- ; iterator next, set K V (local scope); like Next2 but scope is always Local

	// Fused GetLocal + operation superinstructions.
	// $1 = local offset (as in GetLocal), $2 = immediate operand.
	GetLocal2              // -- v1 v2 ; push two locals: mem[$1+fp-1] then mem[$2+fp-1]
	GetLocalAddIntImm      // -- n+$2 ; push local $1 then add immediate $2
	GetLocalSubIntImm      // -- n-$2 ; push local $1 then subtract immediate $2
	GetLocalMulIntImm      // -- n*$2 ; push local $1 then multiply by immediate $2
	GetLocalLowerIntImm    // -- cond ; push local $1 then compare < immediate $2 (signed)
	GetLocalLowerUintImm   // -- cond ; push local $1 then compare < immediate $2 (unsigned)
	GetLocalGreaterIntImm  // -- cond ; push local $1 then compare > immediate $2 (signed)
	GetLocalGreaterUintImm // -- cond ; push local $1 then compare > immediate $2 (unsigned)
	GetLocalReturn         // -- ; push local $1 then return (nret/frameBase from frame)

	// Fused compare + conditional-jump superinstructions.
	// Only LowerInt variants are needed, compiler rewrites Greater comparisons
	// using the identity: (a > imm) same as !(a < imm+1).
	LowerIntImmJumpFalse         // n -- ; if n >= $2 { ip += $1 } ; sp--
	LowerIntImmJumpTrue          // n -- ; if n < $2 { ip += $1 } ; sp--
	GetLocalLowerIntImmJumpFalse // -- ; if local[$1.lo] >= $2 { ip += $1.hi } ($1 = jumpOff_int16<<16 | localOff_int16, $2 = imm32)
	GetLocalLowerIntImmJumpTrue  // -- ; if local[$1.lo] < $2 { ip += $1.hi } ($1 = jumpOff_int16<<16 | localOff_int16, $2 = imm32)

	// In-place local update super-instructions for `x op= y` and `x op= n`,
	// collapsing the GetLocal2+RHS+SetLocal+Pop sequence. No stack effect.
	AddLocalLocal  // -- ; local[$1] += local[$2]
	SubLocalLocal  // -- ; local[$1] -= local[$2]
	AddLocalIntImm // -- ; local[$1] += $2 (signed, fits int32)
	SubLocalIntImm // -- ; local[$1] -= $2 (signed, fits int32)
	IndexSetBool   // a i -- ; a[i] = bool($1)  (fuses Push/GetGlobal bool + IndexSet + Pop)

	MarkNamedRet // -- ; flag this frame as having captured named returns (set bit in retIPInfo)
)

Byte-code instruction set.

func (Op) String

func (i Op) String() string

type Opaque

type Opaque struct{}

Opaque stands in for an external type which could not be resolved at parse time.

type PanicError added in v0.3.0

type PanicError struct {
	Raw    any          // original panic value
	Pos    Pos          // source position of the panicking instruction
	IP     int          // bytecode IP at panic time
	Frames []StackFrame // captured before frame unwinding
	DI     *DebugInfo   // captured DebugInfo for formatting; may be nil
}

PanicError wraps a raw Go panic that escaped the VM with mvm-level diagnostic context.

func (*PanicError) Error added in v0.3.0

func (e *PanicError) Error() string

Error renders the verbose layout (header + snippet + mvm stack) using the DebugInfo captured at panic time. Falls back to "panic: <raw>" if no DebugInfo was captured.

type Pos

type Pos int32

Pos is the source code position of instruction.

type RuntimeFuncInfo added in v0.2.0

type RuntimeFuncInfo struct {
	Name string
	File string
	Line int
}

RuntimeFuncInfo holds the synthesized Name/FileLine for a *runtime.Func sentinel allocated by the bridged runtime.FuncForPC. The sentinel is a fresh &runtime.Func{} pointer; the host runtime never sees it because nativeMethodLookup intercepts Name and FileLine before any host method runs.

func LookupRuntimeFunc added in v0.2.0

func LookupRuntimeFunc(rf *runtime.Func) *RuntimeFuncInfo

LookupRuntimeFunc returns the registered metadata for rf, or nil if rf was not produced by the mvm bridge.

func LookupRuntimeFuncByPC added in v0.2.0

func LookupRuntimeFuncByPC(pc uintptr) (*runtime.Func, *RuntimeFuncInfo)

LookupRuntimeFuncByPC resolves a host-style PC value to a registered sentinel and its metadata. It tries pc-1 first (pkg/errors stores PC = sentinel+1 and looks up via pc-1) and falls back to pc for callers that skipped the +1 convention. Returns nil/nil when pc does not name a virtualized frame.

Compared to the previous (*runtime.Func)(unsafe.Pointer(pc - 1)) idiom, this form does no pointer arithmetic on a uintptr that came from a host pointer, so it is safe under -race / checkptr.

type SelectCaseInfo

type SelectCaseInfo struct {
	Dir    reflect.SelectDir // SelectSend, SelectRecv, or SelectDefault
	Slot   int               // local/global index for received value (-1 if unused)
	OkSlot int               // local/global index for ok bool (-1 if unused)
	Local  bool              // true if slots are local (frame-relative), false for global
}

SelectCaseInfo describes one case of a select statement.

type SelectMeta

type SelectMeta struct {
	Cases    []SelectCaseInfo
	TotalPop int // precomputed number of stack slots consumed by channel/value entries
}

SelectMeta holds metadata for a select statement, stored in the data section.

type StackFrame added in v0.2.0

type StackFrame struct {
	IP       int    // bytecode position within the frame's function
	Pos      Pos    // source position from m.code[IP], 0 if out of range
	TopLevel bool   // synthetic frame for the top-level entry sequence (init / Eval driver)
	Native   bool   // synthetic boundary row: a native call separating two interpreted segments
	Name     string // for Native rows, the native func mvm invoked at the boundary
}

StackFrame is a single entry yielded by WalkCallStack.

type Type

type Type = mtype.Type

Type is the symbolic type representation, re-exported from mtype.

type TypeElem

type TypeElem = mtype.TypeElem

TypeElem is re-exported from mtype.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value is the VM runtime value. Numeric types (bool, int*, uint*, float*) store their value inline in num. ref carries reflect.Zero(t) for type metadata on numeric types. Composite types (string, slice, map, struct, ptr, func, interface) use ref.

func FromReflect

func FromReflect(rv reflect.Value) Value

FromReflect wraps a reflect.Value into a Value.

func NewValue

func NewValue(typ reflect.Type, arg ...int) Value

NewValue returns a zero value for the specified reflect.Type.

func TypeValue

func TypeValue(typ reflect.Type) Value

TypeValue returns a zero value for use as a type descriptor in the data table. Preserves the exact reflect.Type for all kinds so opcodes like MkChan can recover it via ref.Type().

func ValueOf

func ValueOf(v any) Value

ValueOf returns the runtime value of v.

func (Value) Addr

func (v Value) Addr() reflect.Value

Addr returns a pointer value representing the address of v.

func (Value) Bool

func (v Value) Bool() bool

Bool returns v's value as bool.

func (Value) CanAddr

func (v Value) CanAddr() bool

CanAddr reports whether the value is addressable.

func (Value) CanInt

func (v Value) CanInt() bool

CanInt reports whether Int can be called without panicking.

func (Value) CopyArray

func (v Value) CopyArray() Value

CopyArray returns a Value holding a copy of the array in v, so that range iterates over a snapshot (Go spec: range over array uses a copy).

func (Value) Elem

func (v Value) Elem() reflect.Value

Elem returns the value that the interface v contains or the pointer v points to.

func (Value) Equal

func (v Value) Equal(u Value) bool

Equal reports whether v is equal to u.

func (Value) Field

func (v Value) Field(i int) reflect.Value

Field returns v's i'th field.

func (Value) FieldByIndex

func (v Value) FieldByIndex(index []int) reflect.Value

FieldByIndex returns the nested field corresponding to index.

func (Value) Float

func (v Value) Float() float64

Float returns v's value as float64.

func (Value) IfaceVal

func (v Value) IfaceVal() Iface

IfaceVal extracts the Iface from a boxed interface value.

func (Value) Index

func (v Value) Index(i int) reflect.Value

Index returns v's i'th element.

func (Value) Int

func (v Value) Int() int64

Int returns v's value as int64.

func (Value) Interface

func (v Value) Interface() any

Interface returns v's value as interface{}.

func (Value) IsIface

func (v Value) IsIface() bool

IsIface reports whether v holds a boxed interface value.

func (Value) IsValid

func (v Value) IsValid() bool

IsValid reports whether v represents a value (ref is set).

func (Value) Kind

func (v Value) Kind() reflect.Kind

Kind returns the reflect.Kind of the value.

func (Value) Len

func (v Value) Len() int

Len returns v's length.

func (Value) MapIndex

func (v Value) MapIndex(key reflect.Value) reflect.Value

MapIndex returns the value associated with key in the map v.

func (Value) Reflect

func (v Value) Reflect() reflect.Value

Reflect reconstructs a reflect.Value from an inline numeric Value. For composite types, returns ref directly. This may allocate for numeric types; use only at reflect boundaries.

func (Value) Seq

func (v Value) Seq() iter.Seq[reflect.Value]

Seq returns a range-over iterator for the value v.

func (Value) Seq2

func (v Value) Seq2() iter.Seq2[reflect.Value, reflect.Value]

Seq2 returns a range-over-2 iterator for the value v.

func (Value) Set

func (v Value) Set(x reflect.Value)

Set assigns x to the value v.

func (Value) SetMapIndex

func (v Value) SetMapIndex(key, elem reflect.Value)

SetMapIndex sets the element associated with key in the map v.

func (Value) Slice

func (v Value) Slice(i, j int) reflect.Value

Slice returns v[i:j].

func (Value) Slice3

func (v Value) Slice3(i, j, k int) reflect.Value

Slice3 returns v[i:j:k].

func (Value) Type

func (v Value) Type() reflect.Type

Type returns the reflect.Type of the value.

func (Value) Uint

func (v Value) Uint() uint64

Uint returns v's value as uint64.

func (Value) UnwrapType

func (v Value) UnwrapType() (reflect.Type, bool)

UnwrapType checks if v encodes a stdlib type as (*T)(nil). If so, it returns the underlying reflect.Type and true.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL