Skip to main content

sp1_jit/
instructions.rs

1use crate::{RiscOperand, RiscRegister};
2
3/// An ALU instruction backend for a specific target architecture.
4///
5/// This trait is implemented for each target architecture supported by the JIT transpiler.
6pub trait ComputeInstructions: Sized {
7    /// Add the values of two registers together, using 64bit arithmetic.
8    ///
9    /// add: rd = rs1 + rs2
10    fn add(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
11
12    /// Subtract the values of two registers from each other, using 64bit arithmetic.
13    ///
14    /// sub: rd = rs1 - rs2
15    fn sub(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
16
17    /// Bitwise XOR the values of two registers together.
18    ///
19    /// xor: rd = rs1 ^ rs2
20    fn xor(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
21
22    /// Bitwise OR the values of two registers together.
23    ///
24    /// or: rd = rs1 | rs2
25    fn or(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
26
27    /// Bitwise AND the values of two registers together.
28    ///
29    /// and: rd = rs1 & rs2
30    fn and(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
31
32    /// Shift the values of two registers left by the amount specified by the second register.
33    ///
34    /// sll: rd = rs1 << rs2
35    fn sll(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
36
37    /// Shift the values of two registers right by the amount specified by the second register.
38    ///
39    /// srl: rd = rs1 >> rs2
40    fn srl(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
41
42    /// Shift the values of two registers right by the amount specified by the second register,
43    /// using arithmetic right shift.
44    ///
45    /// sra: rd = rs1 >> rs2
46    fn sra(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
47
48    /// Set if less than (signed comparison).
49    ///
50    /// slt: rd = (rs1 < rs2) ? 1 : 0
51    fn slt(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
52
53    /// Set if less than (unsigned comparison).
54    ///
55    /// sltu: rd = (rs1 < rs2) ? 1 : 0
56    fn sltu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
57
58    /// Multiply the values of two registers together, using 64bit arithmetic.
59    ///
60    /// mul: rd = rs1 * rs2
61    fn mul(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
62
63    /// Multiply the values of two registers together and return the high 64 bits (signed).
64    ///
65    /// mulh: rd = (rs1 * rs2) >> 64 (signed)
66    fn mulh(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
67
68    /// Multiply the values of two registers together and return the high 64 bits (unsigned).
69    ///
70    /// mulhu: rd = (rs1 * rs2) >> 64 (unsigned)
71    fn mulhu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
72
73    /// Multiply signed rs1 by unsigned rs2 and return the high 64 bits.
74    ///
75    /// mulhsu: rd = (rs1 * rs2) >> 64 (signed * unsigned)
76    fn mulhsu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
77
78    /// Divide the values of two registers (signed).
79    ///
80    /// div: rd = rs2 == 0 ? 0 : rs1 / rs2
81    fn div(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
82
83    /// Divide the values of two registers (unsigned).
84    ///
85    /// divu: rd = rs2 == 0 ? 0 : rs1 / rs2
86    fn divu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
87
88    /// Remainder of two registers (signed).
89    ///
90    /// rem: rd = rs2 == 0 ? 0 : rs1 % rs2
91    fn rem(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
92
93    /// Remainder of two registers (unsigned).
94    ///
95    /// remu: rd = rs2 == 0 ? 0 : rs1 % rs2
96    fn remu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
97
98    /// Add the values of two registers together, using 64bit arithmetic, but only keeping lower 32
99    /// bits.
100    ///
101    /// addw: rd = (rs1 + rs2) & 0xFFFFFFFF (sign-extended to 64-bit)
102    fn addw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
103
104    /// Subtract the values of two registers, using 64bit arithmetic, but only keeping lower 32
105    /// bits.
106    ///
107    /// subw: rd = (rs1 - rs2) & 0xFFFFFFFF (sign-extended to 64-bit)
108    fn subw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
109
110    /// Shift the values of two registers left by the amount specified by the second register
111    /// (32-bit).
112    ///
113    /// sllw: rd = (rs1 << (rs2 & 0x1F)) & 0xFFFFFFFF (sign-extended to 64-bit)
114    fn sllw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
115
116    /// Shift the values of two registers right by the amount specified by the second register
117    /// (32-bit logical).
118    ///
119    /// srlw: rd = ((rs1 & 0xFFFFFFFF) >> (rs2 & 0x1F)) (sign-extended to 64-bit)
120    fn srlw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
121
122    /// Shift the values of two registers right by the amount specified by the second register
123    /// (32-bit arithmetic).
124    ///
125    /// sraw: rd = ((rs1 as i32) >> (rs2 & 0x1F)) (sign-extended to 64-bit)
126    fn sraw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
127
128    /// Multiply the values of two registers together, using 32bit arithmetic (sign-extended to
129    /// 64-bit).
130    ///
131    /// mulw: rd = (rs1 * rs2) & 0xFFFFFFFF (sign-extended to 64-bit)
132    fn mulw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
133
134    /// Divide the values of two registers together, using 32bit arithmetic (sign-extended to
135    /// 64-bit).
136    ///
137    /// divw: rd = rs2 == 0 ? 0xFFFFFFFF : (rs1 as i32) / (rs2 as i32) (sign-extended to 64-bit)
138    fn divw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
139
140    /// Divide the values of two registers, unsigned 32bit (sign-extended to 64-bit).
141    ///
142    /// divuw: rd = rs2 == 0 ? 0xFFFFFFFF : (rs1 as u32) / (rs2 as u32) (sign-extended to 64-bit)
143    fn divuw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
144
145    /// Remainder the values of two registers together, using 32bit arithmetic (sign-extended to
146    /// 64-bit).
147    ///
148    /// remw: rd = rs2 == 0 ? rs1 : (rs1 as i32) % (rs2 as i32) (sign-extended to 64-bit)
149    fn remw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
150
151    /// Remainder the values of two registers, unsigned 32bit (sign-extended to 64-bit).
152    ///
153    /// remuw: rd = rs2 == 0 ? rs1 : (rs1 as u32) % (rs2 as u32) (sign-extended to 64-bit)
154    fn remuw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand);
155
156    /// Advance to the next pc, storing the current (pc + imm) in a register.
157    ///
158    /// auipc: rd = pc + imm, pc = pc + 4
159    fn auipc(&mut self, rd: RiscRegister, imm: u64);
160
161    /// Load upper immediate into a register.
162    ///
163    /// lui: rd = imm << 12
164    fn lui(&mut self, rd: RiscRegister, imm: u64);
165}
166
167pub trait ControlFlowInstructions: Sized {
168    /// Compare the values of two registers, and jump to an address if they are equal.
169    ///
170    /// beq: pc = pc + ((rs1 == rs2) ? imm : 4)
171    ///
172    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
173    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
174    /// of each instruction.
175    fn beq(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
176
177    /// Compare the values of two registers, and jump to an address if they are not equal.
178    ///
179    /// bne: pc = pc + ((rs1 != rs2) ? imm : 4)
180    ///
181    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
182    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
183    /// of each instruction.
184    fn bne(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
185
186    /// Compare the values of two registers, and jump to an address if the first is less than the
187    /// second.
188    ///
189    /// blt: pc = pc + ((rs1 < rs2) ? imm : 4)
190    ///
191    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
192    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
193    /// of each instruction.
194    fn blt(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
195
196    /// Compare the values of two registers, and jump to an address if the first is greater than or
197    /// equal to the second.
198    ///
199    /// bge: pc = pc + ((rs1 >= rs2) ? imm : 4)
200    ///
201    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
202    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
203    /// of each instruction.
204    fn bge(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
205
206    /// Compare the values of two registers, and jump to an address if the first is less than the
207    /// second, unsigned.
208    ///
209    /// bltu: pc = pc + ((rs1 < rs2) ? imm : 4)
210    ///
211    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
212    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
213    /// of each instruction.
214    fn bltu(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
215
216    /// Compare the values of two registers, and jump to an address if the first is greater than or
217    /// equal to the second, unsigned.
218    ///
219    /// bgeu: pc = pc + ((rs1 >= rs2) ? imm : 4)
220    ///
221    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
222    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
223    /// of each instruction.
224    fn bgeu(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
225
226    /// Jump to an address.
227    ///
228    /// jal: rd = pc + 4, pc = pc + imm
229    ///
230    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
231    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
232    /// of each instruction.
233    fn jal(&mut self, rd: RiscRegister, imm: u64);
234
235    /// Jump to an address, and return to the previous address.
236    ///
237    /// jalr: rd = pc + 4, pc = rs1 + imm
238    ///
239    /// NOTE: During transpilatiom, this method will emit the PC bumps for you,
240    /// typically however, you will want to explicty call [`SP1RiscvTranspiler::set_pc`] at the end
241    /// of each instruction.
242    fn jalr(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
243}
244
245pub trait MemoryInstructions: Sized {
246    /// Load a byte from memory into a register.
247    ///
248    /// lb: rd = m8(rs1 + imm)
249    fn lb(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
250
251    /// Load a half word from memory into a register.
252    ///
253    /// lh: rd = m16(rs1 + imm)
254    fn lh(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
255
256    /// Load a word from memory into a register.
257    ///
258    /// lw: rd = m32(rs1 + imm)
259    fn lw(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
260
261    /// Load a byte from memory into a register, zero extended.
262    ///
263    /// lbu: rd = zx(m8(rs1 + imm))
264    fn lbu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
265
266    /// Load a half word from memory into a register, zero extended.    
267    ///
268    /// lhu: rd = zx(m16(rs1 + imm))
269    fn lhu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
270
271    /// Load a double word from memory into a register.
272    ///
273    /// ldu: rd = m64(rs1 + imm)
274    fn ld(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
275
276    /// Load a word from memory into a register, zero extended.
277    ///
278    /// lwu: rd = zx(m32(rs1 + imm))
279    fn lwu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64);
280
281    /// Store a byte into memory.
282    ///
283    /// sb: m8(rs1 + imm) = rs2[7:0]
284    fn sb(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
285
286    /// Store a half word into memory.
287    ///
288    /// sh: m16(rs1 + imm) = rs2[15:0]
289    fn sh(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
290
291    /// Store a word into memory.
292    ///
293    /// sw: m32(rs1 + imm) = rs2[31:0]
294    fn sw(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
295
296    /// Store a double word into memory.
297    ///
298    /// sd: m64(rs1 + imm) = rs2[63:0]
299    fn sd(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64);
300}
301
302pub trait SystemInstructions: Sized {
303    /// Transfer control to the operating system.
304    fn ecall(&mut self);
305
306    fn unimp(&mut self);
307}