/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dynasmrt-5.0.0/src/riscv.rs
Line | Count | Source |
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 | | |
27 | | use crate::relocations::{ArchitectureRelocationEncoding, Relocation, RelocationType, RelocationSize, RelocationKind, RelocationEncoding, ImpossibleRelocation, fits_signed_bitfield}; |
28 | | use byteorder::{ByteOrder, LittleEndian}; |
29 | | use std::convert::TryFrom; |
30 | | use crate::Register; |
31 | | |
32 | | #[derive(Debug, Clone, Copy)] |
33 | | #[allow(missing_docs)] |
34 | | enum 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 | | |
64 | | impl ArchitectureRelocationEncoding for RiscvRelocationEncoding { |
65 | 0 | fn decode(code: u8) -> Self { |
66 | 0 | match code { |
67 | 0 | 0 => Self::B, |
68 | 0 | 1 => Self::J, |
69 | 0 | 2 => Self::BC, |
70 | 0 | 3 => Self::JC, |
71 | 0 | 4 => Self::HI20, |
72 | 0 | 5 => Self::LO12, |
73 | 0 | 6 => Self::LO12S, |
74 | 0 | 7 => Self::SPLIT32, |
75 | 0 | 8 => Self::SPLIT32S, |
76 | 0 | n => panic!("Invalid complex relocation code {n} given for the current architecture") |
77 | | } |
78 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding as dynasmrt::relocations::ArchitectureRelocationEncoding>::decode Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding as dynasmrt::relocations::ArchitectureRelocationEncoding>::decode |
79 | | } |
80 | | |
81 | | impl RiscvRelocationEncoding { |
82 | 0 | fn bitsize(&self) -> (u8, u8) { |
83 | 0 | match self { |
84 | 0 | Self::B => (12, 1), |
85 | 0 | Self::J => (20, 1), |
86 | 0 | Self::BC => (9, 1), |
87 | 0 | Self::JC => (12, 1), |
88 | | |
89 | 0 | Self::HI20 => (32, 0), |
90 | 0 | Self::LO12 => (32, 0), |
91 | 0 | Self::LO12S => (32, 0), |
92 | | |
93 | 0 | Self::SPLIT32 => (32, 0), |
94 | 0 | Self::SPLIT32S => (32, 0), |
95 | | } |
96 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding>::bitsize Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocationEncoding>::bitsize |
97 | | } |
98 | | |
99 | | /// Relocation implementation for the RV32 and RV64 architectures. |
100 | | #[derive(Debug, Clone, Copy)] |
101 | | pub struct RiscvRelocation(RelocationType<RiscvRelocationEncoding>); |
102 | | |
103 | | impl Relocation for RiscvRelocation { |
104 | 0 | fn from_encoding(encoding: u8) -> Self { |
105 | 0 | RiscvRelocation(RelocationType::decode(encoding)) |
106 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_encoding Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_encoding |
107 | 0 | fn from_size(kind: RelocationKind, size: RelocationSize) -> Self { |
108 | 0 | RiscvRelocation(RelocationType::from_size(kind, size)) |
109 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_size Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::from_size |
110 | 0 | fn size(&self) -> usize { |
111 | 0 | match self.0.encoding { |
112 | 0 | RelocationEncoding::Simple(s) => s.size(), |
113 | 0 | RelocationEncoding::ArchSpecific(c) => match c { |
114 | | RiscvRelocationEncoding::BC |
115 | 0 | | RiscvRelocationEncoding::JC => 2, |
116 | | RiscvRelocationEncoding::B |
117 | | | RiscvRelocationEncoding::J |
118 | | | RiscvRelocationEncoding::HI20 |
119 | | | RiscvRelocationEncoding::LO12 |
120 | 0 | | RiscvRelocationEncoding::LO12S => 4, |
121 | | RiscvRelocationEncoding::SPLIT32 |
122 | 0 | | RiscvRelocationEncoding::SPLIT32S => 8 |
123 | | } |
124 | | } |
125 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::size Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::size |
126 | 0 | fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> { |
127 | 0 | match self.0.encoding { |
128 | 0 | RelocationEncoding::Simple(s) => s.write_value(buf, value), |
129 | 0 | RelocationEncoding::ArchSpecific(c) => { |
130 | | // determine if the value fits |
131 | 0 | let value = i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?;Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value::{closure#0}Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value::{closure#0} |
132 | | |
133 | 0 | let (bits, scaling) = c.bitsize(); |
134 | 0 | 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 | 0 | match c { |
144 | | RiscvRelocationEncoding::HI20 |
145 | | | RiscvRelocationEncoding::LO12 |
146 | | | RiscvRelocationEncoding::LO12S |
147 | | | RiscvRelocationEncoding::SPLIT32 |
148 | | | RiscvRelocationEncoding::SPLIT32S => { |
149 | 0 | if value < -0x8000_0800 || value > 0x7FFF_F7FF { |
150 | 0 | return Err(ImpossibleRelocation { } ); |
151 | 0 | } |
152 | | }, |
153 | | _ => { |
154 | 0 | if !fits_signed_bitfield(value, bits) || (value & mask) != 0 { |
155 | 0 | return Err(ImpossibleRelocation { } ); |
156 | 0 | } |
157 | | } |
158 | | } |
159 | | |
160 | | // we never encode any bit above the 31st so cast now |
161 | 0 | let val_cast = value as u32; |
162 | | |
163 | 0 | match c { |
164 | 0 | RiscvRelocationEncoding::B => { |
165 | 0 | let mut instr = LittleEndian::read_u32(buf); |
166 | 0 | instr &= 0x01FF_F07F; |
167 | 0 |
|
168 | 0 | instr |= ((val_cast >> 12) & 0x1) << 31; |
169 | 0 | instr |= ((val_cast >> 5) & 0x3F) << 25; |
170 | 0 | instr |= ((val_cast >> 1) & 0xF) << 8; |
171 | 0 | instr |= ((val_cast >> 11) & 0x1) << 7; |
172 | 0 |
|
173 | 0 | LittleEndian::write_u32(buf, instr); |
174 | 0 | }, |
175 | 0 | RiscvRelocationEncoding::J => { |
176 | 0 | let mut instr = LittleEndian::read_u32(buf); |
177 | 0 | instr &= 0x0000_0FFF; |
178 | 0 |
|
179 | 0 | instr |= ((val_cast >> 20) & 0x1) << 31; |
180 | 0 | instr |= ((val_cast >> 1) & 0x3FF) << 21; |
181 | 0 | instr |= ((val_cast >> 11) & 0x1) << 20; |
182 | 0 | instr |= ((val_cast >> 12) & 0xFF) << 12; |
183 | 0 |
|
184 | 0 | LittleEndian::write_u32(buf, instr); |
185 | 0 | }, |
186 | 0 | RiscvRelocationEncoding::BC => { |
187 | 0 | let mut instr = LittleEndian::read_u16(buf); |
188 | 0 | instr &= 0xE383; |
189 | 0 |
|
190 | 0 | instr |= (((val_cast >> 8) & 0x1) as u16) << 12; |
191 | 0 | instr |= (((val_cast >> 3) & 0x3) as u16) << 10; |
192 | 0 | instr |= (((val_cast >> 6) & 0x3) as u16) << 5; |
193 | 0 | instr |= (((val_cast >> 1) & 0x3) as u16) << 3; |
194 | 0 | instr |= (((val_cast >> 5) & 0x1) as u16) << 2; |
195 | 0 |
|
196 | 0 | LittleEndian::write_u16(buf, instr); |
197 | 0 | }, |
198 | 0 | RiscvRelocationEncoding::JC => { |
199 | 0 | let mut instr = LittleEndian::read_u16(buf); |
200 | 0 | instr &= 0xE003; |
201 | 0 |
|
202 | 0 | instr |= (((val_cast >> 11) & 0x1) as u16) << 12; |
203 | 0 | instr |= (((val_cast >> 4) & 0x1) as u16) << 11; |
204 | 0 | instr |= (((val_cast >> 8) & 0x3) as u16) << 9; |
205 | 0 | instr |= (((val_cast >> 10) & 0x1) as u16) << 8; |
206 | 0 | instr |= (((val_cast >> 6) & 0x1) as u16) << 7; |
207 | 0 | instr |= (((val_cast >> 7) & 0x1) as u16) << 6; |
208 | 0 | instr |= (((val_cast >> 1) & 0x7) as u16) << 3; |
209 | 0 | instr |= (((val_cast >> 5) & 0x1) as u16) << 2; |
210 | 0 |
|
211 | 0 | LittleEndian::write_u16(buf, instr); |
212 | 0 | }, |
213 | 0 | RiscvRelocationEncoding::HI20 => { |
214 | 0 | let mut instr = LittleEndian::read_u32(buf); |
215 | 0 | instr &= 0x0000_0FFF; |
216 | 0 |
|
217 | 0 | let val_round: u32 = val_cast.wrapping_add(0x800); |
218 | 0 | instr |= val_round & 0xFFFF_F000; |
219 | 0 |
|
220 | 0 | LittleEndian::write_u32(buf, instr); |
221 | 0 | }, |
222 | 0 | RiscvRelocationEncoding::LO12 => { |
223 | 0 | let mut instr = LittleEndian::read_u32(buf); |
224 | 0 | instr &= 0x000F_FFFF; |
225 | 0 |
|
226 | 0 | instr |= (val_cast & 0xFFF) << 20; |
227 | 0 |
|
228 | 0 | LittleEndian::write_u32(buf, instr); |
229 | 0 | }, |
230 | 0 | RiscvRelocationEncoding::LO12S => { |
231 | 0 | let mut instr = LittleEndian::read_u32(buf); |
232 | 0 | instr &= 0x01FF_F07F; |
233 | 0 |
|
234 | 0 | instr |= (val_cast & 0x1F) << 7; |
235 | 0 | instr |= ((val_cast >> 5) & 0x7F) << 25; |
236 | 0 |
|
237 | 0 | LittleEndian::write_u32(buf, instr); |
238 | 0 | }, |
239 | 0 | RiscvRelocationEncoding::SPLIT32 => { |
240 | 0 | let mut instr1 = LittleEndian::read_u32(&buf[..4]); |
241 | 0 | let mut instr2 = LittleEndian::read_u32(&buf[4..]); |
242 | 0 | instr1 &= 0x0000_0FFF; |
243 | 0 | instr2 &= 0x000F_FFFF; |
244 | 0 |
|
245 | 0 | let val_round: u32 = val_cast.wrapping_add(0x800); |
246 | 0 | instr1 |= val_round & 0xFFFF_F000; |
247 | 0 | instr2 |= (val_cast & 0xFFF) << 20; |
248 | 0 |
|
249 | 0 | LittleEndian::write_u32(&mut buf[..4], instr1); |
250 | 0 | LittleEndian::write_u32(&mut buf[4..], instr2); |
251 | 0 | }, |
252 | 0 | RiscvRelocationEncoding::SPLIT32S => { |
253 | 0 | let mut instr1 = LittleEndian::read_u32(&buf[..4]); |
254 | 0 | let mut instr2 = LittleEndian::read_u32(&buf[4..]); |
255 | 0 | instr1 &= 0x0000_0FFF; |
256 | 0 | instr2 &= 0x01FF_F07F; |
257 | 0 |
|
258 | 0 | let val_round: u32 = val_cast.wrapping_add(0x800); |
259 | 0 | instr1 |= val_round & 0xFFFF_F000; |
260 | 0 | instr2 |= (val_cast & 0x1F) << 7; |
261 | 0 | instr2 |= ((val_cast >> 5) & 0x7F) << 25; |
262 | 0 |
|
263 | 0 | LittleEndian::write_u32(&mut buf[..4], instr1); |
264 | 0 | LittleEndian::write_u32(&mut buf[4..], instr2); |
265 | 0 | }, |
266 | | } |
267 | | |
268 | 0 | Ok(()) |
269 | | } |
270 | | } |
271 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::write_value |
272 | 0 | fn read_value(&self, buf: &[u8]) -> isize { |
273 | 0 | match self.0.encoding { |
274 | 0 | RelocationEncoding::Simple(s) => s.read_value(buf), |
275 | 0 | RelocationEncoding::ArchSpecific(c) => { |
276 | | let bits; |
277 | | let mut unpacked; |
278 | | |
279 | 0 | match c { |
280 | 0 | RiscvRelocationEncoding::B => { |
281 | 0 | bits = 12; |
282 | 0 | let instr = LittleEndian::read_u32(buf); |
283 | 0 |
|
284 | 0 | unpacked = ((instr >> 31) & 0x1) << 12; |
285 | 0 | unpacked |= ((instr >> 25) & 0x3F) << 5; |
286 | 0 | unpacked |= ((instr >> 8) & 0xF) << 1; |
287 | 0 | unpacked |= ((instr >> 7) & 0x1) << 11; |
288 | 0 | }, |
289 | 0 | RiscvRelocationEncoding::J => { |
290 | 0 | bits = 20; |
291 | 0 | let instr = LittleEndian::read_u32(buf); |
292 | 0 |
|
293 | 0 | unpacked = ((instr >> 31) & 0x1) << 20; |
294 | 0 | unpacked |= ((instr >> 21) & 0x3FF) << 1; |
295 | 0 | unpacked |= ((instr >> 20) & 0x1) << 11; |
296 | 0 | unpacked |= ((instr >> 12) & 0xFF) << 12; |
297 | 0 | }, |
298 | 0 | RiscvRelocationEncoding::BC => { |
299 | 0 | bits = 9; |
300 | 0 | let instr = u32::from(LittleEndian::read_u16(buf)); |
301 | 0 |
|
302 | 0 | unpacked = ((instr >> 12) & 0x1) << 8; |
303 | 0 | unpacked |= ((instr >> 10) & 0x3) << 3; |
304 | 0 | unpacked |= ((instr >> 5) & 0x3) << 6; |
305 | 0 | unpacked |= ((instr >> 3) & 0x3) << 1; |
306 | 0 | unpacked |= ((instr >> 2) & 0x1) << 5; |
307 | 0 | }, |
308 | 0 | RiscvRelocationEncoding::JC => { |
309 | 0 | bits = 12; |
310 | 0 | let instr = u32::from(LittleEndian::read_u16(buf)); |
311 | 0 |
|
312 | 0 | unpacked = ((instr >> 12) & 0x1) << 11; |
313 | 0 | unpacked |= ((instr >> 11) & 0x1) << 4; |
314 | 0 | unpacked |= ((instr >> 9) & 0x3) << 8; |
315 | 0 | unpacked |= ((instr >> 8) & 0x1) << 10; |
316 | 0 | unpacked |= ((instr >> 7) & 0x1) << 6; |
317 | 0 | unpacked |= ((instr >> 6) & 0x1) << 7; |
318 | 0 | unpacked |= ((instr >> 3) & 0x7) << 1; |
319 | 0 | unpacked |= ((instr >> 2) & 0x1) << 5; |
320 | 0 | }, |
321 | 0 | RiscvRelocationEncoding::HI20 => { |
322 | 0 | bits = 32; |
323 | 0 | let instr = LittleEndian::read_u32(buf); |
324 | 0 |
|
325 | 0 | unpacked = ((instr >> 12) & 0xFFFFF) << 12; |
326 | 0 | // There's a problem here. We don't know the lower |
327 | 0 | // bits of the value that is being read, but they do matter |
328 | 0 | // if this thing would get relocated. luckily, riscv only does |
329 | 0 | // relative relocations so this should never happen, and we |
330 | 0 | // should be fine with just returning the value without adjustment |
331 | 0 | }, |
332 | 0 | RiscvRelocationEncoding::LO12 => { |
333 | 0 | bits = 12; |
334 | 0 | let instr = LittleEndian::read_u32(buf); |
335 | 0 |
|
336 | 0 | unpacked = (instr >> 20) & 0xFFF; |
337 | 0 | }, |
338 | 0 | RiscvRelocationEncoding::LO12S => { |
339 | 0 | bits = 12; |
340 | 0 | let instr = LittleEndian::read_u32(buf); |
341 | 0 |
|
342 | 0 | unpacked = (instr >> 7) & 0x1F; |
343 | 0 | unpacked |= ((instr >> 25) & 0x7F) << 5; |
344 | 0 | }, |
345 | | RiscvRelocationEncoding::SPLIT32 => { |
346 | 0 | bits = 32; |
347 | 0 | let instr1 = LittleEndian::read_u32(&buf[..4]); |
348 | 0 | let instr2 = LittleEndian::read_u32(&buf[4..]); |
349 | | |
350 | 0 | unpacked = ((instr1 >> 12) & 0xFFFFF) << 12; |
351 | 0 | let mut lower: u32 = (instr2 >> 20) & 0xFFF; |
352 | | |
353 | | // sign extend the lower part and then add them |
354 | 0 | lower = (lower ^ 0x800).wrapping_sub(0x800); |
355 | 0 | unpacked = unpacked.wrapping_add(lower) |
356 | | |
357 | | }, |
358 | | RiscvRelocationEncoding::SPLIT32S => { |
359 | 0 | bits = 32; |
360 | 0 | let instr1 = LittleEndian::read_u32(&buf[..4]); |
361 | 0 | let instr2 = LittleEndian::read_u32(&buf[4..]); |
362 | | |
363 | 0 | unpacked = ((instr1 >> 12) & 0xFFFFF) << 12; |
364 | 0 | let mut lower: u32 = (instr2 >> 7) & 0x1F; |
365 | 0 | lower |= ((instr2 >> 25) & 0x7F) << 5; |
366 | | |
367 | | // sign extend the lower part and then add them |
368 | 0 | lower = (lower ^ 0x800).wrapping_sub(0x800); |
369 | 0 | unpacked = unpacked.wrapping_add(lower) |
370 | | }, |
371 | | } |
372 | | |
373 | | // sign extension |
374 | 0 | let offset = 1u64 << (bits - 1); |
375 | 0 | let value: u64 = (unpacked as u64 ^ offset).wrapping_sub(offset); |
376 | | |
377 | 0 | value as i64 as isize |
378 | | } |
379 | | } |
380 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::read_value Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::read_value |
381 | 0 | fn kind(&self) -> RelocationKind { |
382 | 0 | self.0.kind |
383 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::kind Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::kind |
384 | 0 | fn page_size() -> usize { |
385 | 0 | 4096 |
386 | 0 | } Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::page_size Unexecuted instantiation: <dynasmrt::riscv::RiscvRelocation as dynasmrt::relocations::Relocation>::page_size |
387 | | } |
388 | | |
389 | | /// A RISC-V Assembler. This is aliased here for backwards compatability. |
390 | | pub type Assembler = crate::Assembler<RiscvRelocation>; |
391 | | /// A RISC-V AssemblyModifier. This is aliased here for backwards compatability. |
392 | | pub type AssemblyModifier<'a> = crate::Modifier<'a, RiscvRelocation>; |
393 | | /// A RISC-V UncommittedModifier. This is aliased here for backwards compatability. |
394 | | pub 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)] |
401 | 0 | pub fn immediate_out_of_range_unsigned_32(immediate: u32) -> ! { |
402 | 0 | panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_32 Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_32 |
403 | | } |
404 | | |
405 | | /// Handler for `i32` out-of-range riscv64 & riscv32 immediates. |
406 | | #[inline(never)] |
407 | 0 | pub fn immediate_out_of_range_signed_32(immediate: i32) -> ! { |
408 | 0 | panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_32 Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_32 |
409 | | } |
410 | | /// Handler for `u64` out-of-range riscv64 & riscv32 immediates. |
411 | | #[inline(never)] |
412 | 0 | pub fn immediate_out_of_range_unsigned_64(immediate: u64) -> ! { |
413 | 0 | panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_64 Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_unsigned_64 |
414 | | } |
415 | | |
416 | | /// Handler for `i64` out-of-range riscv64 & riscv32 immediates. |
417 | | #[inline(never)] |
418 | 0 | pub fn immediate_out_of_range_signed_64(immediate: i64) -> ! { |
419 | 0 | panic!("Cannot assemble this RISC-V instruction. Immediate {immediate} is out of range.")Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_64 Unexecuted instantiation: dynasmrt::riscv::immediate_out_of_range_signed_64 |
420 | | } |
421 | | |
422 | | /// Handler for invalid riscv64 & riscv32 registers. |
423 | | #[inline(never)] |
424 | 0 | pub fn invalid_register(register: u8) -> ! { |
425 | 0 | panic!("Cannot assemble this RISC-V instruction. Register x{register} cannot be encoded.")Unexecuted instantiation: dynasmrt::riscv::invalid_register Unexecuted instantiation: dynasmrt::riscv::invalid_register |
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)] |
433 | | pub 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 | | } |
443 | | reg_impls!(RX); |
444 | | |
445 | | /// 4, 8 or 16-byte floating point registers |
446 | | #[allow(missing_docs)] |
447 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
448 | | pub 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 | | } |
458 | | reg_impls!(RF); |
459 | | |
460 | | |
461 | | #[cfg(test)] |
462 | | mod 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 | | } |