sema_vm/opcodes.rs
1/// Bytecode opcodes for the Sema VM.
2///
3/// Stack-based: operands are pushed/popped from the value stack.
4/// Variable-length encoding: opcode (1 byte) + operands (u16/u32/i32).
5#[repr(u8)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Op {
8 // Constants & stack
9 Const, // u16 const_index → push constants[i]
10 Nil, // push nil
11 True, // push #t
12 False, // push #f
13 Pop, // discard TOS
14 Dup, // duplicate TOS
15
16 // Locals (slot-addressed within call frame)
17 LoadLocal, // u16 slot → push locals[slot]
18 StoreLocal, // u16 slot → locals[slot] = pop
19
20 // Upvalues (captured variables from enclosing scopes)
21 LoadUpvalue, // u16 index → push upvalues[i].get()
22 StoreUpvalue, // u16 index → upvalues[i].set(pop)
23
24 // Globals (module-level bindings, keyed by Spur)
25 LoadGlobal, // u32 spur → push globals[spur]
26 StoreGlobal, // u32 spur → globals[spur] = pop
27 DefineGlobal, // u32 spur → globals[spur] = pop (define, not set!)
28
29 // Control flow
30 Jump, // i32 relative offset
31 JumpIfFalse, // i32 relative offset (pop condition)
32 JumpIfTrue, // i32 relative offset (pop condition)
33
34 // Function calls
35 Call, // u16 argc → call TOS-argc with argc args
36 TailCall, // u16 argc → tail call (reuse frame)
37 Return, // return TOS
38
39 // Closures
40 MakeClosure, // u16 func_id, u16 n_upvalues, then n * (u8 is_local, u16 idx)
41
42 // Native function calls (fast path)
43 CallNative, // u16 native_id, u16 argc
44
45 // Data constructors
46 MakeList, // u16 n → pop n values, push list
47 MakeVector, // u16 n → pop n values, push vector
48 MakeMap, // u16 n_pairs → pop 2n values, push map
49 MakeHashMap, // u16 n_pairs → pop 2n values, push hashmap
50
51 // Exception handling
52 Throw, // pop value, throw as exception
53
54 // Generic arithmetic & comparison
55 Add,
56 Sub,
57 Mul,
58 Div,
59 Negate,
60 Not,
61 Eq,
62 Lt,
63 Gt,
64 Le,
65 Ge,
66
67 // Specialized int arithmetic (fast paths)
68 AddInt,
69 SubInt,
70 MulInt,
71 LtInt,
72 EqInt,
73
74 // Specialized zero-operand locals (most common slots)
75 LoadLocal0, // = 42
76 LoadLocal1, // = 43
77 LoadLocal2, // = 44
78 LoadLocal3, // = 45
79}
80
81impl Op {
82 pub fn from_u8(byte: u8) -> Option<Op> {
83 match byte {
84 0 => Some(Op::Const),
85 1 => Some(Op::Nil),
86 2 => Some(Op::True),
87 3 => Some(Op::False),
88 4 => Some(Op::Pop),
89 5 => Some(Op::Dup),
90 6 => Some(Op::LoadLocal),
91 7 => Some(Op::StoreLocal),
92 8 => Some(Op::LoadUpvalue),
93 9 => Some(Op::StoreUpvalue),
94 10 => Some(Op::LoadGlobal),
95 11 => Some(Op::StoreGlobal),
96 12 => Some(Op::DefineGlobal),
97 13 => Some(Op::Jump),
98 14 => Some(Op::JumpIfFalse),
99 15 => Some(Op::JumpIfTrue),
100 16 => Some(Op::Call),
101 17 => Some(Op::TailCall),
102 18 => Some(Op::Return),
103 19 => Some(Op::MakeClosure),
104 20 => Some(Op::CallNative),
105 21 => Some(Op::MakeList),
106 22 => Some(Op::MakeVector),
107 23 => Some(Op::MakeMap),
108 24 => Some(Op::MakeHashMap),
109 25 => Some(Op::Throw),
110 26 => Some(Op::Add),
111 27 => Some(Op::Sub),
112 28 => Some(Op::Mul),
113 29 => Some(Op::Div),
114 30 => Some(Op::Negate),
115 31 => Some(Op::Not),
116 32 => Some(Op::Eq),
117 33 => Some(Op::Lt),
118 34 => Some(Op::Gt),
119 35 => Some(Op::Le),
120 36 => Some(Op::Ge),
121 37 => Some(Op::AddInt),
122 38 => Some(Op::SubInt),
123 39 => Some(Op::MulInt),
124 40 => Some(Op::LtInt),
125 41 => Some(Op::EqInt),
126 42 => Some(Op::LoadLocal0),
127 43 => Some(Op::LoadLocal1),
128 44 => Some(Op::LoadLocal2),
129 45 => Some(Op::LoadLocal3),
130 _ => None,
131 }
132 }
133}