Skip to main content

ab_contract_file/
instruction.rs

1use ab_riscv_interpreter::prelude::*;
2use ab_riscv_macros::{instruction, instruction_execution};
3use ab_riscv_primitives::prelude::*;
4use core::fmt;
5use core::ops::ControlFlow;
6
7/// Registers used by contracts.
8///
9/// `ZEROSTORE` generic determines whether to zero `x0` register on write instead of checking
10/// register index on read. `match` loop usually performs better with branching, while threaded
11/// execution benefits from zeroing.
12#[derive(Debug, Default, Clone, Copy)]
13pub struct ContractRegisters<const ZEROSTORE: bool> {
14    regs: [u64; 32],
15}
16
17const impl<const ZEROSTORE: bool> RegisterFile<ContractRegister> for ContractRegisters<ZEROSTORE> {
18    #[inline(always)]
19    fn read(&self, reg: ContractRegister) -> u64 {
20        if reg == ContractRegister::Zero && !ZEROSTORE {
21            // Always zero
22            return 0;
23        }
24
25        // SAFETY: register offset is always within bounds
26        *unsafe { self.regs.get_unchecked(usize::from(reg as u8)) }
27    }
28
29    #[inline(always)]
30    fn write(&mut self, reg: ContractRegister, value: u64) {
31        // SAFETY: register offset is always within bounds
32        *unsafe { self.regs.get_unchecked_mut(usize::from(reg as u8)) } = value;
33
34        if ZEROSTORE {
35            // SAFETY: The register file always has at least one slot
36            *unsafe { self.regs.get_unchecked_mut(0) } = 0;
37        }
38    }
39}
40
41/// A register type used by contracts.
42///
43/// `gp` and `tp` registers are excluded because they are not present in contracts.
44#[derive(Clone, Copy)]
45#[derive_const(Default, Eq, PartialEq)]
46#[repr(u8)]
47pub enum ContractRegister {
48    /// Always zero: `x0`
49    #[default]
50    Zero = 0,
51    /// Return address: `x1`
52    Ra = 1,
53    /// Stack pointer: `x2`
54    Sp = 2,
55    // /// Global pointer: `x3`
56    // Gp = 3,
57    // /// Thread pointer: `x4`
58    // Tp = 4,
59    /// Temporary/alternate return address: `x5`
60    T0 = 5,
61    /// Temporary: `x6`
62    T1 = 6,
63    /// Temporary: `x7`
64    T2 = 7,
65    /// Saved register/frame pointer: `x8`
66    S0 = 8,
67    /// Saved register: `x9`
68    S1 = 9,
69    /// Function argument/return value: `x10`
70    A0 = 10,
71    /// Function argument/return value: `x11`
72    A1 = 11,
73    /// Function argument: `x12`
74    A2 = 12,
75    /// Function argument: `x13`
76    A3 = 13,
77    /// Function argument: `x14`
78    A4 = 14,
79    /// Function argument: `x15`
80    A5 = 15,
81    /// Function argument: `x16`
82    A6 = 16,
83    /// Function argument: `x17`
84    A7 = 17,
85    /// Saved register: `x18`
86    S2 = 18,
87    /// Saved register: `x19`
88    S3 = 19,
89    /// Saved register: `x20`
90    S4 = 20,
91    /// Saved register: `x21`
92    S5 = 21,
93    /// Saved register: `x22`
94    S6 = 22,
95    /// Saved register: `x23`
96    S7 = 23,
97    /// Saved register: `x24`
98    S8 = 24,
99    /// Saved register: `x25`
100    S9 = 25,
101    /// Saved register: `x26`
102    S10 = 26,
103    /// Saved register: `x27`
104    S11 = 27,
105    /// Temporary: `x28`
106    T3 = 28,
107    /// Temporary: `x29`
108    T4 = 29,
109    /// Temporary: `x30`
110    T5 = 30,
111    /// Temporary: `x31`
112    T6 = 31,
113}
114
115impl fmt::Display for ContractRegister {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match self {
118            Self::Zero => write!(f, "zero"),
119            Self::Ra => write!(f, "ra"),
120            Self::Sp => write!(f, "sp"),
121            Self::T0 => write!(f, "t0"),
122            Self::T1 => write!(f, "t1"),
123            Self::T2 => write!(f, "t2"),
124            Self::S0 => write!(f, "s0"),
125            Self::S1 => write!(f, "s1"),
126            Self::A0 => write!(f, "a0"),
127            Self::A1 => write!(f, "a1"),
128            Self::A2 => write!(f, "a2"),
129            Self::A3 => write!(f, "a3"),
130            Self::A4 => write!(f, "a4"),
131            Self::A5 => write!(f, "a5"),
132            Self::A6 => write!(f, "a6"),
133            Self::A7 => write!(f, "a7"),
134            Self::S2 => write!(f, "s2"),
135            Self::S3 => write!(f, "s3"),
136            Self::S4 => write!(f, "s4"),
137            Self::S5 => write!(f, "s5"),
138            Self::S6 => write!(f, "s6"),
139            Self::S7 => write!(f, "s7"),
140            Self::S8 => write!(f, "s8"),
141            Self::S9 => write!(f, "s9"),
142            Self::S10 => write!(f, "s10"),
143            Self::S11 => write!(f, "s11"),
144            Self::T3 => write!(f, "t3"),
145            Self::T4 => write!(f, "t4"),
146            Self::T5 => write!(f, "t5"),
147            Self::T6 => write!(f, "t6"),
148        }
149    }
150}
151
152impl fmt::Debug for ContractRegister {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        fmt::Display::fmt(self, f)
155    }
156}
157
158const impl Register for ContractRegister {
159    const ZERO: Self = Self::Zero;
160    const SP: Self = Self::Sp;
161    const RA: Self = Self::Ra;
162    const A0: Self = Self::A0;
163    const A1: Self = Self::A1;
164    type Type = u64;
165
166    #[inline(always)]
167    fn from_bits(bits: u8) -> Option<Self> {
168        match bits {
169            0 => Some(Self::Zero),
170            1 => Some(Self::Ra),
171            2 => Some(Self::Sp),
172            5 => Some(Self::T0),
173            6 => Some(Self::T1),
174            7 => Some(Self::T2),
175            8 => Some(Self::S0),
176            9 => Some(Self::S1),
177            10 => Some(Self::A0),
178            11 => Some(Self::A1),
179            12 => Some(Self::A2),
180            13 => Some(Self::A3),
181            14 => Some(Self::A4),
182            15 => Some(Self::A5),
183            16 => Some(Self::A6),
184            17 => Some(Self::A7),
185            18 => Some(Self::S2),
186            19 => Some(Self::S3),
187            20 => Some(Self::S4),
188            21 => Some(Self::S5),
189            22 => Some(Self::S6),
190            23 => Some(Self::S7),
191            24 => Some(Self::S8),
192            25 => Some(Self::S9),
193            26 => Some(Self::S10),
194            27 => Some(Self::S11),
195            28 => Some(Self::T3),
196            29 => Some(Self::T4),
197            30 => Some(Self::T5),
198            31 => Some(Self::T6),
199            _ => None,
200        }
201    }
202}
203
204/// SAFETY: `Self::from_bits()` returns `Some()` for `1`, `8`, `9` and `18..=27`
205const unsafe impl ZcmpRegister for ContractRegister {
206    const RVE: bool = false;
207}
208
209/// An instruction type used by contracts
210#[instruction(
211    ignore = [Ecall],
212    inherit = [
213        Rv64ZcaInstruction,
214        Rv64ZcbInstruction,
215        Rv64ZcmpInstruction,
216        Rv64Instruction,
217        Rv64MInstruction,
218        Rv64BInstruction,
219        Rv64ZbcInstruction,
220        Rv64ZknInstruction,
221        ZicondInstruction,
222    ],
223)]
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
225pub enum ContractInstruction<Reg = ContractRegister> {}
226
227#[instruction]
228const impl<Reg> Instruction for ContractInstruction<Reg> {
229    const ALIGNMENT: u8 = align_of::<u32>() as u8;
230
231    type Reg = Reg;
232
233    #[inline(always)]
234    fn try_decode(instruction: u32) -> Option<Self> {
235        None
236    }
237
238    #[inline(always)]
239    fn size(&self) -> u8 {
240        size_of::<u32>() as u8
241    }
242}
243
244#[instruction]
245impl<Reg> fmt::Display for ContractInstruction<Reg>
246where
247    Reg: Register,
248{
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        match self {}
251    }
252}
253
254#[instruction_execution]
255const impl<Reg> ExecutableInstructionOperands for ContractInstruction<Reg> {}
256
257#[instruction_execution]
258const impl<Reg, Env> ExecutableInstructionCsr<Env> for ContractInstruction<Reg> {}
259
260#[instruction_execution]
261impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
262    for ContractInstruction<Reg>
263where
264    Reg: Register,
265{
266    #[inline(always)]
267    fn execute(
268        self,
269        Rs1Rs2OperandValues {
270            rs1_value,
271            rs2_value,
272        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
273        regs: &mut Regs,
274        _env: &mut Env,
275        memory: &mut Memory,
276        program_counter: &mut PC,
277    ) -> ExecutionResult<Self::Reg> {
278        ExecutionResult::ContinueNoWrite
279    }
280}
281
282impl<Reg> ContractInstruction<Reg> {
283    /// Check if the instruction is a jump instruction of any kind (affects program counter)
284    #[inline]
285    #[expect(
286        clippy::rest_pattern_accessible_field,
287        reason = "Do not care about fields"
288    )]
289    pub fn is_jump(&self) -> bool {
290        matches!(
291            self,
292            Self::CJ { .. }
293                | Self::CBeqz { .. }
294                | Self::CBnez { .. }
295                | Self::CJr { .. }
296                | Self::CJalr { .. }
297                | Self::CmPopretz { .. }
298                | Self::CmPopret { .. }
299                | Self::Jalr { .. }
300                | Self::Beq { .. }
301                | Self::Bne { .. }
302                | Self::Blt { .. }
303                | Self::Bge { .. }
304                | Self::Bltu { .. }
305                | Self::Bgeu { .. }
306                | Self::Jal { .. }
307        )
308    }
309}