sp1_core_executor/
instruction.rs

1//! Instructions for the SP1 zkVM.
2
3use core::fmt::Debug;
4use serde::{Deserialize, Serialize};
5
6use crate::opcode::Opcode;
7
8/// RISC-V 32IM Instruction.
9///
10/// The structure of the instruction differs from the RISC-V ISA. We do not encode the instructions
11/// as 32-bit words, but instead use a custom encoding that is more friendly to decode in the
12/// SP1 zkVM.
13#[derive(Clone, Copy, Serialize, Deserialize)]
14#[repr(C)]
15pub struct Instruction {
16    /// The operation to execute.
17    pub opcode: Opcode,
18    /// The first operand.
19    pub op_a: u8,
20    /// The second operand.
21    pub op_b: u32,
22    /// The third operand.
23    pub op_c: u32,
24    /// Whether the second operand is an immediate value.
25    pub imm_b: bool,
26    /// Whether the third operand is an immediate value.
27    pub imm_c: bool,
28}
29
30impl Instruction {
31    /// Create a new [`RiscvInstruction`].
32    #[must_use]
33    pub const fn new(
34        opcode: Opcode,
35        op_a: u8,
36        op_b: u32,
37        op_c: u32,
38        imm_b: bool,
39        imm_c: bool,
40    ) -> Self {
41        Self { opcode, op_a, op_b, op_c, imm_b, imm_c }
42    }
43
44    /// Returns if the instruction is an ALU instruction.
45    #[must_use]
46    #[inline]
47    pub const fn is_alu_instruction(&self) -> bool {
48        matches!(
49            self.opcode,
50            Opcode::ADD |
51                Opcode::SUB |
52                Opcode::XOR |
53                Opcode::OR |
54                Opcode::AND |
55                Opcode::SLL |
56                Opcode::SRL |
57                Opcode::SRA |
58                Opcode::SLT |
59                Opcode::SLTU |
60                Opcode::MUL |
61                Opcode::MULH |
62                Opcode::MULHU |
63                Opcode::MULHSU |
64                Opcode::DIV |
65                Opcode::DIVU |
66                Opcode::REM |
67                Opcode::REMU
68        )
69    }
70
71    /// Returns if the instruction is a ecall instruction.
72    #[must_use]
73    #[inline]
74    pub const fn is_ecall_instruction(&self) -> bool {
75        matches!(self.opcode, Opcode::ECALL)
76    }
77
78    /// Returns if the instruction is a memory load instruction.
79    #[must_use]
80    #[inline]
81    pub const fn is_memory_load_instruction(&self) -> bool {
82        matches!(self.opcode, Opcode::LB | Opcode::LH | Opcode::LW | Opcode::LBU | Opcode::LHU)
83    }
84
85    /// Returns if the instruction is a memory store instruction.
86    #[must_use]
87    #[inline]
88    pub const fn is_memory_store_instruction(&self) -> bool {
89        matches!(self.opcode, Opcode::SB | Opcode::SH | Opcode::SW)
90    }
91
92    /// Returns if the instruction is a branch instruction.
93    #[must_use]
94    #[inline]
95    pub const fn is_branch_instruction(&self) -> bool {
96        matches!(
97            self.opcode,
98            Opcode::BEQ | Opcode::BNE | Opcode::BLT | Opcode::BGE | Opcode::BLTU | Opcode::BGEU
99        )
100    }
101
102    /// Returns if the instruction is a jump instruction.
103    #[must_use]
104    #[inline]
105    pub const fn is_jump_instruction(&self) -> bool {
106        matches!(self.opcode, Opcode::JAL | Opcode::JALR)
107    }
108
109    /// Returns if the instruction is an auipc instruction.
110    #[must_use]
111    #[inline]
112    pub const fn is_auipc_instruction(&self) -> bool {
113        matches!(self.opcode, Opcode::AUIPC)
114    }
115
116    /// Returns if the instruction is a divrem instruction.
117    #[must_use]
118    #[inline]
119    pub const fn is_divrem_instruction(&self) -> bool {
120        matches!(self.opcode, Opcode::DIV | Opcode::DIVU | Opcode::REM | Opcode::REMU)
121    }
122
123    /// Returns if the instruction is an ebreak instruction.
124    #[must_use]
125    #[inline]
126    pub const fn is_ebreak_instruction(&self) -> bool {
127        matches!(self.opcode, Opcode::EBREAK)
128    }
129
130    /// Returns if the instruction is an unimplemented instruction.
131    #[must_use]
132    #[inline]
133    pub const fn is_unimp_instruction(&self) -> bool {
134        matches!(self.opcode, Opcode::UNIMP)
135    }
136}
137
138impl Debug for Instruction {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        let mnemonic = self.opcode.mnemonic();
141        let op_a_formatted = format!("%x{}", self.op_a);
142        let op_b_formatted = if self.imm_b || self.opcode == Opcode::AUIPC {
143            format!("{}", self.op_b as i32)
144        } else {
145            format!("%x{}", self.op_b)
146        };
147        let op_c_formatted =
148            if self.imm_c { format!("{}", self.op_c as i32) } else { format!("%x{}", self.op_c) };
149
150        let width = 10;
151        write!(
152            f,
153            "{mnemonic:<width$} {op_a_formatted:<width$} {op_b_formatted:<width$} {op_c_formatted:<width$}"
154        )
155    }
156}