• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TritonVM / triton-vm / 10351846737

12 Aug 2024 12:25PM UTC coverage: 99.297%. Remained the same
10351846737

push

github

jan-ferdinand
feat: Add instruction `addi`

50 of 51 new or added lines in 5 files covered. (98.04%)

8 existing lines in 3 files now uncovered.

44212 of 44525 relevant lines covered (99.3%)

3866455.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.1
/triton-vm/src/instruction.rs
1
use std::collections::HashMap;
2
use std::fmt::Display;
3
use std::fmt::Formatter;
4
use std::fmt::Result as FmtResult;
5
use std::result;
6

7
use arbitrary::Arbitrary;
8
use arbitrary::Unstructured;
9
use get_size::GetSize;
10
use itertools::Itertools;
11
use lazy_static::lazy_static;
12
use num_traits::ConstZero;
13
use serde_derive::Deserialize;
14
use serde_derive::Serialize;
15
use strum::EnumCount;
16
use strum::EnumIter;
17
use strum::IntoEnumIterator;
18
use twenty_first::prelude::*;
19

20
use AnInstruction::*;
21

22
use crate::error::InstructionError;
23
use crate::instruction::InstructionBit::*;
24
use crate::op_stack::NumberOfWords::*;
25
use crate::op_stack::OpStackElement::*;
26
use crate::op_stack::*;
27

28
type Result<T> = result::Result<T, InstructionError>;
29

30
/// An `Instruction` has `call` addresses encoded as absolute integers.
31
pub type Instruction = AnInstruction<BFieldElement>;
32

33
pub const ALL_INSTRUCTIONS: [Instruction; Instruction::COUNT] =
34
    all_instructions_with_default_args();
35
pub const ALL_INSTRUCTION_NAMES: [&str; Instruction::COUNT] = all_instruction_names();
36

37
lazy_static! {
38
    pub static ref OPCODE_TO_INSTRUCTION_MAP: HashMap<u32, Instruction> = {
39
        let mut opcode_to_instruction_map = HashMap::new();
40
        for instruction in Instruction::iter() {
41
            opcode_to_instruction_map.insert(instruction.opcode(), instruction);
42
        }
43
        opcode_to_instruction_map
44
    };
45
}
46

47
/// A `LabelledInstruction` has `call` addresses encoded as label names.
48
#[derive(Debug, Clone, Eq, PartialEq, Hash, EnumCount)]
49
pub enum LabelledInstruction {
50
    /// An instructions from the [instruction set architecture][isa].
51
    ///
52
    /// [isa]: https://triton-vm.org/spec/isa.html
53
    Instruction(AnInstruction<String>),
54

55
    /// Labels look like "`<name>:`" and are translated into absolute addresses.
56
    Label(String),
57

58
    Breakpoint,
59

60
    TypeHint(TypeHint),
61
}
62

63
/// A hint about a range of stack elements. Helps debugging programs written for Triton VM.
64
/// **Does not enforce types.**
65
///
66
/// Usually constructed by parsing special annotations in the assembly code, for example:
67
/// ```tasm
68
/// hint variable_name: the_type = stack[0]
69
/// hint my_list = stack[1..4]
70
/// ```
UNCOV
71
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, GetSize)]
×
72
pub struct TypeHint {
73
    pub starting_index: usize,
74
    pub length: usize,
75

76
    /// The name of the type, _e.g._, `u32`, `list`, `Digest`, et cetera.
77
    pub type_name: Option<String>,
78

79
    /// The name of the variable.
80
    pub variable_name: String,
81
}
82

83
impl Display for TypeHint {
84
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
1,279✔
85
        let variable = &self.variable_name;
1,279✔
86

1,279✔
87
        let format_type = |t| format!(": {t}");
1,279✔
88
        let maybe_type = self.type_name.as_ref();
1,279✔
89
        let type_name = maybe_type.map(format_type).unwrap_or_default();
1,279✔
90

1,279✔
91
        let start = self.starting_index;
1,279✔
92
        let range = match self.length {
1,279✔
93
            1 => format!("{start}"),
71✔
94
            _ => format!("{start}..{end}", end = start + self.length),
1,208✔
95
        };
96

97
        write!(f, "hint {variable}{type_name} = stack[{range}]")
1,279✔
98
    }
1,279✔
99
}
100

101
impl<'a> Arbitrary<'a> for TypeHint {
102
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
2,276✔
103
        let starting_index = u.arbitrary()?;
2,276✔
104
        let length = u.int_in_range(1..=500)?;
2,276✔
105
        let type_name = match u.arbitrary()? {
2,276✔
106
            true => Some(u.arbitrary::<TypeHintTypeName>()?.into()),
1,045✔
107
            false => None,
1,231✔
108
        };
109
        let variable_name = u.arbitrary::<TypeHintVariableName>()?.into();
2,276✔
110

2,276✔
111
        let type_hint = Self {
2,276✔
112
            starting_index,
2,276✔
113
            length,
2,276✔
114
            type_name,
2,276✔
115
            variable_name,
2,276✔
116
        };
2,276✔
117
        Ok(type_hint)
2,276✔
118
    }
2,276✔
119
}
120

121
impl LabelledInstruction {
122
    pub const fn op_stack_size_influence(&self) -> i32 {
44✔
123
        match self {
44✔
124
            LabelledInstruction::Instruction(instruction) => instruction.op_stack_size_influence(),
43✔
125
            _ => 0,
1✔
126
        }
127
    }
44✔
128
}
129

130
impl Display for LabelledInstruction {
131
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
108,255✔
132
        match self {
108,255✔
133
            LabelledInstruction::Instruction(instruction) => write!(f, "{instruction}"),
106,660✔
134
            LabelledInstruction::Label(label) => write!(f, "{label}:"),
44✔
135
            LabelledInstruction::Breakpoint => write!(f, "break"),
528✔
136
            LabelledInstruction::TypeHint(type_hint) => write!(f, "{type_hint}"),
1,023✔
137
        }
138
    }
108,255✔
139
}
140

