Skip to main content

ab_riscv_primitives/instructions/
rv64.rs

1//! Base RISC-V RV64 instruction set
2
3pub mod b;
4pub mod c;
5pub mod m;
6#[cfg(test)]
7mod tests;
8pub mod zce;
9pub mod zk;
10
11use crate::instructions::Instruction;
12use crate::instructions::utils::{I24, I24WithZeroedBits};
13use crate::registers::general_purpose::Register;
14use ab_riscv_macros::instruction;
15use core::fmt;
16
17/// RISC-V RV64 instruction
18#[instruction]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Rv64Instruction<Reg> {
21    // R-type
22    Add { rd: Reg, rs1: Reg, rs2: Reg },
23    Sub { rd: Reg, rs1: Reg, rs2: Reg },
24    Sll { rd: Reg, rs1: Reg, rs2: Reg },
25    Slt { rd: Reg, rs1: Reg, rs2: Reg },
26    Sltu { rd: Reg, rs1: Reg, rs2: Reg },
27    Xor { rd: Reg, rs1: Reg, rs2: Reg },
28    Srl { rd: Reg, rs1: Reg, rs2: Reg },
29    Sra { rd: Reg, rs1: Reg, rs2: Reg },
30    Or { rd: Reg, rs1: Reg, rs2: Reg },
31    And { rd: Reg, rs1: Reg, rs2: Reg },
32
33    // RV64 R-type W
34    Addw { rd: Reg, rs1: Reg, rs2: Reg },
35    Subw { rd: Reg, rs1: Reg, rs2: Reg },
36    Sllw { rd: Reg, rs1: Reg, rs2: Reg },
37    Srlw { rd: Reg, rs1: Reg, rs2: Reg },
38    Sraw { rd: Reg, rs1: Reg, rs2: Reg },
39
40    // I-type
41    Addi { rd: Reg, rs1: Reg, imm: i16 },
42    Slti { rd: Reg, rs1: Reg, imm: i16 },
43    Sltiu { rd: Reg, rs1: Reg, imm: i16 },
44    Xori { rd: Reg, rs1: Reg, imm: i16 },
45    Ori { rd: Reg, rs1: Reg, imm: i16 },
46    Andi { rd: Reg, rs1: Reg, imm: i16 },
47    Slli { rd: Reg, rs1: Reg, shamt: u8 },
48    Srli { rd: Reg, rs1: Reg, shamt: u8 },
49    Srai { rd: Reg, rs1: Reg, shamt: u8 },
50
51    // RV64 I-type W
52    Addiw { rd: Reg, rs1: Reg, imm: i16 },
53    Slliw { rd: Reg, rs1: Reg, shamt: u8 },
54    Srliw { rd: Reg, rs1: Reg, shamt: u8 },
55    Sraiw { rd: Reg, rs1: Reg, shamt: u8 },
56
57    // Loads (I-type)
58    Lb { rd: Reg, rs1: Reg, imm: i16 },
59    Lh { rd: Reg, rs1: Reg, imm: i16 },
60    Lw { rd: Reg, rs1: Reg, imm: i16 },
61    Ld { rd: Reg, rs1: Reg, imm: i16 },
62    Lbu { rd: Reg, rs1: Reg, imm: i16 },
63    Lhu { rd: Reg, rs1: Reg, imm: i16 },
64    Lwu { rd: Reg, rs1: Reg, imm: i16 },
65
66    // Jalr (I-type)
67    Jalr { rd: Reg, rs1: Reg, imm: i16 },
68
69    // S-type
70    Sb { rs2: Reg, rs1: Reg, imm: i16 },
71    Sh { rs2: Reg, rs1: Reg, imm: i16 },
72    Sw { rs2: Reg, rs1: Reg, imm: i16 },
73    Sd { rs2: Reg, rs1: Reg, imm: i16 },
74
75    // B-type
76    Beq { rs1: Reg, rs2: Reg, imm: I24 },
77    Bne { rs1: Reg, rs2: Reg, imm: I24 },
78    Blt { rs1: Reg, rs2: Reg, imm: I24 },
79    Bge { rs1: Reg, rs2: Reg, imm: I24 },
80    Bltu { rs1: Reg, rs2: Reg, imm: I24 },
81    Bgeu { rs1: Reg, rs2: Reg, imm: I24 },
82
83    // Lui (U-type)
84    Lui { rd: Reg, imm: I24WithZeroedBits<12> },
85
86    // Auipc (U-type)
87    Auipc { rd: Reg, imm: I24WithZeroedBits<12> },
88
89    // Jal (J-type)
90    Jal { rd: Reg, imm: I24 },
91
92    // Fence
93    Fence { pred: u8, succ: u8 },
94    FenceTso,
95
96    // System instructions
97    Ecall,
98    Ebreak,
99
100    // Unimplemented/illegal
101    Unimp,
102}
103
104#[instruction]
105impl<Reg> const Instruction for Rv64Instruction<Reg>
106where
107    Reg: [const] Register<Type = u64>,
108{
109    type Reg = Reg;
110
111    #[inline(always)]
112    fn try_decode(instruction: u32) -> Option<Self> {
113        let opcode = (instruction & 0b111_1111) as u8;
114        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
115        let funct3 = ((instruction >> 12) & 0b111) as u8;
116        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
117        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
118        let funct7 = ((instruction >> 25) & 0b111_1111) as u8;
119
120        match opcode {
121            // R-type
122            0b0110011 => {
123                let rd = Reg::from_bits(rd_bits)?;
124                let rs1 = Reg::from_bits(rs1_bits)?;
125                let rs2 = Reg::from_bits(rs2_bits)?;
126                match (funct3, funct7) {
127                    (0b000, 0b0000000) => Some(Self::Add { rd, rs1, rs2 }),
128                    (0b000, 0b0100000) => Some(Self::Sub { rd, rs1, rs2 }),
129                    (0b001, 0b0000000) => Some(Self::Sll { rd, rs1, rs2 }),
130                    (0b010, 0b0000000) => Some(Self::Slt { rd, rs1, rs2 }),
131                    (0b011, 0b0000000) => Some(Self::Sltu { rd, rs1, rs2 }),
132                    (0b100, 0b0000000) => Some(Self::Xor { rd, rs1, rs2 }),
133                    (0b101, 0b0000000) => Some(Self::Srl { rd, rs1, rs2 }),
134                    (0b101, 0b0100000) => Some(Self::Sra { rd, rs1, rs2 }),
135                    (0b110, 0b0000000) => Some(Self::Or { rd, rs1, rs2 }),
136                    (0b111, 0b0000000) => Some(Self::And { rd, rs1, rs2 }),
137                    _ => None,
138                }
139            }
140            // RV64 R-type W
141            0b0111011 => {
142                let rd = Reg::from_bits(rd_bits)?;
143                let rs1 = Reg::from_bits(rs1_bits)?;
144                let rs2 = Reg::from_bits(rs2_bits)?;
145                match (funct3, funct7) {
146                    (0b000, 0b0000000) => Some(Self::Addw { rd, rs1, rs2 }),
147                    (0b000, 0b0100000) => Some(Self::Subw { rd, rs1, rs2 }),
148                    (0b001, 0b0000000) => Some(Self::Sllw { rd, rs1, rs2 }),
149                    (0b101, 0b0000000) => Some(Self::Srlw { rd, rs1, rs2 }),
150                    (0b101, 0b0100000) => Some(Self::Sraw { rd, rs1, rs2 }),
151                    _ => None,
152                }
153            }
154            // I-type
155            0b0010011 => {
156                let rd = Reg::from_bits(rd_bits)?;
157                let rs1 = Reg::from_bits(rs1_bits)?;
158                let imm = (instruction.cast_signed() >> 20) as i16;
159                match funct3 {
160                    0b000 => Some(Self::Addi { rd, rs1, imm }),
161                    0b010 => Some(Self::Slti { rd, rs1, imm }),
162                    0b011 => Some(Self::Sltiu { rd, rs1, imm }),
163                    0b100 => Some(Self::Xori { rd, rs1, imm }),
164                    0b110 => Some(Self::Ori { rd, rs1, imm }),
165                    0b111 => Some(Self::Andi { rd, rs1, imm }),
166                    0b001 => {
167                        let shamt = (instruction >> 20) as u8 & 0b11_1111;
168                        let funct6 = (instruction >> 26) & 0b11_1111;
169                        if funct6 == 0b000000 {
170                            Some(Self::Slli { rd, rs1, shamt })
171                        } else {
172                            None
173                        }
174                    }
175                    0b101 => {
176                        let shamt = (instruction >> 20) as u8 & 0b11_1111;
177                        let funct6 = (instruction >> 26) & 0b11_1111;
178                        match funct6 {
179                            0b000000 => Some(Self::Srli { rd, rs1, shamt }),
180                            0b010000 => Some(Self::Srai { rd, rs1, shamt }),
181                            _ => None,
182                        }
183                    }
184                    _ => None,
185                }
186            }
187            // RV64 I-type W
188            0b0011011 => {
189                let rd = Reg::from_bits(rd_bits)?;
190                let rs1 = Reg::from_bits(rs1_bits)?;
191                let imm = (instruction.cast_signed() >> 20) as i16;
192                // 5-bit for W shifts
193                let shamt = (instruction >> 20) as u8 & 0b1_1111;
194                match funct3 {
195                    0b000 => Some(Self::Addiw { rd, rs1, imm }),
196                    0b001 => {
197                        if funct7 == 0b0000000 {
198                            Some(Self::Slliw { rd, rs1, shamt })
199                        } else {
200                            None
201                        }
202                    }
203                    0b101 => match funct7 {
204                        0b0000000 => Some(Self::Srliw { rd, rs1, shamt }),
205                        0b0100000 => Some(Self::Sraiw { rd, rs1, shamt }),
206                        _ => None,
207                    },
208                    _ => None,
209                }
210            }
211            // Loads (I-type)
212            0b0000011 => {
213                let rd = Reg::from_bits(rd_bits)?;
214                let rs1 = Reg::from_bits(rs1_bits)?;
215                let imm = (instruction.cast_signed() >> 20) as i16;
216                match funct3 {
217                    0b000 => Some(Self::Lb { rd, rs1, imm }),
218                    0b001 => Some(Self::Lh { rd, rs1, imm }),
219                    0b010 => Some(Self::Lw { rd, rs1, imm }),
220                    0b011 => Some(Self::Ld { rd, rs1, imm }),
221                    0b100 => Some(Self::Lbu { rd, rs1, imm }),
222                    0b101 => Some(Self::Lhu { rd, rs1, imm }),
223                    0b110 => Some(Self::Lwu { rd, rs1, imm }),
224                    _ => None,
225                }
226            }
227            // Jalr (I-type)
228            0b1100111 => {
229                let rd = Reg::from_bits(rd_bits)?;
230                let rs1 = Reg::from_bits(rs1_bits)?;
231                if funct3 == 0b000 {
232                    let imm = (instruction.cast_signed() >> 20) as i16;
233                    Some(Self::Jalr { rd, rs1, imm })
234                } else {
235                    None
236                }
237            }
238            // S-type
239            0b0100011 => {
240                let rs1 = Reg::from_bits(rs1_bits)?;
241                let rs2 = Reg::from_bits(rs2_bits)?;
242                let imm11_5 = ((instruction >> 25) & 0b111_1111).cast_signed();
243                let imm4_0 = ((instruction >> 7) & 0b1_1111).cast_signed();
244                let imm = (imm11_5 << 5) | imm4_0;
245                // Sign extend
246                let imm = ((imm << 20) >> 20) as i16;
247                match funct3 {
248                    0b000 => Some(Self::Sb { rs2, rs1, imm }),
249                    0b001 => Some(Self::Sh { rs2, rs1, imm }),
250                    0b010 => Some(Self::Sw { rs2, rs1, imm }),
251                    0b011 => Some(Self::Sd { rs2, rs1, imm }),
252                    _ => None,
253                }
254            }
255            // B-type
256            0b1100011 => {
257                let rs1 = Reg::from_bits(rs1_bits)?;
258                let rs2 = Reg::from_bits(rs2_bits)?;
259                let imm12 = ((instruction >> 31) & 1).cast_signed();
260                let imm10_5 = ((instruction >> 25) & 0b11_1111).cast_signed();
261                let imm4_1 = ((instruction >> 8) & 0b1111).cast_signed();
262                let imm11 = ((instruction >> 7) & 1).cast_signed();
263                let imm = (imm12 << 12) | (imm11 << 11) | (imm10_5 << 5) | (imm4_1 << 1);
264                // Sign extend
265                let imm = I24::from_i32((imm << 19) >> 19);
266                match funct3 {
267                    0b000 => Some(Self::Beq { rs1, rs2, imm }),
268                    0b001 => Some(Self::Bne { rs1, rs2, imm }),
269                    0b100 => Some(Self::Blt { rs1, rs2, imm }),
270                    0b101 => Some(Self::Bge { rs1, rs2, imm }),
271                    0b110 => Some(Self::Bltu { rs1, rs2, imm }),
272                    0b111 => Some(Self::Bgeu { rs1, rs2, imm }),
273                    _ => None,
274                }
275            }
276            // Lui (U-type)
277            0b0110111 => {
278                let rd = Reg::from_bits(rd_bits)?;
279                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
280                Some(Self::Lui { rd, imm })
281            }
282            // Auipc (U-type)
283            0b0010111 => {
284                let rd = Reg::from_bits(rd_bits)?;
285                let imm = I24WithZeroedBits::from_i32((instruction & 0xffff_f000).cast_signed());
286                Some(Self::Auipc { rd, imm })
287            }
288            // Jal (J-type)
289            0b1101111 => {
290                let rd = Reg::from_bits(rd_bits)?;
291                let imm20 = ((instruction >> 31) & 1).cast_signed();
292                let imm10_1 = ((instruction >> 21) & 0b11_1111_1111).cast_signed();
293                let imm11 = ((instruction >> 20) & 1).cast_signed();
294                let imm19_12 = ((instruction >> 12) & 0b1111_1111).cast_signed();
295                let imm = (imm20 << 20) | (imm19_12 << 12) | (imm11 << 11) | (imm10_1 << 1);
296                // Sign extend
297                let imm = I24::from_i32((imm << 11) >> 11);
298                Some(Self::Jal { rd, imm })
299            }
300            // Fence (I-type like, simplified for EM)
301            0b0001111 => {
302                if funct3 == 0b000 && rd_bits == 0 && rs1_bits == 0 {
303                    let fm = (instruction >> 28) & 0b1111;
304                    let pred = ((instruction >> 24) & 0xf) as u8;
305                    let succ = ((instruction >> 20) & 0xf) as u8;
306                    match fm {
307                        0b0000 => Some(Self::Fence { pred, succ }),
308                        0b1000 => {
309                            // FENCE.TSO: fm=8, pred=RW(0b0011), succ=RW(0b0011) - fixed by spec
310                            if pred == 0b0011 && succ == 0b0011 {
311                                Some(Self::FenceTso)
312                            } else {
313                                None
314                            }
315                        }
316                        _ => None,
317                    }
318                } else {
319                    None
320                }
321            }
322            // System instructions
323            0b1110011 => {
324                let imm = (instruction >> 20) & 0xfff;
325                if funct3 == 0 && rd_bits == 0 && rs1_bits == 0 {
326                    match imm {
327                        0 => Some(Self::Ecall),
328                        1 => Some(Self::Ebreak),
329                        _ => None,
330                    }
331                } else if funct3 == 0b001 && rd_bits == 0 && rs1_bits == 0 && imm == 0xc00 {
332                    // `0xc0001073` is emitted as `unimp`/illegal instruction by various compilers,
333                    // including Rust when it hits a panic
334                    Some(Self::Unimp)
335                } else {
336                    None
337                }
338            }
339            _ => None,
340        }
341    }
342
343    #[inline(always)]
344    fn alignment() -> u8 {
345        align_of::<u32>() as u8
346    }
347
348    #[inline(always)]
349    fn size(&self) -> u8 {
350        size_of::<u32>() as u8
351    }
352}
353
354#[instruction]
355impl<Reg> fmt::Display for Rv64Instruction<Reg>
356where
357    Reg: fmt::Display,
358{
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        match self {
361            Self::Add { rd, rs1, rs2 } => write!(f, "add {}, {}, {}", rd, rs1, rs2),
362            Self::Sub { rd, rs1, rs2 } => write!(f, "sub {}, {}, {}", rd, rs1, rs2),
363            Self::Sll { rd, rs1, rs2 } => write!(f, "sll {}, {}, {}", rd, rs1, rs2),
364            Self::Slt { rd, rs1, rs2 } => write!(f, "slt {}, {}, {}", rd, rs1, rs2),
365            Self::Sltu { rd, rs1, rs2 } => write!(f, "sltu {}, {}, {}", rd, rs1, rs2),
366            Self::Xor { rd, rs1, rs2 } => write!(f, "xor {}, {}, {}", rd, rs1, rs2),
367            Self::Srl { rd, rs1, rs2 } => write!(f, "srl {}, {}, {}", rd, rs1, rs2),
368            Self::Sra { rd, rs1, rs2 } => write!(f, "sra {}, {}, {}", rd, rs1, rs2),
369            Self::Or { rd, rs1, rs2 } => write!(f, "or {}, {}, {}", rd, rs1, rs2),
370            Self::And { rd, rs1, rs2 } => write!(f, "and {}, {}, {}", rd, rs1, rs2),
371
372            Self::Addw { rd, rs1, rs2 } => write!(f, "addw {}, {}, {}", rd, rs1, rs2),
373            Self::Subw { rd, rs1, rs2 } => write!(f, "subw {}, {}, {}", rd, rs1, rs2),
374            Self::Sllw { rd, rs1, rs2 } => write!(f, "sllw {}, {}, {}", rd, rs1, rs2),
375            Self::Srlw { rd, rs1, rs2 } => write!(f, "srlw {}, {}, {}", rd, rs1, rs2),
376            Self::Sraw { rd, rs1, rs2 } => write!(f, "sraw {}, {}, {}", rd, rs1, rs2),
377
378            Self::Addi { rd, rs1, imm } => write!(f, "addi {}, {}, {}", rd, rs1, imm),
379            Self::Slti { rd, rs1, imm } => write!(f, "slti {}, {}, {}", rd, rs1, imm),
380            Self::Sltiu { rd, rs1, imm } => write!(f, "sltiu {}, {}, {}", rd, rs1, imm),
381            Self::Xori { rd, rs1, imm } => write!(f, "xori {}, {}, {}", rd, rs1, imm),
382            Self::Ori { rd, rs1, imm } => write!(f, "ori {}, {}, {}", rd, rs1, imm),
383            Self::Andi { rd, rs1, imm } => write!(f, "andi {}, {}, {}", rd, rs1, imm),
384            Self::Slli { rd, rs1, shamt } => write!(f, "slli {}, {}, {}", rd, rs1, shamt),
385            Self::Srli { rd, rs1, shamt } => write!(f, "srli {}, {}, {}", rd, rs1, shamt),
386            Self::Srai { rd, rs1, shamt } => write!(f, "srai {}, {}, {}", rd, rs1, shamt),
387
388            Self::Addiw { rd, rs1, imm } => write!(f, "addiw {}, {}, {}", rd, rs1, imm),
389            Self::Slliw { rd, rs1, shamt } => write!(f, "slliw {}, {}, {}", rd, rs1, shamt),
390            Self::Srliw { rd, rs1, shamt } => write!(f, "srliw {}, {}, {}", rd, rs1, shamt),
391            Self::Sraiw { rd, rs1, shamt } => write!(f, "sraiw {}, {}, {}", rd, rs1, shamt),
392
393            Self::Lb { rd, rs1, imm } => write!(f, "lb {}, {}({})", rd, imm, rs1),
394            Self::Lh { rd, rs1, imm } => write!(f, "lh {}, {}({})", rd, imm, rs1),
395            Self::Lw { rd, rs1, imm } => write!(f, "lw {}, {}({})", rd, imm, rs1),
396            Self::Ld { rd, rs1, imm } => write!(f, "ld {}, {}({})", rd, imm, rs1),
397            Self::Lbu { rd, rs1, imm } => write!(f, "lbu {}, {}({})", rd, imm, rs1),
398            Self::Lhu { rd, rs1, imm } => write!(f, "lhu {}, {}({})", rd, imm, rs1),
399            Self::Lwu { rd, rs1, imm } => write!(f, "lwu {}, {}({})", rd, imm, rs1),
400
401            Self::Jalr { rd, rs1, imm } => write!(f, "jalr {}, {}({})", rd, imm, rs1),
402
403            Self::Sb { rs2, rs1, imm } => write!(f, "sb {}, {}({})", rs2, imm, rs1),
404            Self::Sh { rs2, rs1, imm } => write!(f, "sh {}, {}({})", rs2, imm, rs1),
405            Self::Sw { rs2, rs1, imm } => write!(f, "sw {}, {}({})", rs2, imm, rs1),
406            Self::Sd { rs2, rs1, imm } => write!(f, "sd {}, {}({})", rs2, imm, rs1),
407
408            Self::Beq { rs1, rs2, imm } => write!(f, "beq {}, {}, {}", rs1, rs2, imm),
409            Self::Bne { rs1, rs2, imm } => write!(f, "bne {}, {}, {}", rs1, rs2, imm),
410            Self::Blt { rs1, rs2, imm } => write!(f, "blt {}, {}, {}", rs1, rs2, imm),
411            Self::Bge { rs1, rs2, imm } => write!(f, "bge {}, {}, {}", rs1, rs2, imm),
412            Self::Bltu { rs1, rs2, imm } => write!(f, "bltu {}, {}, {}", rs1, rs2, imm),
413            Self::Bgeu { rs1, rs2, imm } => write!(f, "bgeu {}, {}, {}", rs1, rs2, imm),
414
415            Self::Lui { rd, imm } => write!(f, "lui {}, 0x{:x}", rd, imm.to_i32() >> 12),
416
417            Self::Auipc { rd, imm } => write!(f, "auipc {}, 0x{:x}", rd, imm.to_i32() >> 12),
418
419            Self::Jal { rd, imm } => write!(f, "jal {}, {}", rd, imm),
420
421            Self::Fence { pred, succ } => write!(f, "fence {}, {}", pred, succ),
422            Self::FenceTso => write!(f, "fence.tso"),
423
424            Self::Ecall => write!(f, "ecall"),
425            Self::Ebreak => write!(f, "ebreak"),
426
427            Self::Unimp => write!(f, "unimp"),
428        }
429    }
430}