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