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}
219
220impl Op {
221 pub const fn portability(self) -> Portability {
225 match self {
226 Self::Constant
227 | Self::Nil
228 | Self::True
229 | Self::False
230 | Self::RootHarness
231 | Self::GetVar
232 | Self::DefLet
233 | Self::DefVar
234 | Self::DefCell
235 | Self::SetVar
236 | Self::PushScope
237 | Self::PopScope
238 | Self::Add
239 | Self::Sub
240 | Self::Mul
241 | Self::Div
242 | Self::Mod
243 | Self::Pow
244 | Self::Negate
245 | Self::Equal
246 | Self::NotEqual
247 | Self::Less
248 | Self::Greater
249 | Self::LessEqual
250 | Self::GreaterEqual
251 | Self::Not
252 | Self::Jump
253 | Self::JumpIfFalse
254 | Self::JumpIfTrue
255 | Self::Pop
256 | Self::Call
257 | Self::TailCall
258 | Self::Return
259 | Self::Closure
260 | Self::BuildList
261 | Self::BuildDict
262 | Self::Subscript
263 | Self::SubscriptOpt
264 | Self::Slice
265 | Self::GetProperty
266 | Self::GetPropertyOpt
267 | Self::SetProperty
268 | Self::SetSubscript
269 | Self::SetLocalSlotProperty
270 | Self::SetLocalSlotSubscript
271 | Self::MethodCall
272 | Self::MethodCallOpt
273 | Self::Concat
274 | Self::Throw
275 | Self::TryCatchSetup
276 | Self::PopHandler
277 | Self::IterInit
278 | Self::IterNext
279 | Self::PopIterator
280 | Self::GetArgc
281 | Self::CallBuiltin
282 | Self::CallBuiltinSpread
283 | Self::Dup
284 | Self::Swap
285 | Self::Contains
286 | Self::AddInt
287 | Self::SubInt
288 | Self::MulInt
289 | Self::DivInt
290 | Self::ModInt
291 | Self::AddFloat
292 | Self::SubFloat
293 | Self::MulFloat
294 | Self::DivFloat
295 | Self::ModFloat
296 | Self::EqualInt
297 | Self::NotEqualInt
298 | Self::LessInt
299 | Self::GreaterInt
300 | Self::LessEqualInt
301 | Self::GreaterEqualInt
302 | Self::EqualFloat
303 | Self::NotEqualFloat
304 | Self::LessFloat
305 | Self::GreaterFloat
306 | Self::LessEqualFloat
307 | Self::GreaterEqualFloat
308 | Self::EqualBool
309 | Self::NotEqualBool
310 | Self::EqualString
311 | Self::NotEqualString
312 | Self::GetLocalSlot
313 | Self::DefLocalSlot
314 | Self::SetLocalSlot
315 | Self::ConcatAssignLocal
316 | Self::BuildEnum
317 | Self::MatchEnum
318 | Self::TryUnwrap
319 | Self::TryWrapOk
320 | Self::AssertBindingType => Portability::Executable,
321
322 Self::Pipe
323 | Self::Parallel
324 | Self::ParallelMap
325 | Self::ParallelMapStream
326 | Self::ParallelSettle
327 | Self::Spawn
328 | Self::SyncMutexEnter
329 | Self::SyncMutexEnterKeyed
330 | Self::TaskScopeEnter
331 | Self::TaskScopeExit
332 | Self::Import
333 | Self::SelectiveImport
334 | Self::NamespaceImport
335 | Self::NamespaceImportMembers
336 | Self::DeadlineSetup
337 | Self::DeadlineEnd
338 | Self::CheckType
339 | Self::CallSpread
340 | Self::MethodCallSpread
341 | Self::Yield => Portability::Deferred,
342 }
343 }
344
345 pub const fn is_executable(self) -> bool {
346 matches!(self.portability(), Portability::Executable)
347 }
348}
349
350pub const OPCODE_ABI_ARTIFACT_VERSION: u16 = 3;
352
353pub const OPCODE_ABI_FINGERPRINT_V3: [u8; 32] = [
363 0x8b, 0xbc, 0xdf, 0x24, 0x31, 0x39, 0x01, 0x8e, 0xc3, 0x48, 0xc3, 0x7d, 0xab, 0x00, 0x82, 0x9c,
364 0xa9, 0x34, 0x3d, 0xb6, 0xb2, 0xfe, 0x3d, 0x22, 0x45, 0x4a, 0xfa, 0x0a, 0xe8, 0x88, 0xa6, 0x79,
365];
366
367pub fn opcode_abi_fingerprint() -> [u8; 32] {
369 let mut hasher = blake3::Hasher::new();
370 for op in Op::ALL {
371 hasher.update(&[*op as u8]);
372 hasher.update(op.name().as_bytes());
373 hasher.update(&[0]);
374 for operand in op.operands() {
375 hasher.update(&[operand.abi_tag()]);
376 }
377 hasher.update(&[0xff]);
378 }
379 *hasher.finalize().as_bytes()
380}
381
382#[cfg(test)]
383mod tests {
384 use super::{
385 opcode_abi_fingerprint, Op, OPCODE_ABI_ARTIFACT_VERSION, OPCODE_ABI_FINGERPRINT_V3,
386 };
387
388 #[test]
389 fn byte_mapping_is_explicit_dense_and_stable() {
390 for (byte, op) in Op::ALL.iter().copied().enumerate() {
391 assert_eq!(Op::from_byte(byte as u8), Some(op));
392 assert_eq!(op as usize, byte);
393 }
394 assert_eq!(Op::from_byte(Op::COUNT as u8), None);
395 }
396
397 #[test]
398 fn opcode_schema_matches_artifact_v3_golden() {
399 assert_eq!(OPCODE_ABI_ARTIFACT_VERSION, crate::ARTIFACT_VERSION);
400 assert_eq!(opcode_abi_fingerprint(), OPCODE_ABI_FINGERPRINT_V3);
401 }
402}