Skip to main content

Op

Enum Op 

Source
pub enum Op {
Show 82 variants PushConst(u32), Pop, Dup, LoadLocal(u16), StoreLocal(u16), MakeRecord { shape_idx: u32, field_count: u16, }, AllocStackRecord { shape_idx: u32, field_count: u16, }, AllocArenaRecord { shape_idx: u32, field_count: u16, }, MakeTuple(u16), AllocStackTuple { arity: u16, }, AllocArenaTuple { arity: u16, }, MakeList(u32), MakeVariant { name_idx: u32, arity: u16, }, GetField { name_idx: u32, site_idx: u32, }, GetElem(u16), TestVariant(u32), GetVariant(u32), GetVariantArg(u16), GetListLen, GetListElem(u32), ListAppend, GetListElemDyn, Jump(i32), JumpIf(i32), JumpIfNot(i32), Call { fn_id: u32, arity: u16, node_id_idx: u32, }, TailCall { fn_id: u32, arity: u16, node_id_idx: u32, }, MakeClosure { fn_id: u32, capture_count: u16, }, CallClosure { arity: u16, node_id_idx: u32, }, SortByKey { node_id_idx: u32, }, ParallelMap { node_id_idx: u32, }, ListMap { node_id_idx: u32, }, ListFilter { node_id_idx: u32, }, ListFold { node_id_idx: u32, }, EffectCall { kind_idx: u32, op_idx: u32, arity: u16, node_id_idx: u32, }, Return, Panic(u32), IntAdd, IntSub, IntMul, IntDiv, IntMod, IntNeg, IntEq, IntLt, IntLe, FloatAdd, FloatSub, FloatMul, FloatDiv, FloatNeg, FloatEq, FloatLt, FloatLe, NumAdd, NumSub, NumMul, NumDiv, NumMod, NumNeg, NumEq, NumLt, NumLe, BoolAnd, BoolOr, BoolNot, StrConcat, StrLen, StrEq, BytesLen, BytesEq, LoadLocalAddIntConst { local_idx: u16, imm_const_idx: u32, }, LoadLocalAddIntConstStoreLocal { src: u16, imm_const_idx: u32, dest: u16, }, LoadLocalAddLocal { lhs_idx: u16, rhs_idx: u16, }, LoadLocalSubLocal { lhs_idx: u16, rhs_idx: u16, }, LoadLocalMulLocal { lhs_idx: u16, rhs_idx: u16, }, LoadLocalEqIntConstJumpIfNot { local_idx: u16, imm_const_idx: u32, jump_offset: i32, }, LoadLocalStoreEqIntConstJumpIfNot { src: u16, dst: u16, imm_const_idx: u32, jump_offset: i32, }, LoadLocalGetFieldAdd { local_idx: u16, name_idx: u32, site_idx: u32, }, LoadLocalGetFieldSub { local_idx: u16, name_idx: u32, site_idx: u32, }, LoadLocalGetFieldMul { local_idx: u16, name_idx: u32, site_idx: u32, }, LoadLocalGetField { local_idx: u16, name_idx: u32, site_idx: u32, },
}

Variants§

§

PushConst(u32)

§

Pop

§

Dup

§

LoadLocal(u16)

§

StoreLocal(u16)

§

MakeRecord