141
/// A Triton VM instruction. See the
142
/// [Instruction Set Architecture](https://triton-vm.org/spec/isa.html)
143
/// for more details.
144
///
145
/// The type parameter `Dest` describes the type of addresses (absolute or labels).
146
#[derive(
147
    Debug,
148
    Copy,
149
    Clone,
150
    Eq,
151
    PartialEq,
152
    Hash,
153
    EnumCount,
154
    EnumIter,
16,922✔
155
    Serialize,
156
    Deserialize,
470✔
UNCOV
157
    GetSize,
×
158
    Arbitrary,
108,056✔
159
)]
160
pub enum AnInstruction<Dest: PartialEq + Default> {
161
    // OpStack manipulation
162
    Pop(NumberOfWords),
163
    Push(BFieldElement),
164
    Divine(NumberOfWords),
165
    Dup(OpStackElement),
166
    Swap(OpStackElement),
167

168
    // Control flow
169
    Halt,
170
    Nop,
171
    Skiz,
172
    Call(Dest),
173
    Return,
174
    Recurse,
175
    RecurseOrReturn,
176
    Assert,
177

178
    // Memory access
179
    ReadMem(NumberOfWords),
180
    WriteMem(NumberOfWords),
181

182
    // Hashing-related
183
    Hash,
184
    AssertVector,
185
    SpongeInit,
186
    SpongeAbsorb,
187
    SpongeAbsorbMem,
188
    SpongeSqueeze,
189

190
    // Base field arithmetic on stack
191
    Add,
192
    AddI(BFieldElement),
193
    Mul,
194
    Invert,
195
    Eq,
196

197
    // Bitwise arithmetic on stack
198
    Split,
199
    Lt,
200
    And,
201
    Xor,
202
    Log2Floor,
203
    Pow,
204
    DivMod,
205
    PopCount,
206

207
    // Extension field arithmetic on stack
208
    XxAdd,
209
    XxMul,
210
    XInvert,
211
    XbMul,
212

213
    // Read/write
214
    ReadIo(NumberOfWords),
215
    WriteIo(NumberOfWords),
216

217
    // Many-in-One
218
    MerkleStep,
219
    XxDotStep,
220
    XbDotStep,
221
}
222

223
impl<Dest: PartialEq + Default> AnInstruction<Dest> {
224
    /// Assign a unique positive integer to each `Instruction`.
225
    pub const fn opcode(&self) -> u32 {
28,249,675✔
226
        match self {
28,249,675✔
227
            Pop(_) => 3,
3,918,805✔
228
            Push(_) => 1,
6,522,443✔
229
            Divine(_) => 9,
5,356✔
230
            Dup(_) => 17,
250,994✔
231
            Swap(_) => 25,
382,527✔
232
            Halt => 0,
14,477✔
233
            Nop => 8,
1,371✔
234
            Skiz => 2,
126,141✔
235
            Call(_) => 33,
18,515✔
236
            Return => 16,
32,388✔
237
            Recurse => 24,
148,975✔
238
            RecurseOrReturn => 32,
1,956✔
239
            Assert => 10,
9,115✔
240
            ReadMem(_) => 41,
3,416,205✔
241
            WriteMem(_) => 11,
488,394✔
242
            Hash => 18,
862,990✔
243
            AssertVector => 26,
5,305✔
244
            SpongeInit => 40,
1,542,362✔
245
            SpongeAbsorb => 34,
468,806✔
246
            SpongeAbsorbMem => 48,
403,850✔
247
            SpongeSqueeze => 56,
466,578✔
248
            Add => 42,
241,787✔
249
            AddI(_) => 49,
1,139✔
250
            Mul => 50,
28,695✔
251
            Invert => 64,
1,384✔
252
            Eq => 58,
20,974✔
253
            Split => 4,
429,538✔
254
            Lt => 6,
427,885✔
255
            And => 14,
428,129✔
256
            Xor => 22,
426,397✔
257
            Log2Floor => 12,
428,394✔
258
            Pow => 30,
428,518✔
259
            DivMod => 20,
850,977✔
260
            PopCount => 28,
427,749✔
261
            XxAdd => 66,
1,830,293✔
262
            XxMul => 74,
1,880,476✔
263
            XInvert => 72,
1,417✔
264
            XbMul => 82,
1,413✔
265
            ReadIo(_) => 57,
17,046✔
266
            WriteIo(_) => 19,
3,926✔
267
            MerkleStep => 36,
1,277,058✔
268
            XxDotStep => 80,
4,267✔
269
            XbDotStep => 88,
4,660✔
270
        }
271
    }
28,249,675✔
272

273
    pub(crate) const fn name(&self) -> &'static str {
174,685✔
274
        match self {
174,685✔
275
            Pop(_) => "pop",
34,661✔
276
            Push(_) => "push",
67,822✔
277
            Divine(_) => "divine",
47✔
278
            Dup(_) => "dup",
87✔
279
            Swap(_) => "swap",
89✔
280
            Halt => "halt",
54✔
281
            Nop => "nop",
48✔
282
            Skiz => "skiz",
26✔
283
            Call(_) => "call",
51✔
284
            Return => "return",
45✔
285
            Recurse => "recurse",
23✔
286
            RecurseOrReturn => "recurse_or_return",
22✔
287
            Assert => "assert",
2,752✔
288
            ReadMem(_) => "read_mem",
6,801✔
289
            WriteMem(_) => "write_mem",
1,107✔
290
            Hash => "hash",
2,595✔
291
            AssertVector => "assert_vector",
39✔
292
            SpongeInit => "sponge_init",
26,298✔
293
            SpongeAbsorb => "sponge_absorb",
4,709✔
294
            SpongeAbsorbMem => "sponge_absorb_mem",
2,785✔
295
            SpongeSqueeze => "sponge_squeeze",
4,737✔
296
            Add => "add",
27✔
297
            AddI(_) => "addi",
41✔
298
            Mul => "mul",
33✔
299
            Invert => "invert",
29✔
300
            Eq => "eq",
2,745✔
301
            Split => "split",
42✔
302
            Lt => "lt",
30✔
303
            And => "and",
29✔
304
            Xor => "xor",
34✔
305
            Log2Floor => "log_2_floor",
51✔
306
            Pow => "pow",
49✔
307
            DivMod => "div_mod",
38✔
308
            PopCount => "pop_count",
36✔
309
            XxAdd => "xx_add",
3,613✔
310
            XxMul => "xx_mul",
3,703✔
311
            XInvert => "x_invert",
35✔
312
            XbMul => "xb_mul",
32✔
313
            ReadIo(_) => "read_io",
2,632✔
314
            WriteIo(_) => "write_io",
69✔
315
            MerkleStep => "merkle_step",
34✔
316
            XxDotStep => "xx_dot_step",
3,045✔
317
            XbDotStep => "xb_dot_step",
3,540✔
318
        }
319
    }
