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