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 PortableV1,
26 UnsupportedV1,
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],
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}
211
212impl Op {
213 pub const fn portability(self) -> Portability {
217 match self {
218 Self::Constant
219 | Self::Nil
220 | Self::True
221 | Self::False
222 | Self::RootHarness
223 | Self::GetVar
224 | Self::DefLet
225 | Self::DefVar
226 | Self::DefCell
227 | Self::SetVar
228 | Self::PushScope
229 | Self::PopScope
230 | Self::Add
231 | Self::Sub
232 | Self::Mul
233 | Self::Div
234 | Self::Mod
235 | Self::Pow
236 | Self::Negate
237 | Self::Equal
238 | Self::NotEqual
239 | Self::Less
240 | Self::Greater
241 | Self::LessEqual
242 | Self::GreaterEqual
243 | Self::Not
244 | Self::Jump
245 | Self::JumpIfFalse
246 | Self::JumpIfTrue
247 | Self::Pop
248 | Self::Call
249 | Self::TailCall
250 | Self::Return
251 | Self::Closure
252 | Self::BuildList
253 | Self::BuildDict
254 | Self::Subscript
255 | Self::SubscriptOpt
256 | Self::Slice
257 | Self::GetProperty
258 | Self::GetPropertyOpt
259 | Self::MethodCall
260 | Self::MethodCallOpt
261 | Self::Concat
262 | Self::Throw
263 | Self::TryCatchSetup
264 | Self::PopHandler
265 | Self::GetArgc
266 | Self::CallBuiltin
267 | Self::CallBuiltinSpread
268 | Self::Dup
269 | Self::Swap
270 | Self::Contains
271 | Self::AddInt
272 | Self::SubInt
273 | Self::MulInt
274 | Self::DivInt
275 | Self::ModInt
276 | Self::AddFloat
277 | Self::SubFloat
278 | Self::MulFloat
279 | Self::DivFloat
280 | Self::ModFloat
281 | Self::EqualInt
282 | Self::NotEqualInt
283 | Self::LessInt
284 | Self::GreaterInt
285 | Self::LessEqualInt
286 | Self::GreaterEqualInt
287 | Self::EqualFloat
288 | Self::NotEqualFloat
289 | Self::LessFloat
290 | Self::GreaterFloat
291 | Self::LessEqualFloat
292 | Self::GreaterEqualFloat
293 | Self::EqualBool
294 | Self::NotEqualBool
295 | Self::EqualString
296 | Self::NotEqualString
297 | Self::GetLocalSlot
298 | Self::DefLocalSlot
299 | Self::SetLocalSlot
300 | Self::ConcatAssignLocal => Portability::PortableV1,
301
302 Self::SetProperty
303 | Self::SetSubscript
304 | Self::SetLocalSlotProperty
305 | Self::SetLocalSlotSubscript
306 | Self::IterInit
307 | Self::IterNext
308 | Self::Pipe
309 | Self::Parallel
310 | Self::ParallelMap
311 | Self::ParallelMapStream
312 | Self::ParallelSettle
313 | Self::Spawn
314 | Self::SyncMutexEnter
315 | Self::SyncMutexEnterKeyed
316 | Self::TaskScopeEnter
317 | Self::TaskScopeExit
318 | Self::Import
319 | Self::SelectiveImport
320 | Self::NamespaceImport
321 | Self::DeadlineSetup
322 | Self::DeadlineEnd
323 | Self::BuildEnum
324 | Self::MatchEnum
325 | Self::PopIterator
326 | Self::CheckType
327 | Self::TryUnwrap
328 | Self::TryWrapOk
329 | Self::CallSpread
330 | Self::MethodCallSpread
331 | Self::Yield => Portability::UnsupportedV1,
332 }
333 }
334
335 pub const fn is_portable_v1(self) -> bool {
336 matches!(self.portability(), Portability::PortableV1)
337 }
338}
339
340pub const OPCODE_ABI_ARTIFACT_VERSION: u16 = 1;
342
343pub const OPCODE_ABI_FINGERPRINT_V1: [u8; 32] = [
348 0x76, 0x1f, 0x93, 0x67, 0xa5, 0x69, 0xd4, 0x18, 0x8b, 0x00, 0xf7, 0xaf, 0x36, 0xe5, 0x51, 0x20,
349 0xcb, 0xb6, 0xed, 0x92, 0xf3, 0x47, 0x95, 0xa0, 0x59, 0x3d, 0x02, 0x9e, 0x57, 0xbe, 0x20, 0x0e,
350];
351
352pub fn opcode_abi_fingerprint() -> [u8; 32] {
354 let mut hasher = blake3::Hasher::new();
355 for op in Op::ALL {
356 hasher.update(&[*op as u8]);
357 hasher.update(op.name().as_bytes());
358 hasher.update(&[0]);
359 for operand in op.operands() {
360 hasher.update(&[operand.abi_tag()]);
361 }
362 hasher.update(&[0xff]);
363 }
364 *hasher.finalize().as_bytes()
365}
366
367#[cfg(test)]
368mod tests {
369 use super::{
370 opcode_abi_fingerprint, Op, OPCODE_ABI_ARTIFACT_VERSION, OPCODE_ABI_FINGERPRINT_V1,
371 };
372
373 #[test]
374 fn byte_mapping_is_explicit_dense_and_stable() {
375 for (byte, op) in Op::ALL.iter().copied().enumerate() {
376 assert_eq!(Op::from_byte(byte as u8), Some(op));
377 assert_eq!(op as usize, byte);
378 }
379 assert_eq!(Op::from_byte(Op::COUNT as u8), None);
380 }
381
382 #[test]
383 fn opcode_schema_matches_artifact_v1_golden() {
384 assert_eq!(OPCODE_ABI_ARTIFACT_VERSION, crate::ARTIFACT_VERSION);
385 assert_eq!(opcode_abi_fingerprint(), OPCODE_ABI_FINGERPRINT_V1);
386 }
387}