174,685✔
320

321
    pub const fn opcode_b(&self) -> BFieldElement {
26,605,774✔
322
        BFieldElement::new(self.opcode() as u64)
26,605,774✔
323
    }
26,605,774✔
324

325
    /// Number of words required to represent the instruction.
326
    pub fn size(&self) -> usize {
225,254,441✔
327
        match self {
225,254,441✔
328
            Pop(_) | Push(_) => 2,
132,704,208✔
329
            Divine(_) => 2,
3,294✔
330
            Dup(_) | Swap(_) => 2,
24,740✔
331
            Call(_) => 2,
10,697✔
332
            ReadMem(_) | WriteMem(_) => 2,
50,488,224✔
333
            AddI(_) => 2,
460✔
334
            ReadIo(_) | WriteIo(_) => 2,
29,347✔
335
            _ => 1,
41,993,471✔
336
        }
337
    }
225,254,441✔
338

339
    /// Get the i'th instruction bit
340
    pub fn ib(&self, arg: InstructionBit) -> BFieldElement {
1,636,215✔
341
        let opcode = self.opcode();
1,636,215✔
342
        let bit_number: usize = arg.into();
1,636,215✔
343

1,636,215✔
344
        ((opcode >> bit_number) & 1).into()
1,636,215✔
345
    }
1,636,215✔
346

347
    pub(crate) fn map_call_address<F, NewDest>(&self, f: F) -> AnInstruction<NewDest>
53,545,298✔
348
    where
53,545,298✔
349
        F: FnOnce(&Dest) -> NewDest,
53,545,298✔
350
        NewDest: PartialEq + Default,
53,545,298✔
351
    {
53,545,298✔
352
        match self {
53,545,298✔
353
            Pop(x) => Pop(*x),
11,806,647✔
354
            Push(x) => Push(*x),
18,870,881✔
355
            Divine(x) => Divine(*x),
426✔
356
            Dup(x) => Dup(*x),
2,144✔
357
            Swap(x) => Swap(*x),
2,051✔
358
            Halt => Halt,
4,931✔
359
            Nop => Nop,
203✔
360
            Skiz => Skiz,
199✔
361
            Call(label) => Call(f(label)),
1,569✔
362
            Return => Return,
917✔
363
            Recurse => Recurse,
139✔
364
            RecurseOrReturn => RecurseOrReturn,
381✔
365
            Assert => Assert,
3,592✔
366
            ReadMem(x) => ReadMem(*x),
10,200,483✔
367
            WriteMem(x) => WriteMem(*x),
1,446,101✔
368
            Hash => Hash,
2,951✔
369
            AssertVector => AssertVector,
663✔
370
            SpongeInit => SpongeInit,
26,691✔
371
            SpongeAbsorb => SpongeAbsorb,
4,805✔
372
            SpongeAbsorbMem => SpongeAbsorbMem,
2,881✔
373
            SpongeSqueeze => SpongeSqueeze,
5,093✔
374
            Add => Add,
1,901✔
375
            AddI(x) => AddI(*x),
203✔
376
            Mul => Mul,
1,744✔
377
            Invert => Invert,
362✔
378
            Eq => Eq,
3,594✔
379
            Split => Split,
421✔
380
            Lt => Lt,
122✔
381
            And => And,
127✔
382
            Xor => Xor,
141✔
383
            Log2Floor => Log2Floor,
443✔
384
            Pow => Pow,
177✔
385
            DivMod => DivMod,
162✔
386
            PopCount => PopCount,
136✔
387
            XxAdd => XxAdd,
5,494,156✔
388
            XxMul => XxMul,
5,644,838✔
389
            XInvert => XInvert,
390✔
390
            XbMul => XbMul,
379✔
391
            ReadIo(x) => ReadIo(*x),
3,569✔
392
            WriteIo(x) => WriteIo(*x),
1,778✔
393
            MerkleStep => MerkleStep,
133✔
394
            XxDotStep => XxDotStep,
3,153✔
395
            XbDotStep => XbDotStep,
3,621✔
396
        }
397
    }
53,545,298✔
398

