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 RVE: bool = false;
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
207/// An instruction type used by contracts
208#[instruction(
209    ignore = [Ecall],
210    inherit = [
211        Rv64ZcaInstruction,
212        Rv64ZcbInstruction,
213        Rv64ZcmpInstruction,
214        Rv64Instruction,
215        Rv64MInstruction,
216        Rv64BInstruction,
217        Rv64ZbcInstruction,
218        Rv64ZknInstruction,
219        ZicondInstruction,
220    ],
221)]
222#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub enum ContractInstruction<Hart = BasicHart<ContractRegister>>
224where
225    Hart: HartConfig, {}
226
227#[instruction]
228const impl<Reg, Hart> Instruction for ContractInstruction<Hart>
229where
230    Hart: [const] HartConfig<Reg = Reg>,
231{
232    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
233
234    const ALIGNMENT: u8 = align_of::<u32>() as u8;
235
236    type Hart = Hart;
237
238    #[inline(always)]
239    fn try_decode(instruction: u32) -> Option<Self> {
240        None
241    }
242
243    #[inline(always)]
244    fn size(&self) -> u8 {
245        size_of::<u32>() as u8
246    }
247}
248
249#[instruction]
250impl<Reg, Hart> fmt::Display for ContractInstruction<Hart>
251where
252    Reg: Register,
253    Hart: HartConfig<Reg = Reg>,
254{
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match self {}
257    }
258}
259
260#[instruction_execution]
261const impl<Reg, Hart> ExecutableInstructionOperands for ContractInstruction<Hart> where
262    Hart: HartConfig<Reg = Reg>
263{
264}
265
266#[instruction_execution]
267const impl<Reg, Hart, Env> ExecutableInstructionCsr<Env> for ContractInstruction<Hart> where
268    Hart: HartConfig<Reg = Reg>
269{
270}
271
272#[instruction_execution]
273impl<Reg, Hart, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
274    for ContractInstruction<Hart>
275where
276    Reg: Register,
277    Hart: HartConfig<Reg = Reg>,
278{
279    #[inline(always)]
280    fn execute(
281        self,
282        Rs1Rs2OperandValues {
283            rs1_value,
284            rs2_value,
285        }: Rs1Rs2OperandValues<Reg::Type>,
286        regs: &mut Regs,
287        _env: &mut Env,
288        memory: &mut Memory,
289        program_counter: &PC,
290    ) -> ExecutionResult<Reg> {
291        ExecutionResult::ContinueNoWrite
292    }
293}