1use std::collections::HashMap;
2use std::fmt::Display;
3use std::fmt::Formatter;
4use std::fmt::Result as FmtResult;
5use std::num::TryFromIntError;
6use std::result;
7
8use arbitrary::Arbitrary;
9use arbitrary::Unstructured;
10use get_size2::GetSize;
11use itertools::Itertools;
12use lazy_static::lazy_static;
13use num_traits::ConstZero;
14use serde::Deserialize;
15use serde::Serialize;
16use strum::EnumCount;
17use strum::EnumIter;
18use strum::IntoEnumIterator;
19use thiserror::Error;
20use twenty_first::prelude::*;
21
22use crate::op_stack::NumberOfWords;
23use crate::op_stack::OpStackElement;
24use crate::op_stack::OpStackError;
25
26type Result<T> = result::Result<T, InstructionError>;
27
28pub type Instruction = AnInstruction<BFieldElement>;
30
31pub const ALL_INSTRUCTIONS: [Instruction; Instruction::COUNT] = [
32 Instruction::Pop(NumberOfWords::N1),
33 Instruction::Push(BFieldElement::ZERO),
34 Instruction::Divine(NumberOfWords::N1),
35 Instruction::Pick(OpStackElement::ST0),
36 Instruction::Place(OpStackElement::ST0),
37 Instruction::Dup(OpStackElement::ST0),
38 Instruction::Swap(OpStackElement::ST0),
39 Instruction::Halt,
40 Instruction::Nop,
41 Instruction::Skiz,
42 Instruction::Call(BFieldElement::ZERO),
43 Instruction::Return,
44 Instruction::Recurse,
45 Instruction::RecurseOrReturn,
46 Instruction::Assert,
47 Instruction::ReadMem(NumberOfWords::N1),
48 Instruction::WriteMem(NumberOfWords::N1),
49 Instruction::Hash,
50 Instruction::AssertVector,
51 Instruction::SpongeInit,
52 Instruction::SpongeAbsorb,
53 Instruction::SpongeAbsorbMem,
54 Instruction::SpongeSqueeze,
55 Instruction::Add,
56 Instruction::AddI(BFieldElement::ZERO),
57 Instruction::Mul,
58 Instruction::Invert,
59 Instruction::Eq,
60 Instruction::Split,
61 Instruction::Lt,
62 Instruction::And,
63 Instruction::Xor,
64 Instruction::Log2Floor,
65 Instruction::Pow,
66 Instruction::DivMod,
67 Instruction::PopCount,
68 Instruction::XxAdd,
69 Instruction::XxMul,
70 Instruction::XInvert,
71 Instruction::XbMul,
72 Instruction::ReadIo(NumberOfWords::N1),
73 Instruction::WriteIo(NumberOfWords::N1),
74 Instruction::MerkleStep,
75 Instruction::MerkleStepMem,
76 Instruction::XxDotStep,
77 Instruction::XbDotStep,
78];
79
80pub const ALL_INSTRUCTION_NAMES: [&str; Instruction::COUNT] = {
81 let mut names = [""; Instruction::COUNT];
82 let mut i = 0;
83 while i < Instruction::COUNT {
84 names[i] = ALL_INSTRUCTIONS[i].name();
85 i += 1;
86 }
87 names
88};
89
90lazy_static! {
91 pub static ref OPCODE_TO_INSTRUCTION_MAP: HashMap<u32, Instruction> = {
92 let mut opcode_to_instruction_map = HashMap::new();
93 for instruction in Instruction::iter() {
94 opcode_to_instruction_map.insert(instruction.opcode(), instruction);
95 }
96 opcode_to_instruction_map
97 };
98}
99
100#[derive(Debug, Clone, Eq, PartialEq, Hash, EnumCount)]
102pub enum LabelledInstruction {
103 Instruction(AnInstruction<String>),
107
108 Label(String),
110
111 Breakpoint,
112
113 TypeHint(TypeHint),
114
115 AssertionContext(AssertionContext),
116}
117
118#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, GetSize)]
127pub struct TypeHint {
128 pub starting_index: usize,
129 pub length: usize,
130
131 pub type_name: Option<String>,
133
134 pub variable_name: String,
136}
137
138#[non_exhaustive]
141#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, GetSize, Arbitrary)]
142pub enum AssertionContext {
143 ID(i128),
144 }
146
147impl LabelledInstruction {
148 pub const fn op_stack_size_influence(&self) -> i32 {
149 match self {
150 LabelledInstruction::Instruction(instruction) => instruction.op_stack_size_influence(),
151 _ => 0,
152 }
153 }
154}
155
156impl Display for LabelledInstruction {
157 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
158 match self {
159 LabelledInstruction::Instruction(instruction) => write!(f, "{instruction}"),
160 LabelledInstruction::Label(label) => write!(f, "{label}:"),
161 LabelledInstruction::Breakpoint => write!(f, "break"),
162 LabelledInstruction::TypeHint(type_hint) => write!(f, "{type_hint}"),
163 LabelledInstruction::AssertionContext(ctx) => write!(f, "{ctx}"),
164 }
165 }
166}
167
168impl Display for TypeHint {
169 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
170 let variable = &self.variable_name;
171
172 let format_type = |t| format!(": {t}");
173 let maybe_type = self.type_name.as_ref();
174 let type_name = maybe_type.map(format_type).unwrap_or_default();
175
176 let start = self.starting_index;
177 let range = match self.length {
178 1 => format!("{start}"),
179 _ => format!("{start}..{end}", end = start + self.length),
180 };
181
182 write!(f, "hint {variable}{type_name} = stack[{range}]")
183 }
184}
185
186impl Display for AssertionContext {
187 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
188 let Self::ID(id) = self;
189 write!(f, "error_id {id}")
190 }
191}
192
193impl<'a> Arbitrary<'a> for TypeHint {
194 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
195 let starting_index = u.arbitrary()?;
196 let length = u.int_in_range(1..=500)?;
197 let type_name = if u.arbitrary()? {
198 Some(u.arbitrary::<TypeHintTypeName>()?.into())
199 } else {
200 None
201 };
202 let variable_name = u.arbitrary::<TypeHintVariableName>()?.into();
203
204 let type_hint = Self {
205 starting_index,
206 length,
207 type_name,
208 variable_name,
209 };
210 Ok(type_hint)
211 }
212}
213
214#[derive(
220 Debug,
221 Copy,
222 Clone,
223 Eq,
224 PartialEq,
225 Hash,
226 EnumCount,
227 EnumIter,
228 Serialize,
229 Deserialize,
230 GetSize,
231 Arbitrary,
232)]
233pub enum AnInstruction<Dest: PartialEq + Default> {
234 Pop(NumberOfWords),
236 Push(BFieldElement),
237 Divine(NumberOfWords),
238 Pick(OpStackElement),
239 Place(OpStackElement),
240 Dup(OpStackElement),
241 Swap(OpStackElement),
242
243 Halt,
245 Nop,
246 Skiz,
247 Call(Dest),
248 Return,
249 Recurse,
250 RecurseOrReturn,
251 Assert,
252
253 ReadMem(NumberOfWords),
255 WriteMem(NumberOfWords),
256
257 Hash,
259 AssertVector,
260 SpongeInit,
261 SpongeAbsorb,
262 SpongeAbsorbMem,
263 SpongeSqueeze,
264
265 Add,
267 AddI(BFieldElement),
268 Mul,
269 Invert,
270 Eq,
271
272 Split,
274 Lt,
275 And,
276 Xor,
277 Log2Floor,
278 Pow,
279 DivMod,
280 PopCount,
281
282 XxAdd,
284 XxMul,
285 XInvert,
286 XbMul,
287
288 ReadIo(NumberOfWords),
290 WriteIo(NumberOfWords),
291
292 MerkleStep,
294 MerkleStepMem,
295 XxDotStep,
296 XbDotStep,
297}
298
299impl<Dest: PartialEq + Default> AnInstruction<Dest> {
300 pub const fn opcode(&self) -> u32 {
302 match self {
303 AnInstruction::Pop(_) => 3,
304 AnInstruction::Push(_) => 1,
305 AnInstruction::Divine(_) => 9,
306 AnInstruction::Pick(_) => 17,
307 AnInstruction::Place(_) => 25,
308 AnInstruction::Dup(_) => 33,
309 AnInstruction::Swap(_) => 41,
310 AnInstruction::Halt => 0,
311 AnInstruction::Nop => 8,
312 AnInstruction::Skiz => 2,
313 AnInstruction::Call(_) => 49,
314 AnInstruction::Return => 16,
315 AnInstruction::Recurse => 24,
316 AnInstruction::RecurseOrReturn => 32,
317 AnInstruction::Assert => 10,
318 AnInstruction::ReadMem(_) => 57,
319 AnInstruction::WriteMem(_) => 11,
320 AnInstruction::Hash => 18,
321 AnInstruction::AssertVector => 26,
322 AnInstruction::SpongeInit => 40,
323 AnInstruction::SpongeAbsorb => 34,
324 AnInstruction::SpongeAbsorbMem => 48,
325 AnInstruction::SpongeSqueeze => 56,
326 AnInstruction::Add => 42,
327 AnInstruction::AddI(_) => 65,
328 AnInstruction::Mul => 50,
329 AnInstruction::Invert => 64,
330 AnInstruction::Eq => 58,
331 AnInstruction::Split => 4,
332 AnInstruction::Lt => 6,
333 AnInstruction::And => 14,
334 AnInstruction::Xor => 22,
335 AnInstruction::Log2Floor => 12,
336 AnInstruction::Pow => 30,
337 AnInstruction::DivMod => 20,
338 AnInstruction::PopCount => 28,
339 AnInstruction::XxAdd => 66,
340 AnInstruction::XxMul => 74,
341 AnInstruction::XInvert => 72,
342 AnInstruction::XbMul => 82,
343 AnInstruction::ReadIo(_) => 73,
344 AnInstruction::WriteIo(_) => 19,
345 AnInstruction::MerkleStep => 36,
346 AnInstruction::MerkleStepMem => 44,
347 AnInstruction::XxDotStep => 80,
348 AnInstruction::XbDotStep => 88,
349 }
350 }
351
352 pub const fn name(&self) -> &'static str {
353 match self {
354 AnInstruction::Pop(_) => "pop",
355 AnInstruction::Push(_) => "push",
356 AnInstruction::Divine(_) => "divine",
357 AnInstruction::Pick(_) => "pick",
358 AnInstruction::Place(_) => "place",
359 AnInstruction::Dup(_) => "dup",
360 AnInstruction::Swap(_) => "swap",
361 AnInstruction::Halt => "halt",
362 AnInstruction::Nop => "nop",
363 AnInstruction::Skiz => "skiz",
364 AnInstruction::Call(_) => "call",
365 AnInstruction::Return => "return",
366 AnInstruction::Recurse => "recurse",
367 AnInstruction::RecurseOrReturn => "recurse_or_return",
368 AnInstruction::Assert => "assert",
369 AnInstruction::ReadMem(_) => "read_mem",
370 AnInstruction::WriteMem(_) => "write_mem",
371 AnInstruction::Hash => "hash",
372 AnInstruction::AssertVector => "assert_vector",
373 AnInstruction::SpongeInit => "sponge_init",
374 AnInstruction::SpongeAbsorb => "sponge_absorb",
375 AnInstruction::SpongeAbsorbMem => "sponge_absorb_mem",
376 AnInstruction::SpongeSqueeze => "sponge_squeeze",
377 AnInstruction::Add => "add",
378 AnInstruction::AddI(_) => "addi",
379 AnInstruction::Mul => "mul",
380 AnInstruction::Invert => "invert",
381 AnInstruction::Eq => "eq",
382 AnInstruction::Split => "split",
383 AnInstruction::Lt => "lt",
384 AnInstruction::And => "and",
385 AnInstruction::Xor => "xor",
386 AnInstruction::Log2Floor => "log_2_floor",
387 AnInstruction::Pow => "pow",
388 AnInstruction::DivMod => "div_mod",
389 AnInstruction::PopCount => "pop_count",
390 AnInstruction::XxAdd => "xx_add",
391 AnInstruction::XxMul => "xx_mul",
392 AnInstruction::XInvert => "x_invert",
393 AnInstruction::XbMul => "xb_mul",
394 AnInstruction::ReadIo(_) => "read_io",
395 AnInstruction::WriteIo(_) => "write_io",
396 AnInstruction::MerkleStep => "merkle_step",
397 AnInstruction::MerkleStepMem => "merkle_step_mem",
398 AnInstruction::XxDotStep => "xx_dot_step",
399 AnInstruction::XbDotStep => "xb_dot_step",
400 }
401 }
402
403 pub const fn opcode_b(&self) -> BFieldElement {
404 BFieldElement::new(self.opcode() as u64)
405 }
406
407 pub fn size(&self) -> usize {
409 match self {
410 AnInstruction::Pop(_) | AnInstruction::Push(_) => 2,
411 AnInstruction::Divine(_) => 2,
412 AnInstruction::Pick(_) | AnInstruction::Place(_) => 2,
413 AnInstruction::Dup(_) | AnInstruction::Swap(_) => 2,
414 AnInstruction::Call(_) => 2,
415 AnInstruction::ReadMem(_) | AnInstruction::WriteMem(_) => 2,
416 AnInstruction::AddI(_) => 2,
417 AnInstruction::ReadIo(_) | AnInstruction::WriteIo(_) => 2,
418 _ => 1,
419 }
420 }
421
422 pub fn ib(&self, arg: InstructionBit) -> BFieldElement {
424 bfe!((self.opcode() >> usize::from(arg)) & 1)
425 }
426
427 pub fn map_call_address<F, NewDest>(&self, f: F) -> AnInstruction<NewDest>
428 where
429 F: FnOnce(&Dest) -> NewDest,
430 NewDest: PartialEq + Default,
431 {
432 match self {
433 AnInstruction::Pop(x) => AnInstruction::Pop(*x),
434 AnInstruction::Push(x) => AnInstruction::Push(*x),
435 AnInstruction::Divine(x) => AnInstruction::Divine(*x),
436 AnInstruction::Pick(x) => AnInstruction::Pick(*x),
437 AnInstruction::Place(x) => AnInstruction::Place(*x),
438 AnInstruction::Dup(x) => AnInstruction::Dup(*x),
439 AnInstruction::Swap(x) => AnInstruction::Swap(*x),
440 AnInstruction::Halt => AnInstruction::Halt,
441 AnInstruction::Nop => AnInstruction::Nop,
442 AnInstruction::Skiz => AnInstruction::Skiz,
443 AnInstruction::Call(label) => AnInstruction::Call(f(label)),
444 AnInstruction::Return => AnInstruction::Return,
445 AnInstruction::Recurse => AnInstruction::Recurse,
446 AnInstruction::RecurseOrReturn => AnInstruction::RecurseOrReturn,
447 AnInstruction::Assert => AnInstruction::Assert,
448 AnInstruction::ReadMem(x) => AnInstruction::ReadMem(*x),
449 AnInstruction::WriteMem(x) => AnInstruction::WriteMem(*x),
450 AnInstruction::Hash => AnInstruction::Hash,
451 AnInstruction::AssertVector => AnInstruction::AssertVector,
452 AnInstruction::SpongeInit => AnInstruction::SpongeInit,
453 AnInstruction::SpongeAbsorb => AnInstruction::SpongeAbsorb,
454 AnInstruction::SpongeAbsorbMem => AnInstruction::SpongeAbsorbMem,
455 AnInstruction::SpongeSqueeze => AnInstruction::SpongeSqueeze,
456 AnInstruction::Add => AnInstruction::Add,
457 AnInstruction::AddI(x) => AnInstruction::AddI(*x),
458 AnInstruction::Mul => AnInstruction::Mul,
459 AnInstruction::Invert => AnInstruction::Invert,
460 AnInstruction::Eq => AnInstruction::Eq,
461 AnInstruction::Split => AnInstruction::Split,
462 AnInstruction::Lt => AnInstruction::Lt,
463 AnInstruction::And => AnInstruction::And,
464 AnInstruction::Xor => AnInstruction::Xor,
465 AnInstruction::Log2Floor => AnInstruction::Log2Floor,
466 AnInstruction::Pow => AnInstruction::Pow,
467 AnInstruction::DivMod => AnInstruction::DivMod,
468 AnInstruction::PopCount => AnInstruction::PopCount,
469 AnInstruction::XxAdd => AnInstruction::XxAdd,
470 AnInstruction::XxMul => AnInstruction::XxMul,
471 AnInstruction::XInvert => AnInstruction::XInvert,
472 AnInstruction::XbMul => AnInstruction::XbMul,
473 AnInstruction::ReadIo(x) => AnInstruction::ReadIo(*x),
474 AnInstruction::WriteIo(x) => AnInstruction::WriteIo(*x),
475 AnInstruction::MerkleStep => AnInstruction::MerkleStep,
476 AnInstruction::MerkleStepMem => AnInstruction::MerkleStepMem,
477 AnInstruction::XxDotStep => AnInstruction::XxDotStep,
478 AnInstruction::XbDotStep => AnInstruction::XbDotStep,
479 }
480 }
481
482 pub const fn op_stack_size_influence(&self) -> i32 {
483 match self {
484 AnInstruction::Pop(n) => -(n.num_words() as i32),
485 AnInstruction::Push(_) => 1,
486 AnInstruction::Divine(n) => n.num_words() as i32,
487 AnInstruction::Pick(_) => 0,
488 AnInstruction::Place(_) => 0,
489 AnInstruction::Dup(_) => 1,
490 AnInstruction::Swap(_) => 0,
491 AnInstruction::Halt => 0,
492 AnInstruction::Nop => 0,
493 AnInstruction::Skiz => -1,
494 AnInstruction::Call(_) => 0,
495 AnInstruction::Return => 0,
496 AnInstruction::Recurse => 0,
497 AnInstruction::RecurseOrReturn => 0,
498 AnInstruction::Assert => -1,
499 AnInstruction::ReadMem(n) => n.num_words() as i32,
500 AnInstruction::WriteMem(n) => -(n.num_words() as i32),
501 AnInstruction::Hash => -5,
502 AnInstruction::AssertVector => -5,
503 AnInstruction::SpongeInit => 0,
504 AnInstruction::SpongeAbsorb => -10,
505 AnInstruction::SpongeAbsorbMem => 0,
506 AnInstruction::SpongeSqueeze => 10,
507 AnInstruction::Add => -1,
508 AnInstruction::AddI(_) => 0,
509 AnInstruction::Mul => -1,
510 AnInstruction::Invert => 0,
511 AnInstruction::Eq => -1,
512 AnInstruction::Split => 1,
513 AnInstruction::Lt => -1,
514 AnInstruction::And => -1,
515 AnInstruction::Xor => -1,
516 AnInstruction::Log2Floor => 0,
517 AnInstruction::Pow => -1,
518 AnInstruction::DivMod => 0,
519 AnInstruction::PopCount => 0,
520 AnInstruction::XxAdd => -3,
521 AnInstruction::XxMul => -3,
522 AnInstruction::XInvert => 0,
523 AnInstruction::XbMul => -1,
524 AnInstruction::ReadIo(n) => n.num_words() as i32,
525 AnInstruction::WriteIo(n) => -(n.num_words() as i32),
526 AnInstruction::MerkleStep => 0,
527 AnInstruction::MerkleStepMem => 0,
528 AnInstruction::XxDotStep => 0,
529 AnInstruction::XbDotStep => 0,
530 }
531 }
532
533 pub fn is_u32_instruction(&self) -> bool {
535 matches!(
536 self,
537 AnInstruction::Split
538 | AnInstruction::Lt
539 | AnInstruction::And
540 | AnInstruction::Xor
541 | AnInstruction::Log2Floor
542 | AnInstruction::Pow
543 | AnInstruction::DivMod
544 | AnInstruction::PopCount
545 | AnInstruction::MerkleStep
546 | AnInstruction::MerkleStepMem
547 )
548 }
549}
550
551impl<Dest: Display + PartialEq + Default> Display for AnInstruction<Dest> {
552 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
553 write!(f, "{}", self.name())?;
554 match self {
555 AnInstruction::Push(arg) => write!(f, " {arg}"),
556 AnInstruction::Pick(arg) => write!(f, " {arg}"),
557 AnInstruction::Place(arg) => write!(f, " {arg}"),
558 AnInstruction::Pop(arg) => write!(f, " {arg}"),
559 AnInstruction::Divine(arg) => write!(f, " {arg}"),
560 AnInstruction::Dup(arg) => write!(f, " {arg}"),
561 AnInstruction::Swap(arg) => write!(f, " {arg}"),
562 AnInstruction::Call(arg) => write!(f, " {arg}"),
563 AnInstruction::ReadMem(arg) => write!(f, " {arg}"),
564 AnInstruction::WriteMem(arg) => write!(f, " {arg}"),
565 AnInstruction::AddI(arg) => write!(f, " {arg}"),
566 AnInstruction::ReadIo(arg) => write!(f, " {arg}"),
567 AnInstruction::WriteIo(arg) => write!(f, " {arg}"),
568 _ => Ok(()),
569 }
570 }
571}
572
573impl Instruction {
574 pub fn arg(&self) -> Option<BFieldElement> {
576 match self {
577 AnInstruction::Push(arg) => Some(*arg),
578 AnInstruction::Call(arg) => Some(*arg),
579 AnInstruction::Pop(arg) => Some(arg.into()),
580 AnInstruction::Divine(arg) => Some(arg.into()),
581 AnInstruction::Pick(arg) => Some(arg.into()),
582 AnInstruction::Place(arg) => Some(arg.into()),
583 AnInstruction::Dup(arg) => Some(arg.into()),
584 AnInstruction::Swap(arg) => Some(arg.into()),
585 AnInstruction::ReadMem(arg) => Some(arg.into()),
586 AnInstruction::WriteMem(arg) => Some(arg.into()),
587 AnInstruction::AddI(arg) => Some(*arg),
588 AnInstruction::ReadIo(arg) => Some(arg.into()),
589 AnInstruction::WriteIo(arg) => Some(arg.into()),
590 _ => None,
591 }
592 }
593
594 pub fn change_arg(self, new_arg: BFieldElement) -> Result<Self> {
597 let illegal_argument_error = || InstructionError::IllegalArgument(self, new_arg);
598 let num_words = new_arg.try_into().map_err(|_| illegal_argument_error());
599 let op_stack_element = new_arg.try_into().map_err(|_| illegal_argument_error());
600
601 let new_instruction = match self {
602 AnInstruction::Pop(_) => AnInstruction::Pop(num_words?),
603 AnInstruction::Push(_) => AnInstruction::Push(new_arg),
604 AnInstruction::Divine(_) => AnInstruction::Divine(num_words?),
605 AnInstruction::Pick(_) => AnInstruction::Pick(op_stack_element?),
606 AnInstruction::Place(_) => AnInstruction::Place(op_stack_element?),
607 AnInstruction::Dup(_) => AnInstruction::Dup(op_stack_element?),
608 AnInstruction::Swap(_) => AnInstruction::Swap(op_stack_element?),
609 AnInstruction::Call(_) => AnInstruction::Call(new_arg),
610 AnInstruction::ReadMem(_) => AnInstruction::ReadMem(num_words?),
611 AnInstruction::WriteMem(_) => AnInstruction::WriteMem(num_words?),
612 AnInstruction::AddI(_) => AnInstruction::AddI(new_arg),
613 AnInstruction::ReadIo(_) => AnInstruction::ReadIo(num_words?),
614 AnInstruction::WriteIo(_) => AnInstruction::WriteIo(num_words?),
615 _ => return Err(illegal_argument_error()),
616 };
617
618 Ok(new_instruction)
619 }
620}
621
622impl TryFrom<u32> for Instruction {
623 type Error = InstructionError;
624
625 fn try_from(opcode: u32) -> Result<Self> {
626 OPCODE_TO_INSTRUCTION_MAP
627 .get(&opcode)
628 .copied()
629 .ok_or(InstructionError::InvalidOpcode(opcode))
630 }
631}
632
633impl TryFrom<u64> for Instruction {
634 type Error = InstructionError;
635
636 fn try_from(opcode: u64) -> Result<Self> {
637 let opcode = u32::try_from(opcode)?;
638 opcode.try_into()
639 }
640}
641
642impl TryFrom<usize> for Instruction {
643 type Error = InstructionError;
644
645 fn try_from(opcode: usize) -> Result<Self> {
646 let opcode = u32::try_from(opcode)?;
647 opcode.try_into()
648 }
649}
650
651impl TryFrom<BFieldElement> for Instruction {
652 type Error = InstructionError;
653
654 fn try_from(opcode: BFieldElement) -> Result<Self> {
655 let opcode = u32::try_from(opcode)?;
656 opcode.try_into()
657 }
658}
659
660#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, EnumCount, EnumIter)]
662pub enum InstructionBit {
663 #[default]
664 IB0,
665 IB1,
666 IB2,
667 IB3,
668 IB4,
669 IB5,
670 IB6,
671}
672
673impl Display for InstructionBit {
674 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
675 let bit_index = usize::from(*self);
676 write!(f, "{bit_index}")
677 }
678}
679
680impl From<InstructionBit> for usize {
681 fn from(instruction_bit: InstructionBit) -> Self {
682 match instruction_bit {
683 InstructionBit::IB0 => 0,
684 InstructionBit::IB1 => 1,
685 InstructionBit::IB2 => 2,
686 InstructionBit::IB3 => 3,
687 InstructionBit::IB4 => 4,
688 InstructionBit::IB5 => 5,
689 InstructionBit::IB6 => 6,
690 }
691 }
692}
693
694impl TryFrom<usize> for InstructionBit {
695 type Error = String;
696
697 fn try_from(bit_index: usize) -> result::Result<Self, Self::Error> {
698 match bit_index {
699 0 => Ok(InstructionBit::IB0),
700 1 => Ok(InstructionBit::IB1),
701 2 => Ok(InstructionBit::IB2),
702 3 => Ok(InstructionBit::IB3),
703 4 => Ok(InstructionBit::IB4),
704 5 => Ok(InstructionBit::IB5),
705 6 => Ok(InstructionBit::IB6),
706 _ => Err(format!(
707 "Index {bit_index} is out of range for `InstructionBit`."
708 )),
709 }
710 }
711}
712
713impl<'a> Arbitrary<'a> for LabelledInstruction {
714 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
715 let instruction = match u.choose_index(LabelledInstruction::COUNT)? {
716 0 => u.arbitrary::<AnInstruction<String>>()?,
717 1 => return Ok(Self::Label(u.arbitrary::<InstructionLabel>()?.into())),
718 2 => return Ok(Self::Breakpoint),
719 3 => return Ok(Self::TypeHint(u.arbitrary()?)),
720 4 => return Ok(Self::AssertionContext(u.arbitrary()?)),
721 _ => unreachable!(),
722 };
723 let legal_label = String::from(u.arbitrary::<InstructionLabel>()?);
724 let instruction = instruction.map_call_address(|_| legal_label.clone());
725
726 Ok(Self::Instruction(instruction))
727 }
728}
729
730#[derive(Debug, Clone, Eq, PartialEq, Hash)]
731struct InstructionLabel(String);
732
733impl From<InstructionLabel> for String {
734 fn from(label: InstructionLabel) -> Self {
735 label.0
736 }
737}
738
739impl<'a> Arbitrary<'a> for InstructionLabel {
740 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
741 let legal_start_characters = ('a'..='z').chain('A'..='Z').chain('_'..='_');
742 let legal_characters = legal_start_characters
743 .clone()
744 .chain('0'..='9')
745 .chain('-'..='-')
746 .collect_vec();
747
748 let mut label = u.choose(&legal_start_characters.collect_vec())?.to_string();
749 for _ in 0..u.arbitrary_len::<char>()? {
750 label.push(*u.choose(&legal_characters)?);
751 }
752 while ALL_INSTRUCTION_NAMES.contains(&label.as_str()) {
753 label.push(*u.choose(&legal_characters)?);
754 }
755 Ok(Self(label))
756 }
757}
758
759#[derive(Debug, Clone, Eq, PartialEq, Hash)]
760struct TypeHintVariableName(String);
761
762impl From<TypeHintVariableName> for String {
763 fn from(label: TypeHintVariableName) -> Self {
764 label.0
765 }
766}
767
768impl<'a> Arbitrary<'a> for TypeHintVariableName {
769 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
770 let legal_start_characters = 'a'..='z';
771 let legal_characters = legal_start_characters
772 .clone()
773 .chain('0'..='9')
774 .chain('_'..='_')
775 .collect_vec();
776
777 let mut variable_name = u.choose(&legal_start_characters.collect_vec())?.to_string();
778 for _ in 0..u.arbitrary_len::<char>()? {
779 variable_name.push(*u.choose(&legal_characters)?);
780 }
781 Ok(Self(variable_name))
782 }
783}
784
785#[derive(Debug, Clone, Eq, PartialEq, Hash)]
786struct TypeHintTypeName(String);
787
788impl From<TypeHintTypeName> for String {
789 fn from(label: TypeHintTypeName) -> Self {
790 label.0
791 }
792}
793
794impl<'a> Arbitrary<'a> for TypeHintTypeName {
795 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
796 let legal_start_characters = ('a'..='z').chain('A'..='Z');
797 let legal_characters = legal_start_characters
798 .clone()
799 .chain('0'..='9')
800 .collect_vec();
801
802 let mut type_name = u.choose(&legal_start_characters.collect_vec())?.to_string();
803 for _ in 0..u.arbitrary_len::<char>()? {
804 type_name.push(*u.choose(&legal_characters)?);
805 }
806 Ok(Self(type_name))
807 }
808}
809
810#[non_exhaustive]
811#[derive(Debug, Clone, Eq, PartialEq, Error)]
812pub enum InstructionError {
813 #[error("opcode {0} is invalid")]
814 InvalidOpcode(u32),
815
816 #[error("opcode is out of range: {0}")]
817 OutOfRangeOpcode(#[from] TryFromIntError),
818
819 #[error("invalid argument {1} for instruction `{0}`")]
820 IllegalArgument(Instruction, BFieldElement),
821
822 #[error("instruction pointer points outside of program")]
823 InstructionPointerOverflow,
824
825 #[error("jump stack is empty")]
826 JumpStackIsEmpty,
827
828 #[error("assertion failed: {0}")]
829 AssertionFailed(AssertionError),
830
831 #[error("vector assertion failed because stack[{0}] != stack[{r}]: {1}", r = .0 + Digest::LEN)]
832 VectorAssertionFailed(usize, AssertionError),
833
834 #[error("0 does not have a multiplicative inverse")]
835 InverseOfZero,
836
837 #[error("division by 0 is impossible")]
838 DivisionByZero,
839
840 #[error("the Sponge state must be initialized before it can be used")]
841 SpongeNotInitialized,
842
843 #[error("the logarithm of 0 does not exist")]
844 LogarithmOfZero,
845
846 #[error("public input buffer is empty after {0} reads")]
847 EmptyPublicInput(usize),
848
849 #[error("secret input buffer is empty after {0} reads")]
850 EmptySecretInput(usize),
851
852 #[error("no more secret digests available")]
853 EmptySecretDigestInput,
854
855 #[error("Triton VM has halted and cannot execute any further instructions")]
856 MachineHalted,
857
858 #[error(transparent)]
859 OpStackError(#[from] OpStackError),
860}
861
862#[non_exhaustive]
864#[derive(Debug, Clone, Eq, PartialEq, Error)]
865pub struct AssertionError {
866 pub expected: BFieldElement,
868
869 pub actual: BFieldElement,
871
872 pub id: Option<i128>,
874 }
878
879impl Display for AssertionError {
880 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
881 if let Some(id) = self.id {
882 write!(f, "[{id}] ")?;
883 }
884 write!(f, "expected {}, got {}", self.expected, self.actual)
885 }
886}
887
888impl AssertionError {
889 pub fn new(expected: impl Into<BFieldElement>, actual: impl Into<BFieldElement>) -> Self {
890 Self {
891 expected: expected.into(),
892 actual: actual.into(),
893 id: None,
894 }
895 }
896
897 #[must_use]
898 pub fn with_context(mut self, context: AssertionContext) -> Self {
899 match context {
900 AssertionContext::ID(id) => self.id = Some(id),
901 };
902 self
903 }
904}
905
906#[cfg(test)]
907pub mod tests {
908 use std::collections::HashMap;
909
910 use assert2::assert;
911 use itertools::Itertools;
912 use num_traits::One;
913 use num_traits::Zero;
914 use rand::Rng;
915 use strum::EnumCount;
916 use strum::IntoEnumIterator;
917 use strum::VariantNames;
918 use twenty_first::prelude::*;
919
920 use crate::triton_asm;
921 use crate::triton_program;
922
923 use super::*;
924
925 #[derive(Debug, Copy, Clone, EnumCount, EnumIter, VariantNames)]
926 pub enum InstructionBucket {
927 HasArg,
928 ShrinksStack,
929 IsU32,
930 }
931
932 impl InstructionBucket {
933 pub fn contains(self, instruction: Instruction) -> bool {
934 match self {
935 InstructionBucket::HasArg => instruction.arg().is_some(),
936 InstructionBucket::ShrinksStack => instruction.op_stack_size_influence() < 0,
937 InstructionBucket::IsU32 => instruction.is_u32_instruction(),
938 }
939 }
940
941 pub fn flag(self) -> u32 {
942 match self {
943 InstructionBucket::HasArg => 1,
944 InstructionBucket::ShrinksStack => 1 << 1,
945 InstructionBucket::IsU32 => 1 << 2,
946 }
947 }
948 }
949
950 impl Instruction {
951 pub fn flag_set(self) -> u32 {
952 InstructionBucket::iter()
953 .map(|bucket| u32::from(bucket.contains(self)) * bucket.flag())
954 .fold(0, |acc, bit_flag| acc | bit_flag)
955 }
956
957 fn computed_opcode(self) -> u32 {
958 let mut index_within_flag_set = 0;
959 for other_instruction in Instruction::iter() {
960 if other_instruction == self {
961 break;
962 }
963 if other_instruction.flag_set() == self.flag_set() {
964 index_within_flag_set += 1;
965 }
966 }
967
968 index_within_flag_set * 2_u32.pow(InstructionBucket::COUNT as u32) + self.flag_set()
969 }
970 }
971
972 #[test]
973 fn computed_and_actual_opcodes_are_identical() {
974 for instruction in Instruction::iter() {
975 let opcode = instruction.computed_opcode();
976 let name = instruction.name();
977 println!("{opcode: >3} {name}");
978 }
979
980 for instruction in Instruction::iter() {
981 let expected_opcode = instruction.computed_opcode();
982 let opcode = instruction.opcode();
983 assert!(expected_opcode == opcode, "{instruction}");
984 }
985 }
986
987 #[test]
988 fn opcodes_are_unique() {
989 let mut opcodes_to_instruction_map = HashMap::new();
990 for instruction in Instruction::iter() {
991 let opcode = instruction.opcode();
992 let maybe_entry = opcodes_to_instruction_map.insert(opcode, instruction);
993 if let Some(other_instruction) = maybe_entry {
994 panic!("{other_instruction} and {instruction} both have opcode {opcode}.");
995 }
996 }
997 }
998
999 #[test]
1000 fn number_of_instruction_bits_is_correct() {
1001 let all_opcodes = Instruction::iter().map(|instruction| instruction.opcode());
1002 let highest_opcode = all_opcodes.max().unwrap();
1003 let num_required_bits_for_highest_opcode = highest_opcode.ilog2() + 1;
1004 assert!(InstructionBit::COUNT == num_required_bits_for_highest_opcode as usize);
1005 }
1006
1007 #[test]
1008 fn parse_push_pop() {
1009 let program = triton_program!(push 1 push 1 add pop 2);
1010 let instructions = program.into_iter().collect_vec();
1011 let expected = vec![
1012 Instruction::Push(bfe!(1)),
1013 Instruction::Push(bfe!(1)),
1014 Instruction::Add,
1015 Instruction::Pop(NumberOfWords::N2),
1016 ];
1017
1018 assert!(expected == instructions);
1019 }
1020
1021 #[test]
1022 #[should_panic(expected = "Duplicate label: foo")]
1023 fn fail_on_duplicate_labels() {
1024 triton_program!(
1025 push 2
1026 call foo
1027 bar: push 2
1028 foo: push 3
1029 foo: push 4
1030 halt
1031 );
1032 }
1033
1034 #[test]
1035 fn ib_registers_are_binary() {
1036 for instruction in ALL_INSTRUCTIONS {
1037 for instruction_bit in InstructionBit::iter() {
1038 let ib_value = instruction.ib(instruction_bit);
1039 assert!(ib_value.is_zero() ^ ib_value.is_one());
1040 }
1041 }
1042 }
1043
1044 #[test]
1045 fn instruction_to_opcode_to_instruction_is_consistent() {
1046 for instr in ALL_INSTRUCTIONS {
1047 assert!(instr == instr.opcode().try_into().unwrap());
1048 }
1049 }
1050
1051 #[test]
1054 fn list_of_all_instructions_contains_unique_instructions() {
1055 assert!(ALL_INSTRUCTIONS.into_iter().all_unique());
1056 assert!(ALL_INSTRUCTION_NAMES.into_iter().all_unique());
1057 }
1058
1059 #[test]
1060 fn convert_various_types_to_instructions() {
1061 let _push = Instruction::try_from(1_usize).unwrap();
1062 let _dup = Instruction::try_from(9_u64).unwrap();
1063 let _swap = Instruction::try_from(17_u32).unwrap();
1064 let _pop = Instruction::try_from(3_usize).unwrap();
1065 }
1066
1067 #[test]
1068 fn change_arguments_of_various_instructions() {
1069 assert!(Instruction::Push(bfe!(0)).change_arg(bfe!(7)).is_ok());
1070 assert!(Instruction::Dup(OpStackElement::ST0)
1071 .change_arg(bfe!(1024))
1072 .is_err());
1073 assert!(Instruction::Swap(OpStackElement::ST0)
1074 .change_arg(bfe!(1337))
1075 .is_err());
1076 assert!(Instruction::Swap(OpStackElement::ST0)
1077 .change_arg(bfe!(0))
1078 .is_ok());
1079 assert!(Instruction::Swap(OpStackElement::ST0)
1080 .change_arg(bfe!(1))
1081 .is_ok());
1082 assert!(Instruction::Pop(NumberOfWords::N4)
1083 .change_arg(bfe!(0))
1084 .is_err());
1085 assert!(Instruction::Pop(NumberOfWords::N1)
1086 .change_arg(bfe!(2))
1087 .is_ok());
1088 assert!(Instruction::Nop.change_arg(bfe!(7)).is_err());
1089 }
1090
1091 #[test]
1092 fn print_various_instructions() {
1093 println!("instruction_push: {:?}", Instruction::Push(bfe!(7)));
1094 println!("instruction_assert: {}", Instruction::Assert);
1095 println!("instruction_invert: {:?}", Instruction::Invert);
1096 println!(
1097 "instruction_dup: {}",
1098 Instruction::Dup(OpStackElement::ST14)
1099 );
1100 }
1101
1102 #[test]
1103 fn instruction_size_is_consistent_with_having_arguments() {
1104 for instruction in Instruction::iter() {
1105 match instruction.arg() {
1106 Some(_) => assert!(2 == instruction.size()),
1107 None => assert!(1 == instruction.size()),
1108 }
1109 }
1110 }
1111
1112 #[test]
1113 fn opcodes_are_consistent_with_argument_indication_bit() {
1114 let argument_indicator_bit_mask = 1;
1115 for instruction in Instruction::iter() {
1116 let opcode = instruction.opcode();
1117 println!("Testing instruction {instruction} with opcode {opcode}.");
1118 let has_arg = instruction.arg().is_some();
1119 assert!(has_arg == (opcode & argument_indicator_bit_mask != 0));
1120 }
1121 }
1122
1123 #[test]
1124 fn opcodes_are_consistent_with_shrink_stack_indication_bit() {
1125 let shrink_stack_indicator_bit_mask = 2;
1126 for instruction in Instruction::iter() {
1127 let opcode = instruction.opcode();
1128 println!("Testing instruction {instruction} with opcode {opcode}.");
1129 let shrinks_stack = instruction.op_stack_size_influence() < 0;
1130 assert!(shrinks_stack == (opcode & shrink_stack_indicator_bit_mask != 0));
1131 }
1132 }
1133
1134 #[test]
1135 fn opcodes_are_consistent_with_u32_indication_bit() {
1136 let u32_indicator_bit_mask = 4;
1137 for instruction in Instruction::iter() {
1138 let opcode = instruction.opcode();
1139 println!("Testing instruction {instruction} with opcode {opcode}.");
1140 assert!(instruction.is_u32_instruction() == (opcode & u32_indicator_bit_mask != 0));
1141 }
1142 }
1143
1144 #[test]
1145 fn instruction_bits_are_consistent() {
1146 for instruction_bit in InstructionBit::iter() {
1147 println!("Testing instruction bit {instruction_bit}.");
1148 let bit_index = usize::from(instruction_bit);
1149 let recovered_instruction_bit = InstructionBit::try_from(bit_index).unwrap();
1150 assert!(instruction_bit == recovered_instruction_bit);
1151 }
1152 }
1153
1154 #[test]
1155 fn instruction_bit_conversion_fails_for_invalid_bit_index() {
1156 let invalid_bit_index = rand::rng().random_range(InstructionBit::COUNT..=usize::MAX);
1157 let maybe_instruction_bit = InstructionBit::try_from(invalid_bit_index);
1158 assert!(maybe_instruction_bit.is_err());
1159 }
1160
1161 #[test]
1162 fn stringify_some_instructions() {
1163 let instructions = triton_asm!(push 3 invert push 2 mul push 1 add write_io 1 halt);
1164 let code = instructions.iter().join("\n");
1165 println!("{code}");
1166 }
1167
1168 #[test]
1169 fn labelled_instructions_act_on_op_stack_as_indicated() {
1170 for instruction in ALL_INSTRUCTIONS {
1171 let labelled_instruction = instruction.map_call_address(|_| "dummy_label".to_string());
1172 let labelled_instruction = LabelledInstruction::Instruction(labelled_instruction);
1173
1174 assert!(
1175 instruction.op_stack_size_influence()
1176 == labelled_instruction.op_stack_size_influence()
1177 );
1178 }
1179 }
1180
1181 #[test]
1182 fn labels_indicate_no_change_to_op_stack() {
1183 let labelled_instruction = LabelledInstruction::Label("dummy_label".to_string());
1184 assert!(0 == labelled_instruction.op_stack_size_influence());
1185 }
1186
1187 #[test]
1188 fn can_change_arg() {
1189 for instr in ALL_INSTRUCTIONS {
1190 if let Some(arg) = instr.arg() {
1191 let new_instr = instr.change_arg(arg + bfe!(1)).unwrap();
1192 assert_eq!(instr.opcode(), new_instr.opcode());
1193 assert_ne!(instr, new_instr);
1194 } else {
1195 assert!(instr.change_arg(bfe!(0)).is_err())
1196 }
1197 }
1198 }
1199}