1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum OperandKind {
13 ImmediateU8,
14 ImmediateU16,
15 BuiltinIdU64,
16 ConstantU16,
17 StringConstantU16,
18 LocalU16,
19 FunctionU16,
20 JumpU16,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Portability {
25 Executable,
26 Deferred,
27}
28
29impl OperandKind {
30 pub const fn width(self) -> usize {
31 match self {
32 Self::ImmediateU8 => 1,
33 Self::ImmediateU16
34 | Self::ConstantU16
35 | Self::StringConstantU16
36 | Self::LocalU16
37 | Self::FunctionU16
38 | Self::JumpU16 => 2,
39 Self::BuiltinIdU64 => 8,
40 }
41 }
42
43 const fn abi_tag(self) -> u8 {
44 match self {
45 Self::ImmediateU8 => 0,
46 Self::ImmediateU16 => 1,
47 Self::BuiltinIdU64 => 2,
48 Self::ConstantU16 => 3,
49 Self::LocalU16 => 4,
50 Self::FunctionU16 => 5,
51 Self::JumpU16 => 6,
52 Self::StringConstantU16 => 7,
53 }
54 }
55}
56
57macro_rules! define_opcodes {
58 ($($name:ident = $byte:literal => [$($operand:ident),* $(,)?]),+ $(,)?) => {
59 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
60 #[repr(u8)]
61 pub enum Op { $($name = $byte),+ }
62
63 impl Op {
64 pub const ALL: &'static [Self] = &[$(Self::$name),+];
65 pub const COUNT: usize = Self::ALL.len();
66
67 #[inline]
68 pub fn from_byte(byte: u8) -> Option<Self> {
69 Self::ALL.get(byte as usize).copied()
70 }
71
72 pub const fn name(self) -> &'static str {
73 match self { $(Self::$name => stringify!($name)),+ }
74 }
75
76 pub const fn operands(self) -> &'static [OperandKind] {
77 match self {
78 $(Self::$name => &[$(OperandKind::$operand),*]),+
79 }
80 }
81
82 pub const fn instruction_len(self) -> usize {
83 let operands = self.operands();
84 let mut index = 0;
85 let mut width = 1;
86 while index < operands.len() {
87 width += operands[index].width();
88 index += 1;
89 }
90 width
91 }
92 }
93 };
94}
95
96define_opcodes! {
97 Constant = 0 => [ConstantU16],
98 Nil = 1 => [],
99 True = 2 => [],
100 False = 3 => [],
101 RootHarness = 4 => [],
102 GetVar = 5 => [StringConstantU16],
103 DefLet = 6 => [StringConstantU16],
104 DefVar = 7 => [StringConstantU16],
105 DefCell = 8 => [StringConstantU16],
106 SetVar = 9 => [StringConstantU16],
107 PushScope = 10 => [],
108 PopScope = 11 => [],
109 Add = 12 => [],
110 Sub = 13 => [],
111 Mul = 14 => [],
112 Div = 15 => [],
113 Mod = 16 => [],
114 Pow = 17 => [],
115 Negate = 18 => [],
116 Equal = 19 => [],
117 NotEqual = 20 => [],
118 Less = 21 => [],
119 Greater = 22 => [],
120 LessEqual = 23 => [],
121 GreaterEqual = 24 => [],
122 Not = 25 => [],
123 Jump = 26 => [JumpU16],
124 JumpIfFalse = 27 => [JumpU16],
125 JumpIfTrue = 28 => [JumpU16],
126 Pop = 29 => [],
127 Call = 30 => [ImmediateU8],
128 TailCall = 31 => [ImmediateU8],
129 Return = 32 => [],
130 Closure = 33 => [FunctionU16],
131 BuildList = 34 => [ImmediateU16],
132 BuildDict = 35 => [ImmediateU16],
133 Subscript = 36 => [],
134 SubscriptOpt = 37 => [],
135 Slice = 38 => [],
136 GetProperty = 39 => [StringConstantU16],
137 GetPropertyOpt = 40 => [StringConstantU16],
138 SetProperty = 41 => [StringConstantU16, StringConstantU16],
139 SetSubscript = 42 => [StringConstantU16],
140 SetLocalSlotProperty = 43 => [StringConstantU16, LocalU16],
141 SetLocalSlotSubscript = 44 => [LocalU16],
142 MethodCall = 45 => [StringConstantU16, ImmediateU8],
143 MethodCallOpt = 46 => [StringConstantU16, ImmediateU8],
144 Concat = 47 => [ImmediateU16],
145 IterInit = 48 => [],
146 IterNext = 49 => [JumpU16],
147 Pipe = 50 => [],
148 Throw = 51 => [],
149 TryCatchSetup = 52 => [JumpU16, StringConstantU16],
150 PopHandler = 53 => [],
151 Parallel = 54 => [],
152 ParallelMap = 55 => [],
153 ParallelMapStream = 56 => [],
154 ParallelSettle = 57 => [],
155 Spawn = 58 => [],
156 SyncMutexEnter = 59 => [],
157 SyncMutexEnterKeyed = 60 => [],
158 TaskScopeEnter = 61 => [],
159 TaskScopeExit = 62 => [],
160 Import = 63 => [StringConstantU16],
161 SelectiveImport = 64 => [StringConstantU16, StringConstantU16],
162 NamespaceImport = 65 => [StringConstantU16, StringConstantU16],
163 DeadlineSetup = 66 => [],
164 DeadlineEnd = 67 => [],
165 BuildEnum = 68 => [StringConstantU16, StringConstantU16, ImmediateU16],
166 MatchEnum = 69 => [StringConstantU16, StringConstantU16],
167 PopIterator = 70 => [],
168 GetArgc = 71 => [],
169 CheckType = 72 => [StringConstantU16, StringConstantU16],
170 TryUnwrap = 73 => [],
171 TryWrapOk = 74 => [],
172 CallSpread = 75 => [],
173 CallBuiltin = 76 => [BuiltinIdU64, StringConstantU16, ImmediateU8],
174 CallBuiltinSpread = 77 => [BuiltinIdU64, StringConstantU16],
175 MethodCallSpread = 78 => [StringConstantU16],
176 Dup = 79 => [],
177 Swap = 80 => [],
178 Contains = 81 => [],
179 AddInt = 82 => [],
180 SubInt = 83 => [],
181 MulInt = 84 => [],
182 DivInt = 85 => [],
183 ModInt = 86 => [],
184 AddFloat = 87 => [],
185 SubFloat = 88 => [],
186 MulFloat = 89 => [],
187 DivFloat = 90 => [],
188 ModFloat = 91 => [],
189 EqualInt = 92 => [],
190 NotEqualInt = 93 => [],
191 LessInt = 94 => [],
192 GreaterInt = 95 => [],
193 LessEqualInt = 96 => [],
194 GreaterEqualInt = 97 => [],
195 EqualFloat = 98 => [],
196 NotEqualFloat = 99 => [],
197 LessFloat = 100 => [],
198 GreaterFloat = 101 => [],
199 LessEqualFloat = 102 => [],
200 GreaterEqualFloat = 103 => [],
201 EqualBool = 104 => [],
202 NotEqualBool = 105 => [],
203 EqualString = 106 => [],
204 NotEqualString = 107 => [],
205 Yield = 108 => [],
206 GetLocalSlot = 109 => [LocalU16],
207 DefLocalSlot = 110 => [LocalU16],
208 SetLocalSlot = 111 => [LocalU16],
209 ConcatAssignLocal = 112 => [LocalU16],
210 NamespaceImportMembers = 113 => [StringConstantU16, StringConstantU16, StringConstantU16],
211}
212
213impl Op {
214 pub const fn portability(self) -> Portability {
218 match self {
219 Self::Constant
220 | Self::Nil
221 | Self::True
222 | Self::False
223 | Self::RootHarness
224 | Self::GetVar
225 | Self::DefLet
226 | Self::DefVar
227 | Self::DefCell
228 | Self::SetVar
229 | Self::PushScope
230 | Self::PopScope
231 | Self::Add
232 | Self::Sub
233 | Self::Mul
234 | Self::Div
235 | Self::Mod
236 | Self::Pow
237 | Self::Negate
238 | Self::Equal
239 | Self::NotEqual
240 | Self::Less
241 | Self::Greater
242 | Self::LessEqual
243 | Self::GreaterEqual
244 | Self::Not
245 | Self::Jump
246 | Self::JumpIfFalse
247 | Self::JumpIfTrue
248 | Self::Pop
249 | Self::Call
250 | Self::TailCall
251 | Self::Return
252 | Self::Closure
253 | Self::BuildList
254 | Self::BuildDict
255 | Self::Subscript
256 | Self::SubscriptOpt
257 | Self::Slice
258 | Self::GetProperty
259 | Self::GetPropertyOpt
260 | Self::SetProperty
261 | Self::SetSubscript
262 | Self::SetLocalSlotProperty
263 | Self::SetLocalSlotSubscript
264 | Self::MethodCall
265 | Self::MethodCallOpt
266 | Self::Concat
267 | Self::Throw
268 | Self::TryCatchSetup
269 | Self::PopHandler
270 | Self::IterInit
271 | Self::IterNext
272 | Self::PopIterator
273 | Self::GetArgc
274 | Self::CallBuiltin
275 | Self::CallBuiltinSpread
276 | Self::Dup
277 | Self::Swap
278 | Self::Contains
279 | Self::AddInt
280 | Self::SubInt
281 | Self::MulInt
282 | Self::DivInt
283 | Self::ModInt
284 | Self::AddFloat
285 | Self::SubFloat
286 | Self::MulFloat
287 | Self::DivFloat
288 | Self::ModFloat
289 | Self::EqualInt
290 | Self::NotEqualInt
291 | Self::LessInt
292 | Self::GreaterInt
293 | Self::LessEqualInt
294 | Self::GreaterEqualInt
295 | Self::EqualFloat
296 | Self::NotEqualFloat
297 | Self::LessFloat
298 | Self::GreaterFloat
299 | Self::LessEqualFloat
300 | Self::GreaterEqualFloat
301 | Self::EqualBool
302 | Self::NotEqualBool
303 | Self::EqualString
304 | Self::NotEqualString
305 | Self::GetLocalSlot
306 | Self::DefLocalSlot
307 | Self::SetLocalSlot
308 | Self::ConcatAssignLocal
309 | Self::BuildEnum
310 | Self::MatchEnum
311 | Self::TryUnwrap
312 | Self::TryWrapOk => Portability::Executable,
313
314 Self::Pipe
315 | Self::Parallel
316 | Self::ParallelMap
317 | Self::ParallelMapStream
318 | Self::ParallelSettle
319 | Self::Spawn
320 | Self::SyncMutexEnter
321 | Self::SyncMutexEnterKeyed
322 | Self::TaskScopeEnter
323 | Self::TaskScopeExit
324 | Self::Import
325 | Self::SelectiveImport
326 | Self::NamespaceImport
327 | Self::NamespaceImportMembers
328 | Self::DeadlineSetup
329 | Self::DeadlineEnd
330 | Self::CheckType
331 | Self::CallSpread
332 | Self::MethodCallSpread
333 | Self::Yield => Portability::Deferred,
334 }
335 }
336
337 pub const fn is_executable(self) -> bool {
338 matches!(self.portability(), Portability::Executable)
339 }
340}
341
342pub const OPCODE_ABI_ARTIFACT_VERSION: u16 = 2;
344
345pub const OPCODE_ABI_FINGERPRINT_V2: [u8; 32] = [
350 0xa6, 0xb2, 0x94, 0x00, 0xa9, 0x05, 0x0d, 0xf4, 0xbb, 0x67, 0xb7, 0xc3, 0x40, 0x62, 0x3c, 0xb2,
351 0x47, 0xc3, 0x75, 0xe6, 0xb6, 0x81, 0x78, 0x68, 0xaa, 0x4a, 0x12, 0x09, 0x5b, 0xd7, 0x5e, 0x56,
352];
353
354pub fn opcode_abi_fingerprint() -> [u8; 32] {
356 let mut hasher = blake3::Hasher::new();
357 for op in Op::ALL {
358 hasher.update(&[*op as u8]);
359 hasher.update(op.name().as_bytes());
360 hasher.update(&[0]);
361 for operand in op.operands() {
362 hasher.update(&[operand.abi_tag()]);
363 }
364 hasher.update(&[0xff]);
365 }
366 *hasher.finalize().as_bytes()
367}
368
369#[cfg(test)]
370mod tests {
371 use super::{
372 opcode_abi_fingerprint, Op, OPCODE_ABI_ARTIFACT_VERSION, OPCODE_ABI_FINGERPRINT_V2,
373 };
374
375 #[test]
376 fn byte_mapping_is_explicit_dense_and_stable() {
377 for (byte, op) in Op::ALL.iter().copied().enumerate() {
378 assert_eq!(Op::from_byte(byte as u8), Some(op));
379 assert_eq!(op as usize, byte);
380 }
381 assert_eq!(Op::from_byte(Op::COUNT as u8), None);
382 }
383
384 #[test]
385 fn opcode_schema_matches_artifact_v2_golden() {
386 assert_eq!(OPCODE_ABI_ARTIFACT_VERSION, crate::ARTIFACT_VERSION);
387 assert_eq!(opcode_abi_fingerprint(), OPCODE_ABI_FINGERPRINT_V2);
388 }
389}