dynasmrt/
riscv.rs

1//! Runtime support for the 32-bit and 64-bit RISC-V architecture assembling targets.
2//!
3//! The riscv instruction sets feature 16-bit and 32-bit width instructions. It features relocations
4//! up to 20 bits in size in a single instruction, or 32 bits in size using sequences of two
5//! instructions.
6//!
7//! The core relocation behaviour for these architecture is provided by the [`RiscvRelocation`] type.
8//!
9//! Next to that, this module contains the following:
10//!
11//! ## Type aliases
12//!
13//! Several specialized type aliases of the generic [`Assembler`] are provided as these are by far the most common usecase.
14//!
15//! ## Enums
16//!
17//! There are enumerations of every RISC-V register family. 
18//! These enums implement the [`Register`] trait and their discriminant values match their numeric encoding in dynamic register literals.
19//!
20//! *Note: The presence of some registers listed here is purely what is encodable. Check the relevant architecture documentation to find what is architecturally valid.*
21//!
22//! ## Functions
23//!
24//! This module contains handlers for error conditions in the case where a dynamically selected register is invalid, or a dynamically encoded immediate is out of range.
25//! These panic with a friendly error message if any of these conditions happen at runtime.
26
27use crate::relocations::{ArchitectureRelocationEncoding, Relocation, RelocationType, RelocationSize, RelocationKind, RelocationEncoding, ImpossibleRelocation, fits_signed_bitfield};
28use byteorder::{ByteOrder, LittleEndian};
29use std::convert::TryFrom;
30use crate::Register;
31
32#[derive(Debug, Clone, Copy)]
33#[allow(missing_docs)]
34enum RiscvRelocationEncoding {
35    // beq, beqz, bge, bgeu, bgez, bgt, bgtu, bgtz, ble, bleu, blez, blt, bltu, bltz, bne, bnez
36    // 12 bits, 2-bit scaled
37    B,
38    // j, jal
39    // 20 bits, 2-bit scaled
40    J,
41    // c.beqz, c.bnez
42    // 9 bits, 2-bit scaled
43    BC,
44    // c.j, c.jal
45    // 12 bits, 2-bit scaled
46    JC,
47    // auipc
48    // 32 bits, 12-bit scaled
49    HI20,
50    // loads, addi.
51    // 12 bits, no scaling
52    LO12,
53    // stores
54    // 12 bits, no scaling
55    LO12S,
56    // pc-relative addrgen/load pseudo instructions
57    // 32 bits, no scaling
58    SPLIT32,
59    // pc-relative store pseudo instructions
60    // 32 bits, no scaling
61    SPLIT32S,
62}
63
64impl ArchitectureRelocationEncoding for RiscvRelocationEncoding {
65    fn decode(code: u8) -> Self {
66        match code {
67            0 => Self::B,
68            1 => Self::J,
69            2 => Self::BC,
70            3 => Self::JC,
71            4 => Self::HI20,
72            5 => Self::LO12,
73            6 => Self::LO12S,
74            7 => Self::SPLIT32,
75            8 => Self::SPLIT32S,
76            n => panic!("Invalid complex relocation code {n} given for the current architecture")
77        }
78    }
79}
80
81impl RiscvRelocationEncoding {
82    fn bitsize(&self) -> (u8, u8) {
83        match self {
84            Self::B => (12, 1),
85            Self::J => (20, 1),
86            Self::BC => (9, 1),
87            Self::JC => (12, 1),
88
89            Self::HI20 => (32, 0),
90            Self::LO12 => (32, 0),
91            Self::LO12S => (32, 0),
92
93            Self::SPLIT32 => (32, 0),
94            Self::SPLIT32S => (32, 0),
95        }
96    }
97}
98
99/// Relocation implementation for the RV32 and RV64 architectures.
100#[derive(Debug, Clone, Copy)]
101pub struct RiscvRelocation(RelocationType<RiscvRelocationEncoding>);
102
103impl Relocation for RiscvRelocation {
104    fn from_encoding(encoding: u8) -> Self {
105        RiscvRelocation(RelocationType::decode(encoding))
106    }
107    fn from_size(kind: RelocationKind, size: RelocationSize) -> Self {
108        RiscvRelocation(RelocationType::from_size(kind, size))
109    }
110    fn size(&self) -> usize {
111        match self.0.encoding {
112            RelocationEncoding::Simple(s) => s.size(),
113            RelocationEncoding::ArchSpecific(c) => match c {
114                RiscvRelocationEncoding::BC
115                | RiscvRelocationEncoding::JC => 2,
116                RiscvRelocationEncoding::B
117                | RiscvRelocationEncoding::J
118                | RiscvRelocationEncoding::HI20
119                | RiscvRelocationEncoding::LO12
120                | RiscvRelocationEncoding::LO12S => 4,
121                RiscvRelocationEncoding::SPLIT32
122                | RiscvRelocationEncoding::SPLIT32S => 8
123            }
124        }
125    }
126    fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
127        match self.0.encoding {
128            RelocationEncoding::Simple(s) => s.write_value(buf, value),
129            RelocationEncoding::ArchSpecific(c) => {
130                // determine if the value fits
131                let value = i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?;
132
133                let (bits, scaling) = c.bitsize();
134                let mask = (1i64 << scaling) - 1;
135                // special case: the 32-bit AUIPC-based offsets don't actually
136                // range from -0x8000_0000 to 0x7FFF_FFFF on RV64 due to how
137                // sign extension interacts between them, they range from
138                // -0x8000_0800 to 0x7FFF_F7FF. But on RV32 they do span
139                // from -0x8000_0000 to 0x7FFF_FFFF.
140                // neither of these limits will ever occur in practical code,
141                // so for sanity's sake we just clamp to between -0x8000_0000 and
142                // 0x7FFF_F7FF
143                match c {
144                    RiscvRelocationEncoding::HI20
145                    | RiscvRelocationEncoding::LO12
146                    | RiscvRelocationEncoding::LO12S
147                    | RiscvRelocationEncoding::SPLIT32
148                    | RiscvRelocationEncoding::SPLIT32S => {
149                        if value < -0x8000_0800 || value > 0x7FFF_F7FF {
150                            return Err(ImpossibleRelocation { } );  
151                        }
152                    },
153                    _ => {
154                        if !fits_signed_bitfield(value, bits) || (value & mask) != 0 {
155                            return Err(ImpossibleRelocation { } );
156                        }
157                    }
158                }
159
160                // we never encode any bit above the 31st so cast now
161                let val_cast = value as u32;
162
163                match c {
164                    RiscvRelocationEncoding::B => {
165                        let mut instr = LittleEndian::read_u32(buf);
166                        instr &= 0x01FF_F07F;
167
168                        instr |= ((val_cast >> 12) & 0x1) << 31;
169                        instr |= ((val_cast >> 5) & 0x3F) << 25;
170                        instr |= ((val_cast >> 1) & 0xF) << 8;
171                        instr |= ((val_cast >> 11) & 0x1) << 7;
172
173                        LittleEndian::write_u32(buf, instr);
174                    },
175                    RiscvRelocationEncoding::J => {
176                        let mut instr = LittleEndian::read_u32(buf);
177                        instr &= 0x0000_0FFF;
178
179                        instr |= ((val_cast >> 20) & 0x1) << 31;
180                        instr |= ((val_cast >> 1) & 0x3FF) << 21;
181                        instr |= ((val_cast >> 11) & 0x1) << 20;
182                        instr |= ((val_cast >> 12) & 0xFF) << 12;
183
184                        LittleEndian::write_u32(buf, instr);
185                    },
186                    RiscvRelocationEncoding::BC => {
187                        let mut instr = LittleEndian::read_u16(buf);
188                        instr &= 0xE383;
189
190                        instr |= (((val_cast >> 8) & 0x1) as u16) << 12;
191                        instr |= (((val_cast >> 3) & 0x3) as u16) << 10;
192                        instr |= (((val_cast >> 6) & 0x3) as u16) << 5;
193                        instr |= (((val_cast >> 1) & 0x3) as u16) << 3;
194                        instr |= (((val_cast >> 5) & 0x1) as u16) << 2;
195
196                        LittleEndian::write_u16(buf, instr);
197                    },
198                    RiscvRelocationEncoding::JC => {
199                        let mut instr = LittleEndian::read_u16(buf);
200                        instr &= 0xE003;
201
202                        instr |= (((val_cast >> 11) & 0x1) as u16) << 12;
203                        instr |= (((val_cast >> 4) & 0x1) as u16) << 11;
204                        instr |= (((val_cast >> 8) & 0x3) as u16) << 9;
205                        instr |= (((val_cast >> 10) & 0x1) as u16) << 8;
206                        instr |= (((val_cast >> 6) & 0x1) as u16) << 7;
207                        instr |= (((val_cast >> 7) & 0x1) as u16) << 6;
208                        instr |= (((val_cast >> 1) & 0x7) as u16) << 3;
209                        instr |= (((val_cast >> 5) & 0x1) as u16) << 2;
210
211                        LittleEndian::write_u16(buf, instr);
212                    },
213                    RiscvRelocationEncoding::HI20 => {
214                        let mut instr = LittleEndian::read_u32(buf);
215                        instr &= 0x0000_0FFF;
216
217                        let val_round: u32 = val_cast.wrapping_add(0x800);
218                        instr |= val_round & 0xFFFF_F000;
219
220                        LittleEndian::write_u32(buf, instr);
221                    },
222                    RiscvRelocationEncoding::LO12 => {
223                        let mut instr = LittleEndian::read_u32(buf);
224                        instr &= 0x000F_FFFF;
225
226                        instr |= (val_cast & 0xFFF) << 20;
227
228                        LittleEndian::write_u32(buf, instr);
229                    },
230                    RiscvRelocationEncoding::LO12S => {
231                        let mut instr = LittleEndian::read_u32(buf);
232                        instr &= 0x01FF_F07F;
233
234                        instr |= (val_cast & 0x1F) << 7;
235                        instr |= ((val_cast >> 5) & 0x7F) << 25;
236
237                        LittleEndian::write_u32(buf, instr);
238                    },
239                    RiscvRelocationEncoding::SPLIT32 => {
240                        let mut instr1 = LittleEndian::read_u32(&buf[..4]);
241                        let mut instr2 = LittleEndian::read_u32(&buf[4..]);
242                        instr1 &= 0x0000_0FFF;
243                        instr2 &= 0x000F_FFFF;
244
245                        let val_round: u32 = val_cast.wrapping_add(0x800);
246                        instr1 |= val_round & 0xFFFF_F000;
247                        instr2 |= (val_cast & 0xFFF) << 20;
248
249                        LittleEndian::write_u32(&mut buf[..4], instr1);
250                        LittleEndian::write_u32(&mut buf[4..], instr2);
251                    },
252                    RiscvRelocationEncoding::SPLIT32S => {
253                        let mut instr1 = LittleEndian::read_u32(&buf[..4]);
254                        let mut instr2 = LittleEndian::read_u32(&buf[4..]);
255                        instr1 &= 0x0000_0FFF;
256                        instr2 &= 0x01FF_F07F;
257
258                        let val_round: u32 = val_cast.wrapping_add(0x800);
259                        instr1 |= val_round & 0xFFFF_F000;
260                        instr2 |= (val_cast & 0x1F) << 7;
261                        instr2 |= ((val_cast >> 5) & 0x7F) << 25;
262
263                        LittleEndian::write_u32(&mut buf[..4], instr1);
264                        LittleEndian::write_u32(&mut buf[4..], instr2);
265                    },
266                }
267
268                Ok(())
269            }
270        }
271    }
272    fn read_value(&self, buf: &[u8]) -> isize {
273        match self.0.encoding {
274            RelocationEncoding::Simple(s) => s.read_value(buf),
275            RelocationEncoding::ArchSpecific(c) => {
276                let bits;
277                let mut unpacked;
278
279                match c {
280                    RiscvRelocationEncoding::B => {
281                        bits = 12;
282                        let instr = LittleEndian::read_u32(buf);
283
284                        unpacked = ((instr >> 31) & 0x1) << 12;
285                        unpacked |= ((instr >> 25) & 0x3F) << 5;
286                        unpacked |= ((instr >> 8) & 0xF) << 1;
287                        unpacked |= ((instr >> 7) & 0x1) << 11;
288                    },
289                    RiscvRelocationEncoding::J => {
290                        bits = 20;
291                        let instr = LittleEndian::read_u32(buf);
292
293                        unpacked = ((instr >> 31) & 0x1) << 20;
294                        unpacked |= ((instr >> 21) & 0x3FF) << 1;
295                        unpacked |= ((instr >> 20) & 0x1) << 11;
296                        unpacked |= ((instr >> 12) & 0xFF) << 12;
297                    },
298                    RiscvRelocationEncoding::BC => {
299                        bits = 9;
300                        let instr = u32::from(LittleEndian::read_u16(buf));
301
302                        unpacked = ((instr >> 12) & 0x1) << 8;
303                        unpacked |= ((instr >> 10) & 0x3) << 3;
304                        unpacked |= ((instr >> 5) & 0x3) << 6;
305                        unpacked |= ((instr >> 3) & 0x3) << 1;
306                        unpacked |= ((instr >> 2) & 0x1) << 5;
307                    },
308                    RiscvRelocationEncoding::JC => {
309                        bits = 12;
310                        let instr = u32::from(LittleEndian::read_u16(buf));
311
312                        unpacked = ((instr >> 12) & 0x1) << 11;
313                        unpacked |= ((instr >> 11) & 0x1) << 4;
314                        unpacked |= ((instr >> 9) & 0x3) << 8;
315                        unpacked |= ((instr >> 8) & 0x1) << 10;
316                        unpacked |= ((instr >> 7) & 0x1) << 6;
317                        unpacked |= ((instr >> 6) & 0x1) << 7;
318                        unpacked |= ((instr >> 3) & 0x7) << 1;
319                        unpacked |= ((instr >> 2) & 0x1) << 5;
320                    },
321                    RiscvRelocationEncoding::HI20 => {
322                        bits = 32;
323                        let instr = LittleEndian::read_u32(buf);
324
325                        unpacked = ((instr >> 12) & 0xFFFFF) << 12;
326                        // There's a problem here. We don't know the lower
327                        // bits of the value that is being read, but they do matter
328                        // if this thing would get relocated. luckily, riscv only does
329                        // relative relocations so this should never happen, and we
330                        // should be fine with just returning the value without adjustment
331                    },
332                    RiscvRelocationEncoding::LO12 => {
333                        bits = 12;
334                        let instr = LittleEndian::read_u32(buf);
335
336                        unpacked = (instr >> 20) & 0xFFF;
337                    },
338                    RiscvRelocationEncoding::LO12S => {
339                        bits = 12;
340                        let instr = LittleEndian::read_u32(buf);
341
342                        unpacked = (instr >> 7) & 0x1F;
343                        unpacked |= ((instr >> 25) & 0x7F) << 5;
344                    },
345                    RiscvRelocationEncoding::SPLIT32 => {
346                        bits = 32;
347                        let instr1 = LittleEndian::read_u32(&buf[..4]);
348                        let instr2 = LittleEndian::read_u32(&buf[4..]);
349
350                        unpacked = ((instr1 >> 12) & 0xFFFFF) << 12;
351                        let mut lower: u32 = (instr2 >> 20) & 0xFFF;
352
353                        // sign extend the lower part and then add them
354                        lower = (lower ^ 0x800).wrapping_sub(0x800);
355                        unpacked = unpacked.wrapping_add(lower)
356
357                    },
358                    RiscvRelocationEncoding::SPLIT32S => {
359                        bits = 32;
360                        let instr1 = LittleEndian::read_u32(&buf[..4]);
361                        let instr2 = LittleEndian::read_u32(&buf[4..]);
362
363                        unpacked = ((instr1 >> 12) & 0xFFFFF) << 12;
364                        let mut lower: u32 = (instr2 >> 7) & 0x1F;
365                        lower |= ((instr2 >> 25) & 0x7F) << 5;
366
367                        // sign extend the lower part and then add them
368                        lower = (lower ^ 0x800).wrapping_sub(0x800);
369                        unpacked = unpacked.wrapping_add(lower)
370                    },
371                }
372
373                // sign extension
374                let offset = 1u64 << (bits - 1);
375                let value: u64 = (unpacked as u64 ^ offset).wrapping_sub(offset);
376
377                value as i64 as isize
378            }
379        }
380    }
381    fn kind(&self) -> RelocationKind {
382        self.0.kind
383    }
384    fn page_size() -> usize {
385        4096
386    }
387}
388
389/// A RISC-V Assembler. This is aliased here for backwards compatability.
390pub type Assembler = crate::Assembler<RiscvRelocation>;
391/// A RISC-V AssemblyModifier. This is aliased here for backwards compatability.
392pub type AssemblyModifier<'a> = crate::Modifier<'a, RiscvRelocation>;
393/// A RISC-V UncommittedModifier. This is aliased here for backwards compatability.
394pub type UncommittedModifier<'a> = crate::UncommittedModifier<'a>;
395
396// these should explicitly never be inlined, as this is the slow path.
397// that's also why these aren't made generic.
398
399/// Handler for `u32` out-of-range riscv64 & riscv32 immediates.
400#[inline(never)]
401pub fn immediate_out_of_range_unsigned_32(immediate: u32) -> ! {
402    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
403}
404
405/// Handler for `i32` out-of-range riscv64 & riscv32 immediates.
406#[inline(never)]
407pub fn immediate_out_of_range_signed_32(immediate: i32) -> ! {
408    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
409}
410/// Handler for `u64` out-of-range riscv64 & riscv32 immediates.
411#[inline(never)]
412pub fn immediate_out_of_range_unsigned_64(immediate: u64) -> ! {
413    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
414}
415
416/// Handler for `i64` out-of-range riscv64 & riscv32 immediates.
417#[inline(never)]
418pub fn immediate_out_of_range_signed_64(immediate: i64) -> ! {
419    panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")
420}
421
422/// Handler for invalid riscv64 & riscv32 registers.
423#[inline(never)]
424pub fn invalid_register(register: u8) -> ! {
425    panic!("Cannot assemble this RISC-V instruction. Register x{register} cannot be encoded.")
426}
427
428
429/// 4 or 8-byte general purpopse registers, where X0 is the zero register
430/// When using the RV32/64E profile, only the first 16 registers are valid
431#[allow(missing_docs)]
432#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
433pub enum RX {
434    X0 = 0x00, X1 = 0x01, X2 = 0x02, X3 = 0x03,
435    X4 = 0x04, X5 = 0x05, X6 = 0x06, X7 = 0x07,
436    X8 = 0x08, X9 = 0x09, X10= 0x0A, X11= 0x0B,
437    X12= 0x0C, X13= 0x0D, X14= 0x0E, X15= 0x0F,
438    X16= 0x10, X17= 0x11, X18= 0x12, X19= 0x13,
439    X20= 0x14, X21= 0x15, X22= 0x16, X23= 0x17,
440    X24= 0x18, X25= 0x19, X26= 0x1A, X27= 0x1B,
441    X28= 0x1C, X29= 0x1D, X30= 0x1E, X31= 0x1F,
442}
443reg_impls!(RX);
444
445/// 4, 8 or 16-byte floating point registers
446#[allow(missing_docs)]
447#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
448pub enum RF {
449    F0 = 0x00, F1 = 0x01, F2 = 0x02, F3 = 0x03,
450    F4 = 0x04, F5 = 0x05, F6 = 0x06, F7 = 0x07,
451    F8 = 0x08, F9 = 0x09, F10= 0x0A, F11= 0x0B,
452    F12= 0x0C, F13= 0x0D, F14= 0x0E, F15= 0x0F,
453    F16= 0x10, F17= 0x11, F18= 0x12, F19= 0x13,
454    F20= 0x14, F21= 0x15, F22= 0x16, F23= 0x17,
455    F24= 0x18, F25= 0x19, F26= 0x1A, F27= 0x1B,
456    F28= 0x1C, F29= 0x1D, F30= 0x1E, F31= 0x1F,
457}
458reg_impls!(RF);
459
460
461#[cfg(test)]
462mod tests {
463    use super::RX::*;
464    use crate::Register;
465
466    #[test]
467    fn reg_code() {
468        assert_eq!(X2.code(), 2);
469    }
470
471    #[test]
472    fn reg_code_from() {
473        assert_eq!(u8::from(X24), 0x18);
474    }
475}