399
    pub const fn op_stack_size_influence(&self) -> i32 {
20,238,581✔
400
        match self {
20,238,581✔
401
            Pop(n) => -(n.num_words() as i32),
3,934,386✔
402
            Push(_) => 1,
6,694,457✔
403
            Divine(n) => n.num_words() as i32,
1,008✔
404
            Dup(_) => 1,
305,291✔
405
            Swap(_) => 0,
202,763✔
406
            Halt => 0,
4,148✔
407
            Nop => 0,
171✔
408
            Skiz => -1,
271,581✔
409
            Call(_) => 0,
71,676✔
410
            Return => 0,
71,315✔
411
            Recurse => 0,
248,178✔
412
            RecurseOrReturn => 0,
753✔
413
            Assert => -1,
26,444✔
414
            ReadMem(n) => n.num_words() as i32,
3,432,196✔
415
            WriteMem(n) => -(n.num_words() as i32),
506,978✔
416
            Hash => -5,
3,554✔
417
            AssertVector => -5,
23,374✔
418
            SpongeInit => 0,
97,680✔
419
            SpongeAbsorb => -10,
11,205✔
420
            SpongeAbsorbMem => 0,
2,944✔
421
            SpongeSqueeze => 10,
11,471✔
422
            Add => -1,
85,611✔
423
            AddI(_) => 0,
99✔
424
            Mul => -1,
9,697✔
425
            Invert => 0,
357✔
426
            Eq => -1,
247,906✔
427
            Split => 1,
437✔
428
            Lt => -1,
184✔
429
            And => -1,
141✔
430
            Xor => -1,
140✔
431
            Log2Floor => 0,
469✔
432
            Pow => -1,
244✔
433
            DivMod => 0,
149✔
434
            PopCount => 0,
106✔
435
            XxAdd => -3,
1,832,797✔
436
            XxMul => -3,
1,883,071✔
437
            XInvert => 0,
363✔
438
            XbMul => -1,
361✔
439
            ReadIo(n) => n.num_words() as i32,
51,465✔
440
            WriteIo(n) => -(n.num_words() as i32),
1,917✔
441
            MerkleStep => 0,
194,743✔
442
            XxDotStep => 0,
3,138✔
443
            XbDotStep => 0,
3,613✔
444
        }
445
    }
20,238,581✔
446

447
    /// Indicates whether the instruction operates on base field elements that are also u32s.
448
    pub fn is_u32_instruction(&self) -> bool {
428,382✔
449
        matches!(
427,406✔
450
            self,
428,382✔
451
            Split | Lt | And | Xor | Log2Floor | Pow | DivMod | PopCount | MerkleStep
452
        )
453
    }
428,382✔
454
}
455

456
impl<Dest: Display + PartialEq + Default> Display for AnInstruction<Dest> {
457
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
174,529✔
458
        write!(f, "{}", self.name())?;
174,529✔
459
        match self {
174,529✔
460
            Push(arg) => write!(f, " {arg}"),
67,793✔
461
            Pop(arg) | Divine(arg) => write!(f, " {arg}"),
34,689✔
462
            Dup(arg) | Swap(arg) => write!(f, " {arg}"),
170✔
463
            Call(arg) => write!(f, " {arg}"),
49✔
464
            ReadMem(arg) | WriteMem(arg) => write!(f, " {arg}"),
7,896✔
465
            AddI(arg) => write!(f, " {arg}"),
39✔
466
            ReadIo(arg) | WriteIo(arg) => write!(f, " {arg}"),
2,697✔
467
            _ => Ok(()),
61,196✔
468
        }
469
    }
174,529✔
470
}
471

472
impl Instruction {
473
    /// Get the argument of the instruction, if it has one.
474
    pub fn arg(&self) -> Option<BFieldElement> {
55,076,545✔
475
        match self {
55,076,545✔
476
            Push(arg) | Call(arg) => Some(*arg),
18,965,595✔
477
            Pop(arg) | Divine(arg) => Some(arg.into()),
11,705,198✔
478
            Dup(arg) | Swap(arg) => Some(arg.into()),
316,106✔
479
            ReadMem(arg) | WriteMem(arg) => Some(arg.into()),
11,655,643✔
480
            AddI(arg) => Some(*arg),
181✔
481
            ReadIo(arg) | WriteIo(arg) => Some(arg.into()),
15,316✔
482
            _ => None,
12,418,506✔
483
        }
484
    }
55,076,545✔
485

486
    /// Change the argument of the instruction, if it has one. Returns an `Err` if the instruction
487
    /// does not have an argument or if the argument is out of range.
488
    pub fn change_arg(self, new_arg: BFieldElement) -> Result<Self> {
28,315,104✔
489
        let illegal_argument_error = InstructionError::IllegalArgument(self, new_arg);
28,315,104✔
490
        let num_words = new_arg.try_into().map_err(|_| illegal_argument_error);
28,315,104✔
491
        let op_stack_element = new_arg.try_into().map_err(|_| illegal_argument_error);
28,315,104✔
492

493
        let new_instruction = match self {
28,315,104✔
494
            Pop(_) => Pop(num_words?),
7,791,616✔
495
            Push(_) => Push(new_arg),
12,584,339✔
496
            Divine(_) => Divine(num_words?),
1,239✔
497
            Dup(_) => Dup(op_stack_element?),
67,199✔
498
            Swap(_) => Swap(op_stack_element?),
91,670✔
499
            Call(_) => Call(new_arg),
4,603✔
500
            ReadMem(_) => ReadMem(num_words?),
6,804,683✔
501
            WriteMem(_) => WriteMem(num_words?),
965,491✔
502
            AddI(_) => AddI(new_arg),
42✔
503
            ReadIo(_) => ReadIo(num_words?),
3,871✔
504
            WriteIo(_) => WriteIo(num_words?),
318✔
505
            _ => return Err(illegal_argument_error),
33✔
506
        };
507

508
        Ok(new_instruction)
28,315,068✔
509
    }
28,315,104✔
510
}
511

512
impl TryFrom<u32> for Instruction {
513
    type Error = InstructionError;
514

515
    fn try_from(opcode: u32) -> Result<Self> {
37,030,464✔
516
        OPCODE_TO_INSTRUCTION_MAP
37,030,464✔
517
            .get(&opcode)
37,030,464✔
518
            .copied()
37,030,464✔
519
            .ok_or(InstructionError::InvalidOpcode(opcode))
37,030,464✔
520
    }
37,030,464✔
521
}
522

523
impl TryFrom<u64> for Instruction {
524
    type Error = InstructionError;
525

526
    fn try_from(opcode: u64) -> Result<Self> {
2,372✔
527
        let opcode = u32::try_from(opcode)?;
2,372✔
528
        opcode.try_into()
2,372✔
529
    }
2,372✔
530
}
531

532
impl TryFrom<usize> for Instruction {
533
    type Error = InstructionError;
534

535
    fn try_from(opcode: usize) -> Result<Self> {
2✔
536
        let opcode = u32::try_from(opcode)?;
2✔
537
        opcode.try_into()
2✔
538
    }
2✔
539
}
540