Builds a record by interning its field-name shape in Program.record_shapes (#461). shape_idx indexes that side-table; field_count is shape.len() cached inline so the stack-effect verifier can compute its delta without needing a Program reference. The VM pops field_count values off the stack and pairs them with Program.record_shapes[shape_idx].

Externalizing the field-name vec is what lets Op be Copy, which is the precondition for direct-threaded dispatch (code[pc] becomes a register-sized read instead of an every-step Vec clone).

Fields

§shape_idx: u32
§field_count: u16
§

AllocStackRecord

Stack-allocated record (#464). Same shape semantics as MakeRecord — pops field_count field values and pairs them with Program.record_shapes[shape_idx] — but the field values are stored in the current frame’s slab inside the VM’s stack_record_arena, not in a heap-allocated IndexMap. The stack pushes a Value::StackRecord whose slab_start indexes into the arena.

Emitted by the compiler in place of MakeRecord at sites that escape::build_escape_index proves do not escape the frame (returned, captured, stored into another aggregate, or passed to a call). Runtime fallback: when the frame’s stack_record_budget_remaining is exhausted, the op silently degrades to the heap path (identical observable effect to MakeRecord), so a single function can mix stack and heap records without compile-time partitioning.

body_hash stability (#222): canonical encoding decodes this op back to the historical {"MakeRecord":{"field_name_indices": [...]}} form, so closure identity is invariant under the step-2 lowering.

Fields

§shape_idx: u32
§field_count: u16
§

AllocArenaRecord

Request-scoped arena record (#463 slice 2a). Same shape semantics as MakeRecord and AllocStackRecord — pops field_count field values, pairs them with Program.record_shapes[shape_idx], and pushes a record handle — but the fields live in the VM’s request-scoped arena_slab, not the per-frame stack-record arena, so the value can outlive the allocating frame as long as the surrounding request scope (opened by EffectHandler::enter_request_scope) is still active. The resulting Value::ArenaRecord indexes the slab.

Emitted (slice 2b) at sites arena::build_arena_index proves do not escape the request scope. Runtime fallback: when no scope is active (e.g. a non-handler context calls a function that was compiled with arena lowering), the op silently degrades to the MakeRecord heap path — identical observable effect.

body_hash stability (#222): canonical encoding decodes back to {"MakeRecord":{"field_name_indices":[...]}}, so closure identity is invariant under the lowering, mirroring AllocStackRecord.

Fields

§shape_idx: u32
§field_count: u16
§

MakeTuple(u16)

§

AllocStackTuple

Frame-local tuple (#464 tuple codegen). Stack-alloc analogue of MakeTuple: pops arity values into the VM’s stack-record arena and pushes a Value::StackTuple whose slab_start indexes the arena. Emitted by the compiler in place of MakeTuple at sites escape::build_escape_index proves do not escape the frame. Runtime fallback to the heap Value::Tuple path when the frame’s stack-record budget is exhausted — identical observable effect, so stack and heap tuples can mix within one function. body_hash stability (#222): canonical encoding decodes this op back to MakeTuple(arity), so closure identity is invariant under the lowering.

Fields

§arity: u16
§

AllocArenaTuple

Request-scoped arena tuple (#463 slice 2a). Tuple analogue of AllocArenaRecord: pops arity values into the VM’s request-scoped arena_slab and pushes a Value::ArenaTuple handle. Same fallback rule (no active scope → MakeTuple heap path) and same body_hash invariance (decodes back to MakeTuple(arity)).

Fields

§arity: u16
§

MakeList(u32)

§

MakeVariant

Fields

§name_idx: u32
§arity: u16
§

GetField

Record field access. name_idx indexes a Const::FieldName in the constant pool — the field to read. site_idx is a stable per-function index assigned by the compiler at emit time (#462 slice 1), keyed into the per-fn inline-cache table in the VM. Replaces the pre-#462 (fn_id << 32 | pc) IC key so the cache survives the future dispatch rewrite (#461) and a JIT (#465). body_hash stability: the canonical encoding drops site_idx and serializes as the historical GetField(u32) tuple form, so closure identity (#222) is unchanged.

Fields

§name_idx: u32
§site_idx: u32
§

GetElem(u16)

§

TestVariant(u32)

§

GetVariant(u32)

§

GetVariantArg(u16)

§

GetListLen

§

GetListElem(u32)

§

ListAppend

Pop [list, value]; push list with value appended.

§

GetListElemDyn

Pop list; push it indexed by the integer on top. Stack: [list, idx] → [list[idx]]. (Like GetListElem(u32) but the index is dynamic.)

§

Jump(i32)

§

JumpIf(i32)

§

JumpIfNot(i32)

§

Call

Fields

§fn_id: u32
§arity: u16
§node_id_idx: u32
§

TailCall

Fields

§fn_id: u32
§arity: u16
§node_id_idx: u32
§

MakeClosure

Build a Value::Closure: pop capture_count values (in order) and pair them with fn_id.

Fields

§fn_id: u32
§capture_count: u16
§

CallClosure

Call a closure: pop arity args + 1 closure (top of stack), invoke.

Fields

§arity: u16
§node_id_idx: u32
§

SortByKey

Stable sort-by-key (#338). Stack: [xs, f] (xs underneath). Pops the key-fn f and the list xs, applies f to each element to derive a sortable key, returns the list reordered so keys ascend. Keys must be one of Int / Float / Str; other key types pair-wise compare as equal (preserving insertion order). node_id_idx is the originating NodeId.

Fields

§node_id_idx: u32
§

ParallelMap

Parallel map (#305 slice 1). Stack: [xs, f] (xs underneath). Pops the closure f and the list xs, applies f to each element in parallel via OS threads, pushes the result list in input order. node_id_idx is the originating NodeId for trace keying. The pool size is capped by LEX_PAR_MAX_CONCURRENCY (default = available CPU cores).

Slice 1 limitation: closures invoking effects fail at runtime with VmError::Effect. The per-thread effect handler split is queued as slice 2.

Fields

§node_id_idx: u32
§

ListMap

Map a list (#464 list-builder fast path). Stack: [xs, f] (xs underneath). Pops the closure f and list xs, applies f to each element, pushes the result list. Native opcode (mirrors SortByKey/ParallelMap) rather than an inlined bytecode loop: the loop form re-LoadLocal’d (cloned) the whole input and accumulator lists each iteration — O(n²) — whereas the VM here owns xs and builds the output with one pre-sized allocation.

Fields

§node_id_idx: u32
§

ListFilter

Filter a list (#464). Stack: [xs, pred]. Pops pred and xs, keeps the elements for which pred(x) is true. Native, same rationale as ListMap.

Fields

§node_id_idx: u32
§

ListFold

Left-fold a list (#464). Stack: [xs, init, f] (xs deepest). Pops f, init, xs; threads acc = f(acc, x) from init over the elements; pushes the final acc. Native, same rationale as ListMap.

Fields

§node_id_idx: u32
§

EffectCall

EFFECT_CALL <effect_kind_const_idx> <op_name_const_idx> <arity>. Pops arity args, dispatches to a host effect handler, pushes result. node_id_idx points to a Const::NodeId for trace keying.

Fields

§kind_idx: u32
§op_idx: u32
§arity: u16
§node_id_idx: u32
§

Return

§

Panic(u32)

§

IntAdd

§

IntSub

§

IntMul

§

IntDiv

§

IntMod

§

IntNeg

§

IntEq

§

IntLt

§

IntLe

§

FloatAdd

§

FloatSub

§

FloatMul

§

FloatDiv

§

FloatNeg

§

FloatEq

§

FloatLt

§

FloatLe

§

NumAdd

§

NumSub

§

NumMul

§

NumDiv

§

NumMod

§

NumNeg

§

NumEq

§

NumLt

§

NumLe

§

BoolAnd

§

BoolOr

§

BoolNot

§

StrConcat

§

StrLen

§

StrEq

§

BytesLen

§

BytesEq

§

LoadLocalAddIntConst

Fused LoadLocal(local_idx) + PushConst(imm_const_idx) + IntAdd. imm_const_idx must point to a Const::Int. The dispatch arm reads the local, adds the constant, pushes the result, and advances pc by 3 (past this op and the two inert PushConst + IntAdd slots that follow). For body_hash stability (#222) the canonical encoding decomposes this op back to a standalone LoadLocal(local_idx) at hash time; the unchanged PushConst / IntAdd at the next two slots hash normally, so the total bytes match pre-fusion.

Fields

§local_idx: u16
§imm_const_idx: u32
§

LoadLocalAddIntConstStoreLocal

Fused LoadLocal(src) + PushConst(imm_const_idx) + IntAdd + StoreLocal(dest) (#461 superinstruction slice 2). Bypasses the value stack entirely: reads locals[src], adds the Int constant, writes locals[dest]. Advances pc by 4. Stack delta: 0.

The peephole pass that emits this op runs after slice 1, looking for [LoadLocalAddIntConst, ., ., StoreLocal] where the middle two slots are slice-1 tombstones (the original PushConst + IntAdd). The verifier and the body-hash decoder both treat the 3 following slots as tombstones owned by this op.

Fields

§src: u16
§imm_const_idx: u32
§dest: u16
§

LoadLocalAddLocal

Fused LoadLocal(lhs_idx) + LoadLocal(rhs_idx) + IntAdd (#461 superinstruction slice 3). The binary-op-on-two-locals idiom — fires on any a + b where both operands are statically-typed Int locals (e.g. acc + n in tail-recursive accumulator loops). Reads locals[lhs_idx] and locals[rhs_idx], pushes the sum, advances pc by 3. Stack delta: +1.

body_hash stability (#222): canonical encoding decomposes back to a standalone LoadLocal(lhs_idx). The unchanged LoadLocal(rhs_idx) + IntAdd tombstones at pc+1 and pc+2 hash normally, so the total bytes match the pre-fusion form. Verifier walks the tombstones as if live: their deltas (+1 LoadLocal, -1 IntAdd) cancel, matching the unfused depth at pc+3.

Fields

§lhs_idx: u16
§rhs_idx: u16
§

LoadLocalSubLocal

Fused LoadLocal(lhs_idx) + LoadLocal(rhs_idx) + IntSub (#461 superinstruction slice 4). Sibling of LoadLocalAddLocal for the typed Int subtraction binop — fires on any a - b where both operands are Int locals. Reads locals[lhs_idx] and locals[rhs_idx], pushes lhs - rhs, advances pc by 3. Stack delta: +1. Tombstone + body-hash story matches LoadLocalAddLocal exactly.

Fields

§lhs_idx: u16
§rhs_idx: u16
§

LoadLocalMulLocal

Fused LoadLocal(lhs_idx) + LoadLocal(rhs_idx) + IntMul (#461 superinstruction slice 4). Sibling of LoadLocalAddLocal for the typed Int multiplication binop. Same shape: reads the two Int locals, pushes lhs * rhs, advances pc by 3. Stack delta: +1. Tombstone + body-hash story matches LoadLocalAddLocal exactly.

Fields

§lhs_idx: u16
§rhs_idx: u16
§

LoadLocalEqIntConstJumpIfNot

Fused LoadLocal(local_idx) + PushConst(imm_const_idx) + IntEq + JumpIfNot(offset) (#461 superinstruction slice 5, pattern-match arm-test idiom). Fires on every numeric pattern arm test — match n { 0 => acc; _ => recurse } and the cascade of integer-literal arms in any pattern match — after compile_pattern_test lowers the historical NumEq to IntEq for Int-literal patterns. Reads the local, compares against the Int constant; if equal, advances pc by 4 (past the 3 tombstones, into the arm body); if not equal, jumps to pc + 4 + jump_offset (the JumpIfNot’s original target — the next arm test or the panic-no-match block). Stack delta: 0 (original sequence had +1, +1, -1, -1).

Jump-aware peephole — slice 5 is the first fusion that absorbs a control-flow op. The verifier walks the fused op with both fall-through and branch successors, skipping past the trailing three tombstones (mirroring slice 2’s 4-slot pattern). body_hash decodes back to a standalone LoadLocal(local_idx); the trailing primitive ops stay in the code stream as tombstones and hash normally — so closure identity (#222) stays invariant.

Fields

§local_idx: u16
§imm_const_idx: u32
§jump_offset: i32
§

LoadLocalStoreEqIntConstJumpIfNot

Fused LoadLocal(src) + StoreLocal(dst) + LoadLocalEqIntConstJumpIfNot { local_idx: dst, ... } (#461 superinstruction slice 6). Absorbs the match-scrutinee dance — the LoadLocal + StoreLocal the compiler emits to bind the match expression to a fresh local before each arm’s pattern test reads it back. Reads locals[src], mirrors the original StoreLocal(dst) by writing the same value into locals[dst] (so the SECOND and later arm tests in the same match still see the scrutinee at the expected slot), then compares against the constant. Equal → advance pc by 6 (skip past the 5 tombstones — original StoreLocal + slice-5 fused op + slice-5’s 3 trailing tombstones). Not equal → jump to pc + 6 + jump_offset (the original JumpIfNot’s target; jump_offset is copied unchanged from the slice-5 op). Stack delta: 0.

body_hash decodes back to a standalone LoadLocal(src); the trailing 5 ops (StoreLocal, the slice-5 fused op decoded as LoadLocal(dst), PushConst, IntEq, JumpIfNot) stay in the code stream as tombstones and hash normally.

Fields

§src: u16
§dst: u16
§imm_const_idx: u32
§jump_offset: i32
§

LoadLocalGetFieldAdd

Fused LoadLocal(local_idx) + GetField{name_idx, site_idx} + IntAdd (#461 superinstruction slice 7). Fires on the acc + r.field accumulator-with-field-read idiom — the shape any expr + record.field lowers to when the LHS is already on the stack and the RHS is a same-frame record field. After #464 step 2 dropped the IndexMap allocation from hot-path records, this fusion is the next dispatch- overhead bottleneck on the response_build profile.

Dispatch: pops the prior stack top (an Int), reads locals[local_idx], performs the polymorphic-IC GetField lookup keyed by (fn_id, site_idx) against name_idx, adds the field value to the popped Int, pushes the result, advances pc by 3.

Stack delta: +1 (matches a bare LoadLocal). The trailing GetField (delta 0) and IntAdd (delta -1) stay in the code stream as inert tombstones; the verifier walks them as live and their cancelling deltas leave depth at pc+3 matching the unfused form.

body_hash stability (#222): canonical encoding decomposes to a standalone LoadLocal(local_idx); the unchanged GetField and IntAdd at pc+1 and pc+2 hash normally, so the total bytes match pre-fusion. The trailing GetField’s own body-hash decoding (which strips site_idx) means the hash is unchanged across recompiles where IC-site numbering differs.

Safety: the trailing two slots must not be jump targets (standard tombstone rule). The first slot may be a target — the fused op there is live.

Fields

§local_idx: u16
§name_idx: u32
§site_idx: u32
§

LoadLocalGetFieldSub

Slice 8 of #461: IntSub / IntMul siblings of slice 7’s LoadLocalGetFieldAdd. Fuse LoadLocal + GetField + IntSub and LoadLocal + GetField + IntMul respectively — the acc - r.field and acc * r.field idioms. Same tombstone, jump-safety, body-hash (decode to LoadLocal(local_idx)), and verifier (+1 delta) story as slice 7.

IntSub is not commutative: the unfused sequence leaves the field value on top, so IntSub’s deeper-minus-top semantics give acc - field. The fused dispatch preserves that order.

Fields

§local_idx: u16
§name_idx: u32
§site_idx: u32
§

LoadLocalGetFieldMul

Fields

§local_idx: u16
§name_idx: u32
§site_idx: u32
§

LoadLocalGetField

Slice 9 of #461: fuse LoadLocal(local_idx) + GetField{name_idx, site_idx} — the bare record.field read, the single most common field-access shape. Unlike slices 7/8 there’s no arithmetic terminator; this is a 2-op window.

The win is allocation, not just dispatch: the unfused pair LoadLocal clones the entire record onto the value stack (a Box<IndexMap> for a heap record), GetField pops it, reads one field, and drops the rest. The fused op reads the field out of the local by reference (read_local_record_field) and clones only that one value. On the response_build profile the whole-record clone+drop of the returned Response (r.total) was the dominant malloc source.

Stack delta: +1 (LoadLocal +1, GetField 0). The trailing GetField stays as a single inert tombstone (delta 0); the verifier walks it, leaving depth at pc+2 matching the unfused [LoadLocal, GetField] pair. body_hash decodes to a standalone LoadLocal(local_idx); the trailing GetField hashes normally.

Safety: the trailing slot (the original GetField) must not be a jump target. The first slot may be.

Fields

§local_idx: u16
§name_idx: u32
§site_idx: u32

Trait Implementations§

Source§

impl Clone for Op

Source§

fn clone(&self) -> Op

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Op

Source§

impl Debug for Op

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Op

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Op

Source§

fn eq(&self, other: &Op) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for Op

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Op

Auto Trait Implementations§

§

impl Freeze for Op

§

impl RefUnwindSafe for Op

§

impl Send for Op

§

impl Sync for Op

§

impl Unpin for Op

§

impl UnsafeUnpin for Op

§

impl UnwindSafe for Op

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.