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