541
impl TryFrom<BFieldElement> for Instruction {
542
    type Error = InstructionError;
543

544
    fn try_from(opcode: BFieldElement) -> Result<Self> {
37,028,560✔
545
        let opcode = u32::try_from(opcode)?;
37,028,560✔
546
        opcode.try_into()
37,028,046✔
547
    }
37,028,560✔
548
}
549

550
/// A list of all instructions with default arguments, if any.
551
const fn all_instructions_with_default_args() -> [AnInstruction<BFieldElement>; Instruction::COUNT]
4✔
552
{
4✔
553
    [
4✔
554
        Pop(N1),
4✔
555
        Push(BFieldElement::ZERO),
4✔
556
        Divine(N1),
4✔
557
        Dup(ST0),
4✔
558
        Swap(ST0),
4✔
559
        Halt,
4✔
560
        Nop,
4✔
561
        Skiz,
4✔
562
        Call(BFieldElement::ZERO),
4✔
563
        Return,
4✔
564
        Recurse,
4✔
565
        RecurseOrReturn,
4✔
566
        Assert,
4✔
567
        ReadMem(N1),
4✔
568
        WriteMem(N1),
4✔
569
        Hash,
4✔
570
        AssertVector,
4✔
571
        SpongeInit,
4✔
572
        SpongeAbsorb,
4✔
573
        SpongeAbsorbMem,
4✔
574
        SpongeSqueeze,
4✔
575
        Add,
4✔
576
        AddI(BFieldElement::ZERO),
4✔
577
        Mul,
4✔
578
        Invert,
4✔
579
        Eq,
4✔
580
        Split,
4✔
581
        Lt,
4✔
582
        And,
4✔
583
        Xor,
4✔
584
        Log2Floor,
4✔
585
        Pow,
4✔
586
        DivMod,
4✔
587
        PopCount,
4✔
588
        XxAdd,
4✔
589
        XxMul,
4✔
590
        XInvert,
4✔
591
        XbMul,
4✔
592
        ReadIo(N1),
4✔
593
        WriteIo(N1),
4✔
594
        MerkleStep,
4✔
595
        XxDotStep,
4✔
596
        XbDotStep,
4✔
597
    ]
4✔
598
}
4✔
599

600
const fn all_instruction_names() -> [&'static str; Instruction::COUNT] {
1✔
601
    let mut names = [""; Instruction::COUNT];
1✔
602
    let mut i = 0;
1✔
603
    while i < Instruction::COUNT {
44✔
604
        names[i] = ALL_INSTRUCTIONS[i].name();
43✔
605
        i += 1;
43✔
606
    }
43✔
607
    names
1✔
608
}
1✔
609

610
/// Indicators for all the possible bits in an [`Instruction`].
611
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, EnumCount, EnumIter)]
660✔
612
pub enum InstructionBit {
613
    #[default]
614
    IB0,
615
    IB1,
616
    IB2,
617
    IB3,
618
    IB4,
619
    IB5,
620
    IB6,
621
}
622

623
impl Display for InstructionBit {
624
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
7✔
625
        let bit_index = usize::from(*self);
7✔
626
        write!(f, "{bit_index}")
7✔
627
    }
7✔
628
}
629

630
impl From<InstructionBit> for usize {
631
    fn from(instruction_bit: InstructionBit) -> Self {
1,636,229✔
632
        match instruction_bit {
1,636,229✔
633
            IB0 => 0,
233,747✔
634
            IB1 => 1,
233,747✔
635
            IB2 => 2,
233,747✔
636
            IB3 => 3,
233,747✔
637
            IB4 => 4,
233,747✔
638
            IB5 => 5,
233,747✔
639
            IB6 => 6,
233,747✔
640
        }
641
    }
1,636,229✔
642
}
643

644
impl TryFrom<usize> for InstructionBit {
645
    type Error = String;
646

647
    fn try_from(bit_index: usize) -> result::Result<Self, Self::Error> {
8✔
648
        match bit_index {
8✔
649
            0 => Ok(IB0),
1✔
650
            1 => Ok(IB1),
1✔
651
            2 => Ok(IB2),
1✔
652
            3 => Ok(IB3),
1✔
653
            4 => Ok(IB4),
1✔
654
            5 => Ok(IB5),
1✔
655
            6 => Ok(IB6),
1✔
656
            _ => Err(format!(
1✔
657
                "Index {bit_index} is out of range for `InstructionBit`."
1✔
658
            )),
1✔
659
        }
660
    }
8✔
661
}
662

663
impl<'a> Arbitrary<'a> for LabelledInstruction {
664
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
60,927✔
665
        let instruction = match u.choose_index(LabelledInstruction::COUNT)? {
60,927✔
666
            0 => u.arbitrary::<AnInstruction<String>>()?,
54,934✔
667
            1 => return Ok(Self::Label(u.arbitrary::<InstructionLabel>()?.into())),
1,994✔
668
            2 => return Ok(Self::Breakpoint),
1,979✔
669
            3 => return Ok(Self::TypeHint(u.arbitrary()?)),
2,020✔
UNCOV
670
            _ => unreachable!(),
×
671
        };
672
        let legal_label = String::from(u.arbitrary::<InstructionLabel>()?);
54,934✔
673
        let instruction = instruction.map_call_address(|_| legal_label.clone());
54,934✔
674

54,934✔
675
        Ok(Self::Instruction(instruction))
54,934✔
676
    }
60,927✔
677
}
678

679
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
680
struct InstructionLabel(String);
681

682
impl From<InstructionLabel> for String {
683
    fn from(label: InstructionLabel) -> Self {
56,928✔
684
        label.0
56,928✔
685
    }
56,928✔
686
}
687

688
impl<'a> Arbitrary<'a> for InstructionLabel {
689
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
56,928✔
690
        let legal_start_characters = ('a'..='z').chain('A'..='Z').chain('_'..='_');
56,928✔
691
        let legal_characters = legal_start_characters
56,928✔
692
            .clone()
56,928✔
693
            .chain('0'..='9')
56,928✔
694
            .chain('-'..='-')
56,928✔
695
            .collect_vec();
56,928✔
696

697
        let mut label = u.choose(&legal_start_characters.collect_vec())?.to_string();
56,928✔
698
        for _ in 0..u.arbitrary_len::<char>()? {
56,928✔
699
            label.push(*u.choose(&legal_characters)?);
44,533✔
700
        }
701
        while ALL_INSTRUCTION_NAMES.contains(&label.as_str()) {
56,928✔
UNCOV
702
            label.push(*u.choose(&legal_characters)?);
×
703
        }
704
        Ok(Self(label))
56,928✔
705
    }
56,928✔
706
}
707

708
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
709
struct TypeHintVariableName(String);
710

711
impl From<TypeHintVariableName> for String {
712
    fn from(label: TypeHintVariableName) -> Self {
2,276✔
713
        label.0
2,276✔
714
    }
2,276✔
715
}
716

717
impl<'a> Arbitrary<'a> for TypeHintVariableName {
718
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
2,276✔
719
        let legal_start_characters = 'a'..='z';
2,276✔
720
        let legal_characters = legal_start_characters
2,276✔
721
            .clone()
2,276✔
722
            .chain('0'..='9')
2,276✔
723
            .chain('_'..='_')
2,276✔
724
            .collect_vec();
2,276✔
725

726
        let mut variable_name = u.choose(&legal_start_characters.collect_vec())?.to_string();
2,276✔
727
        for _ in 0..u.arbitrary_len::<char>()? {
2,276✔
728
            variable_name.push(*u.choose(&legal_characters)?);
25,537✔
729
        }
730
        Ok(Self(variable_name))
2,276✔
731
    }
2,276✔
732
}
733

734
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
735
struct TypeHintTypeName(String);
736

737
impl From<TypeHintTypeName> for String {
738
    fn from(label: TypeHintTypeName) -> Self {
1,045✔
739
        label.0
1,045✔
740
    }
1,045✔
741
}
742

743
impl<'a> Arbitrary<'a> for TypeHintTypeName {
744
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
1,045✔
745
        let legal_start_characters = ('a'..='z').chain('A'..='Z');
1,045✔
746
        let legal_characters = legal_start_characters
1,045✔
747
            .clone()
1,045✔
748
            .chain('0'..='9')
1,045✔
749
            .collect_vec();
1,045✔
750

751
        let mut type_name = u.choose(&legal_start_characters.collect_vec())?.to_string();
1,045✔
752
        for _ in 0..u.arbitrary_len::<char>()? {
1,045✔
753
            type_name.push(*u.choose(&legal_characters)?);
14,497✔
754
        }
755
        Ok(Self(type_name))
1,045✔
756
    }
1,045✔
757
}
758

759
#[cfg(test)]
760
pub mod tests {
761
    use std::collections::HashMap;
762

763
    use assert2::assert;
764
    use assert2::let_assert;
765
    use itertools::Itertools;
766
    use num_traits::One;
767
    use num_traits::Zero;
768
    use rand::thread_rng;
769
    use rand::Rng;
770
    use strum::EnumCount;
771
    use strum::IntoEnumIterator;
772
    use strum::VariantNames;
773
    use twenty_first::prelude::*;
774

775
    use crate::instruction::*;
776
    use crate::op_stack::NUM_OP_STACK_REGISTERS;
777
    use crate::program::PublicInput;
778
    use crate::triton_asm;
779
    use crate::triton_program;
780
    use crate::vm::tests::test_program_for_call_recurse_return;
781
    use crate::vm::VMState;
782
    use crate::NonDeterminism;
783
    use crate::Program;
784

785
    #[derive(Debug, Copy, Clone, EnumCount, EnumIter, VariantNames)]
26,243✔
786
    pub enum InstructionBucket {
787
        HasArg,
788
        ShrinksStack,
789
        IsU32,
790
    }
791

792
    impl InstructionBucket {
793
        pub fn contains(self, instruction: Instruction) -> bool {
11,223✔
794
            match self {
11,223✔
795
                InstructionBucket::HasArg => instruction.arg().is_some(),
3,741✔
796
                InstructionBucket::ShrinksStack => instruction.op_stack_size_influence() < 0,
3,741✔
797
                InstructionBucket::IsU32 => instruction.is_u32_instruction(),
3,741✔
798
            }
799
        }
11,223✔
800

801
        pub fn flag(self) -> u32 {
11,247✔
802
            match self {
11,247✔
803
                InstructionBucket::HasArg => 1,
3,749✔
804
                InstructionBucket::ShrinksStack => 1 << 1,
3,749✔
805
                InstructionBucket::IsU32 => 1 << 2,
3,749✔
806
            }
807
        }
11,247✔
808
    }
809

810
    impl Instruction {
811
        pub fn flag_set(self) -> u32 {
3,741✔
812
            InstructionBucket::iter()
3,741✔
813
                .map(|bucket| u32::from(bucket.contains(self)) * bucket.flag())
11,223✔
814
                .fold(0, |acc, bit_flag| acc | bit_flag)
11,223✔
815
        }
3,741✔
816

817
        fn computed_opcode(self) -> u32 {
86✔
818
            let mut index_within_flag_set = 0;
86✔
819
            for other_instruction in Instruction::iter() {
1,892✔
820
                if other_instruction == self {
1,892✔
821
                    break;
86✔
822
                }
1,806✔
823
                if other_instruction.flag_set() == self.flag_set() {
1,806✔
824
                    index_within_flag_set += 1;
336✔
825
                }
1,470✔
826
            }
827

828
            index_within_flag_set * 2_u32.pow(InstructionBucket::COUNT as u32) + self.flag_set()
86✔
829
        }
86✔
830
    }
831

832
    #[test]
833
    fn computed_and_actual_opcodes_are_identical() {
1✔
834
        for instruction in Instruction::iter() {
44✔
835
            let opcode = instruction.computed_opcode();
43✔
836
            let name = instruction.name();
43✔
837
            println!("{opcode: >3} {name}");
43✔
838
        }
43✔
839

840
        for instruction in Instruction::iter() {
44✔
841
            let expected_opcode = instruction.computed_opcode();
43✔
842
            let opcode = instruction.opcode();
43✔
843
            assert!(expected_opcode == opcode, "{instruction}");
43✔
844
        }
845
    }
1✔
846

847
    #[test]
848
    fn opcodes_are_unique() {
1✔
849
        let mut opcodes_to_instruction_map = HashMap::new();
1✔
850
        for instruction in Instruction::iter() {
44✔
851
            let opcode = instruction.opcode();
43✔
852
            let maybe_entry = opcodes_to_instruction_map.insert(opcode, instruction);
43✔
853
            if let Some(other_instruction) = maybe_entry {
43✔
NEW
854
                panic!("{other_instruction} and {instruction} both have opcode {opcode}.");
×
855
            }
43✔
856
        }
857
    }
1✔
858

859
    #[test]
860
    fn number_of_instruction_bits_is_correct() {
1✔
861
        let all_opcodes = Instruction::iter().map(|instruction| instruction.opcode());
43✔
862
        let highest_opcode = all_opcodes.max().unwrap();
1✔
863
        let num_required_bits_for_highest_opcode = highest_opcode.ilog2() + 1;
1✔
864
        assert!(InstructionBit::COUNT == num_required_bits_for_highest_opcode as usize);
1✔
865
    }
1✔
866

867
    #[test]
868
    fn parse_push_pop() {
1✔
869
        let program = triton_program!(push 1 push 1 add pop 2);
1✔
870
        let instructions = program.into_iter().collect_vec();
1✔
871
        let expected = vec![Push(bfe!(1)), Push(bfe!(1)), Add, Pop(N2)];
1✔
872

1✔
873
        assert!(expected == instructions);
1✔
874
    }
1✔
875

876
    #[test]
877
    #[should_panic(expected = "Duplicate label: foo")]
878
    fn fail_on_duplicate_labels() {
1✔
879
        triton_program!(
1✔
880
            push 2
1✔
881
            call foo
1✔
882
            bar: push 2
1✔
883
            foo: push 3
1✔
884
            foo: push 4
1✔
885
            halt
1✔
886
        );
1✔
887
    }
1✔
888

889
    #[test]
890
    fn ib_registers_are_binary() {
1✔
891
        for instruction in ALL_INSTRUCTIONS {
44✔
892
            for instruction_bit in InstructionBit::iter() {
344✔
893
                let ib_value = instruction.ib(instruction_bit);
301✔
894
                assert!(ib_value.is_zero() ^ ib_value.is_one());
301✔
895
            }
896
        }
897
    }
1✔
898

899
    #[test]
900
    fn instruction_to_opcode_to_instruction_is_consistent() {
1✔
901
        for instr in ALL_INSTRUCTIONS {
44✔
902
            assert!(instr == instr.opcode().try_into().unwrap());
43✔
903
        }
904
    }
1✔
905

906
    #[test]
907
    /// Serves no other purpose than to increase code coverage results.
908
    fn run_constant_methods() {
1✔
909
        all_instructions_with_default_args();
1✔
910
        all_instruction_names();
1✔
911
    }
1✔
912

913
    #[test]
914
    fn convert_various_types_to_instructions() {
1✔
915
        let _push = Instruction::try_from(1_usize).unwrap();
1✔
916
        let _dup = Instruction::try_from(9_u64).unwrap();
1✔
917
        let _swap = Instruction::try_from(17_u32).unwrap();
1✔
918
        let _pop = Instruction::try_from(3_usize).unwrap();
1✔
919
    }
1✔
920

921
    #[test]
922
    fn change_arguments_of_various_instructions() {
1✔
923
        assert!(Push(bfe!(0)).change_arg(bfe!(7)).is_ok());
1✔
924
        assert!(Dup(ST0).change_arg(bfe!(1024)).is_err());
1✔
925
        assert!(Swap(ST0).change_arg(bfe!(1337)).is_err());
1✔
926
        assert!(Swap(ST0).change_arg(bfe!(0)).is_ok());
1✔
927
        assert!(Swap(ST0).change_arg(bfe!(1)).is_ok());
1✔
928
        assert!(Pop(N4).change_arg(bfe!(0)).is_err());
1✔
929
        assert!(Pop(N1).change_arg(bfe!(2)).is_ok());
1✔
930
        assert!(Nop.change_arg(bfe!(7)).is_err());
1✔
931
    }
1✔
932

933
    #[test]
934
    fn print_various_instructions() {
1✔
935
        println!("instruction_push: {:?}", Instruction::Push(bfe!(7)));
1✔
936
        println!("instruction_assert: {}", Instruction::Assert);
1✔
937
        println!("instruction_invert: {:?}", Instruction::Invert);
1✔
938
        println!("instruction_dup: {}", Instruction::Dup(ST14));
1✔
939
    }
1✔
940

941
    #[test]
942
    fn instruction_size_is_consistent_with_having_arguments() {
1✔
943
        for instruction in Instruction::iter() {
44✔
944
            match instruction.arg() {
43✔
945
                Some(_) => assert!(2 == instruction.size()),
11✔
946
                None => assert!(1 == instruction.size()),
32✔
947
            }
948
        }
949
    }
1✔
950

951
    #[test]
952
    fn opcodes_are_consistent_with_argument_indication_bit() {
1✔
953
        let argument_indicator_bit_mask = 1;
1✔
954
        for instruction in Instruction::iter() {
44✔
955
            let opcode = instruction.opcode();
43✔
956
            println!("Testing instruction {instruction} with opcode {opcode}.");
43✔
957
            let has_arg = instruction.arg().is_some();
43✔
958
            assert!(has_arg == (opcode & argument_indicator_bit_mask != 0));
43✔
959
        }
960
    }
1✔
961

962
    #[test]
963
    fn opcodes_are_consistent_with_shrink_stack_indication_bit() {
1✔
964
        let shrink_stack_indicator_bit_mask = 2;
1✔
965
        for instruction in Instruction::iter() {
44✔
966
            let opcode = instruction.opcode();
43✔
967
            println!("Testing instruction {instruction} with opcode {opcode}.");
43✔
968
            let shrinks_stack = instruction.op_stack_size_influence() < 0;
43✔
969
            assert!(shrinks_stack == (opcode & shrink_stack_indicator_bit_mask != 0));
43✔
970
        }
971
    }
1✔
972

973
    #[test]
974
    fn opcodes_are_consistent_with_u32_indication_bit() {
1✔
975
        let u32_indicator_bit_mask = 4;
1✔
976
        for instruction in Instruction::iter() {
44✔
977
            let opcode = instruction.opcode();
43✔
978
            println!("Testing instruction {instruction} with opcode {opcode}.");
43✔
979
            assert!(instruction.is_u32_instruction() == (opcode & u32_indicator_bit_mask != 0));
43✔
980
        }
981
    }
1✔
982

983
    #[test]
984
    fn instruction_bits_are_consistent() {
1✔
985
        for instruction_bit in InstructionBit::iter() {
8✔
986
            println!("Testing instruction bit {instruction_bit}.");
7✔
987
            let bit_index = usize::from(instruction_bit);
7✔
988
            let recovered_instruction_bit = InstructionBit::try_from(bit_index).unwrap();
7✔
989
            assert!(instruction_bit == recovered_instruction_bit);
7✔
990
        }
991
    }
1✔
992

993
    #[test]
994
    fn instruction_bit_conversion_fails_for_invalid_bit_index() {
1✔
995
        let invalid_bit_index = thread_rng().gen_range(InstructionBit::COUNT..=usize::MAX);
1✔
996
        let maybe_instruction_bit = InstructionBit::try_from(invalid_bit_index);
1✔
997
        assert!(maybe_instruction_bit.is_err());
1✔
998
    }
1✔
999

1000
    #[test]
1001
    fn stringify_some_instructions() {
1✔
1002
        let instructions = triton_asm!(push 3 invert push 2 mul push 1 add write_io 1 halt);
1✔
1003
        let code = instructions.iter().join("\n");
1✔
1004
        println!("{code}");
1✔
1005
    }
1✔
1006

1007
    #[test]
1008
    fn instructions_act_on_op_stack_as_indicated() {
1✔
1009
        for test_instruction in all_instructions_with_default_args() {
44✔
1010
            let (program, stack_size_before_test_instruction) =
43✔
1011
                construct_test_program_for_instruction(test_instruction);
43✔
1012
            let public_input = PublicInput::from(bfe_array![0]);
43✔
1013
            let mock_digests = [Digest::default()];
43✔
1014
            let non_determinism = NonDeterminism::from(bfe_array![0]).with_digests(mock_digests);
43✔
1015

43✔
1016
            let mut vm_state = VMState::new(&program, public_input, non_determinism);
43✔
1017
            let_assert!(Ok(()) = vm_state.run());
43✔
1018
            let stack_size_after_test_instruction = vm_state.op_stack.len();
43✔
1019

43✔
1020
            let stack_size_difference = (stack_size_after_test_instruction as i32)
43✔
1021
                - (stack_size_before_test_instruction as i32);
43✔
1022
            assert!(
43✔
1023
                test_instruction.op_stack_size_influence() == stack_size_difference,
43✔
UNCOV
1024
                "{test_instruction}"
×
1025
            );
1026
        }
1027
    }
1✔
1028

1029
    fn construct_test_program_for_instruction(
43✔
1030
        instruction: AnInstruction<BFieldElement>,
43✔
1031
    ) -> (Program, usize) {
43✔
1032
        if matches!(instruction, Call(_) | Return | Recurse | RecurseOrReturn) {
43✔
1033
            // need jump stack setup
1034
            let program = test_program_for_call_recurse_return().program;
4✔
1035
            let stack_size = NUM_OP_STACK_REGISTERS;
4✔
1036
            (program, stack_size)
4✔
1037
        } else {
1038
            let num_push_instructions = 10;
39✔
1039
            let push_instructions = triton_asm![push 1; num_push_instructions];
39✔
1040
            let program = triton_program!(sponge_init {&push_instructions} {instruction} nop halt);
39✔
1041

1042
            let stack_size_when_reaching_test_instruction =
39✔
1043
                NUM_OP_STACK_REGISTERS + num_push_instructions;
39✔
1044
            (program, stack_size_when_reaching_test_instruction)
39✔
1045
        }
1046
    }
43✔
1047

1048
    #[test]
1049
    fn labelled_instructions_act_on_op_stack_as_indicated() {
1✔
1050
        for instruction in all_instructions_with_default_args() {
44✔
1051
            let labelled_instruction = instruction.map_call_address(|_| "dummy_label".to_string());
43✔
1052
            let labelled_instruction = LabelledInstruction::Instruction(labelled_instruction);
43✔
1053

43✔
1054
            assert!(
43✔
1055
                instruction.op_stack_size_influence()
43✔
1056
                    == labelled_instruction.op_stack_size_influence()
43✔
1057
            );
43✔
1058
        }
1059
    }
1✔
1060

1061
    #[test]
1062
    fn labels_indicate_no_change_to_op_stack() {
1✔
1063
        let labelled_instruction = LabelledInstruction::Label("dummy_label".to_string());
1✔
1064
        assert!(0 == labelled_instruction.op_stack_size_influence());
1✔
1065
    }
1✔
1066

1067
    #[test]
1068
    fn can_change_arg() {
1✔
1069
        for instruction in all_instructions_with_default_args() {
44✔
1070
            if let Some(arg) = instruction.arg() {
43✔
1071
                assert_ne!(
11✔
1072
                    instruction,
11✔
1073
                    (instruction.change_arg(arg + bfe!(1))).unwrap()
11✔
1074
                );
11✔
1075
            } else {
1076
                assert!(instruction.change_arg(bfe!(0)).is_err())
32✔
1077
            }
1078
        }
1079
    }
1✔
1080
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc