Skip to main content

linker_utils/
riscv64.rs

1use crate::bit_misc::BitExtraction;
2use crate::elf::AllowedRange;
3use crate::elf::RelocationKind;
4use crate::elf::RelocationKindInfo;
5use crate::elf::RelocationSize;
6use crate::elf::RiscVInstruction;
7use crate::relaxation::RelocationModifier;
8use crate::utils::and_from_slice;
9use crate::utils::or_from_slice;
10use crate::utils::u32_from_slice;
11
12/// JAL instruction range: signed 21-bit immediate, 2-byte aligned.
13pub const JAL_RANGE: std::ops::RangeInclusive<i64> = -(1i64 << 20)..=((1i64 << 20) - 1);
14
15#[inline]
16#[must_use]
17pub fn distance_fits_jal(distance: i64) -> bool {
18    JAL_RANGE.contains(&distance)
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum RelaxationKind {
23    /// Leave the instruction alone. Used when we only want to change the kind of relocation used.
24    NoOp,
25
26    /// Replace with nop
27    ReplaceWithNop,
28
29    /// Rewrite auipc+jalr to jal.
30    CallToJal,
31
32    /// Rewrite 4-byte `lui rd, imm` to 2-byte `c.lui rd, nzimm`.
33    Hi20ToCLui,
34
35    /// Delete the `lui` instruction entirely when `%hi(value)` is zero.
36    Hi20Delete,
37
38    /// Rewrite the `rs1` field of an I-type or S-type instruction from the deleted `lui`'s `rd` to
39    /// `x0`.
40    Lo12Rs1ToZero,
41}
42
43impl RelaxationKind {
44    pub fn apply(self, section_bytes: &mut [u8], offset_in_section: &mut u64, _addend: &mut i64) {
45        let offset = *offset_in_section as usize;
46        match self {
47            RelaxationKind::NoOp => {}
48            RelaxationKind::ReplaceWithNop => {
49                section_bytes[offset..offset + 4].copy_from_slice(&[
50                    0x01, 0x0, // nop
51                    0x01, 0x0, // nop
52                ]);
53            }
54            RelaxationKind::CallToJal => {
55                let auipc_word = u32_from_slice(&section_bytes[offset..]);
56                let rd = (auipc_word >> 7) & 0x1f;
57                let jal_base = 0x6fu32 | (rd << 7);
58                section_bytes[offset..offset + 4].copy_from_slice(&jal_base.to_le_bytes());
59            }
60            RelaxationKind::Hi20ToCLui => {
61                let lui_lo = u16::from_le_bytes([section_bytes[offset], section_bytes[offset + 1]]);
62                let rd = (lui_lo >> 7) & 0x1f;
63                let clui_base: u16 = 0x6001 | (rd << 7);
64                section_bytes[offset..offset + 2].copy_from_slice(&clui_base.to_le_bytes());
65            }
66            RelaxationKind::Hi20Delete => {}
67            RelaxationKind::Lo12Rs1ToZero => {
68                let word = u32_from_slice(&section_bytes[offset..]);
69                let word = word & !RS1_MASK;
70                section_bytes[offset..offset + 4].copy_from_slice(&word.to_le_bytes());
71            }
72        }
73    }
74
75    #[must_use]
76    pub fn next_modifier(&self) -> RelocationModifier {
77        match self {
78            RelaxationKind::CallToJal | RelaxationKind::Hi20Delete => {
79                RelocationModifier::SkipNextRelocation
80            }
81            _ => RelocationModifier::Normal,
82        }
83    }
84
85    /// Returns true if the HI20 value is zero, meaning the `lui` instruction can be deleted
86    /// entirely and the following LO12 instruction's rs1 changed to x0.
87    #[must_use]
88    pub fn hi20_is_zero(value: u64) -> bool {
89        let hi20 = value.wrapping_add(0x800) >> 12;
90
91        (hi20 & 0xf_ffff) == 0
92    }
93
94    /// Returns true if the HI20 value (i.e. `(value + 0x800) >> 12`) fits in the
95    /// c.lui 6-bit signed non-zero immediate range: [-32, -1] ∪ [1, 31].
96    #[must_use]
97    pub fn hi20_fits_clui(value: u64) -> bool {
98        let hi20 = value.wrapping_add(0x800) >> 12;
99        let hi20_signed = hi20 as i64;
100        // The 6-bit signed immediate excludes 0, and wraps around for large values.
101        // Check that the sign-extended 6-bit value equals the full hi20.
102        let as_6bit = ((hi20_signed as i8) << 2) >> 2; // sign-extend from 6 bits
103        i64::from(as_6bit) == hi20_signed && hi20_signed != 0
104    }
105
106    /// Returns true if the rd register is valid for c.lui.
107    #[must_use]
108    pub fn rd_valid_for_clui(rd: u32) -> bool {
109        rd != 0 && rd != 2
110    }
111
112    /// Returns the `RelocationKindInfo` to use when a HI20 relocation has been relaxed to c.lui.
113    #[must_use]
114    pub fn clui_rel_info() -> RelocationKindInfo {
115        RelocationKindInfo {
116            kind: RelocationKind::Absolute,
117            size: RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::CluiType),
118            mask: None,
119            range: AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
120            alignment: 1,
121            bias: 0,
122            thunkable: false,
123        }
124    }
125}
126
127#[must_use]
128pub const fn relocation_type_from_raw(r_type: u32) -> Option<RelocationKindInfo> {
129    // The relocation listing following the order defined in the standard:
130    // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#relocations
131    let (kind, size, mask, range, alignment) = match r_type {
132        object::elf::R_RISCV_NONE => (
133            RelocationKind::None,
134            RelocationSize::ByteSize(0),
135            None,
136            AllowedRange::no_check(),
137            1,
138        ),
139        object::elf::R_RISCV_32 => (
140            RelocationKind::Absolute,
141            RelocationSize::ByteSize(4),
142            None,
143            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
144            1,
145        ),
146        object::elf::R_RISCV_64 => (
147            RelocationKind::Absolute,
148            RelocationSize::ByteSize(8),
149            None,
150            AllowedRange::no_check(),
151            1,
152        ),
153        object::elf::R_RISCV_BRANCH => (
154            RelocationKind::Relative,
155            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::BType),
156            None,
157            // Can represent an even signed 13-bit offset (-4096 to +4094).
158            AllowedRange::new(-(2i64.pow(12)), 2i64.pow(12) - 1),
159            2,
160        ),
161        object::elf::R_RISCV_JAL => (
162            RelocationKind::Relative,
163            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::JType),
164            None,
165            // Can represent an even signed 21-bit offset (-1MiB to +1MiB-2).
166            AllowedRange::new(-(2i64.pow(20)), 2i64.pow(20) - 1),
167            2,
168        ),
169        object::elf::R_RISCV_CALL | object::elf::R_RISCV_CALL_PLT => (
170            RelocationKind::PltRelative,
171            RelocationSize::bit_mask_riscv(0, 64, RiscVInstruction::UiType),
172            None,
173            // Can represent signed or unsigned value.
174            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
175            1,
176        ),
177        object::elf::R_RISCV_GOT_HI20 => (
178            RelocationKind::GotRelative,
179            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
180            None,
181            // Can represent signed or unsigned value.
182            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
183            1,
184        ),
185        object::elf::R_RISCV_TLS_GOT_HI20 => (
186            RelocationKind::GotTpOff,
187            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
188            None,
189            // Can represent signed or unsigned value.
190            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
191            1,
192        ),
193        object::elf::R_RISCV_TLS_GD_HI20 => (
194            RelocationKind::TlsGd,
195            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
196            None,
197            // Can represent signed or unsigned value.
198            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
199            1,
200        ),
201        object::elf::R_RISCV_PCREL_HI20 => (
202            RelocationKind::Relative,
203            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
204            None,
205            // Can represent signed or unsigned value.
206            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
207            1,
208        ),
209        object::elf::R_RISCV_PCREL_LO12_I => (
210            RelocationKind::RelativeRiscVLow12,
211            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::IType),
212            None,
213            // Can represent signed or unsigned value.
214            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
215            1,
216        ),
217        object::elf::R_RISCV_PCREL_LO12_S => (
218            RelocationKind::RelativeRiscVLow12,
219            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::SType),
220            None,
221            // Can represent signed or unsigned value.
222            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
223            1,
224        ),
225        object::elf::R_RISCV_HI20 => (
226            RelocationKind::Absolute,
227            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
228            None,
229            // Can represent signed or unsigned value.
230            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
231            1,
232        ),
233        object::elf::R_RISCV_LO12_I => (
234            RelocationKind::Absolute,
235            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::IType),
236            None,
237            // Can represent signed or unsigned value.
238            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
239            1,
240        ),
241        object::elf::R_RISCV_LO12_S => (
242            RelocationKind::Absolute,
243            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::SType),
244            None,
245            // Can represent signed or unsigned value.
246            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
247            1,
248        ),
249        object::elf::R_RISCV_TPREL_HI20 => (
250            RelocationKind::TpOff,
251            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::UType),
252            None,
253            // Can represent signed or unsigned value.
254            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
255            1,
256        ),
257        object::elf::R_RISCV_TPREL_LO12_I => (
258            RelocationKind::TpOff,
259            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::IType),
260            None,
261            // Can represent signed or unsigned value.
262            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
263            1,
264        ),
265        object::elf::R_RISCV_TPREL_LO12_S => (
266            RelocationKind::TpOff,
267            RelocationSize::bit_mask_riscv(0, 32, RiscVInstruction::SType),
268            None,
269            // Can represent signed or unsigned value.
270            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
271            1,
272        ),
273        object::elf::R_RISCV_TPREL_ADD => (
274            RelocationKind::None,
275            RelocationSize::ByteSize(0),
276            None,
277            AllowedRange::no_check(),
278            1,
279        ),
280        object::elf::R_RISCV_ADD8 => (
281            RelocationKind::AbsoluteAddition,
282            RelocationSize::ByteSize(1),
283            None,
284            AllowedRange::no_check(),
285            1,
286        ),
287        object::elf::R_RISCV_ADD16 => (
288            RelocationKind::AbsoluteAddition,
289            RelocationSize::ByteSize(2),
290            None,
291            AllowedRange::no_check(),
292            1,
293        ),
294        object::elf::R_RISCV_ADD32 => (
295            RelocationKind::AbsoluteAddition,
296            RelocationSize::ByteSize(4),
297            None,
298            AllowedRange::no_check(),
299            1,
300        ),
301        object::elf::R_RISCV_ADD64 => (
302            RelocationKind::AbsoluteAddition,
303            RelocationSize::ByteSize(8),
304            None,
305            AllowedRange::no_check(),
306            1,
307        ),
308        object::elf::R_RISCV_SUB8 => (
309            RelocationKind::AbsoluteSubtraction,
310            RelocationSize::ByteSize(1),
311            None,
312            AllowedRange::no_check(),
313            1,
314        ),
315        object::elf::R_RISCV_SUB16 => (
316            RelocationKind::AbsoluteSubtraction,
317            RelocationSize::ByteSize(2),
318            None,
319            AllowedRange::no_check(),
320            1,
321        ),
322        object::elf::R_RISCV_SUB32 => (
323            RelocationKind::AbsoluteSubtraction,
324            RelocationSize::ByteSize(4),
325            None,
326            AllowedRange::no_check(),
327            1,
328        ),
329        object::elf::R_RISCV_SUB64 => (
330            RelocationKind::AbsoluteSubtraction,
331            RelocationSize::ByteSize(8),
332            None,
333            AllowedRange::no_check(),
334            1,
335        ),
336        object::elf::R_RISCV_GOT32_PCREL => (
337            RelocationKind::GotRelative,
338            RelocationSize::ByteSize(4),
339            None,
340            // Can represent signed or unsigned value.
341            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
342            1,
343        ),
344        object::elf::R_RISCV_ALIGN => (
345            RelocationKind::Alignment,
346            RelocationSize::ByteSize(0),
347            None,
348            AllowedRange::no_check(),
349            1,
350        ),
351        object::elf::R_RISCV_RVC_BRANCH => (
352            RelocationKind::Relative,
353            RelocationSize::bit_mask_riscv(0, 16, RiscVInstruction::CbType),
354            None,
355            AllowedRange::new(-(2i64.pow(8)), 2i64.pow(8) - 1),
356            2,
357        ),
358        object::elf::R_RISCV_RVC_JUMP => (
359            RelocationKind::Relative,
360            RelocationSize::bit_mask_riscv(0, 16, RiscVInstruction::CjType),
361            None,
362            AllowedRange::new(-(2i64.pow(11)), 2i64.pow(11) - 1),
363            2,
364        ),
365        object::elf::R_RISCV_RELAX => (
366            RelocationKind::None,
367            RelocationSize::ByteSize(0),
368            None,
369            AllowedRange::no_check(),
370            1,
371        ),
372        object::elf::R_RISCV_SUB6 => (
373            RelocationKind::AbsoluteSubtractionWord6,
374            RelocationSize::ByteSize(1),
375            None,
376            AllowedRange::no_check(),
377            1,
378        ),
379        object::elf::R_RISCV_SET6 => (
380            RelocationKind::AbsoluteSetWord6,
381            RelocationSize::ByteSize(1),
382            None,
383            AllowedRange::no_check(),
384            1,
385        ),
386        object::elf::R_RISCV_SET8 => (
387            RelocationKind::AbsoluteSet,
388            RelocationSize::ByteSize(1),
389            None,
390            AllowedRange::no_check(),
391            1,
392        ),
393        object::elf::R_RISCV_SET16 => (
394            RelocationKind::AbsoluteSet,
395            RelocationSize::ByteSize(2),
396            None,
397            AllowedRange::no_check(),
398            1,
399        ),
400        object::elf::R_RISCV_SET32 => (
401            RelocationKind::AbsoluteSet,
402            RelocationSize::ByteSize(4),
403            None,
404            AllowedRange::no_check(),
405            1,
406        ),
407        object::elf::R_RISCV_32_PCREL => (
408            RelocationKind::Relative,
409            RelocationSize::ByteSize(4),
410            None,
411            // Can represent signed or unsigned value.
412            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
413            1,
414        ),
415        object::elf::R_RISCV_PLT32 => (
416            RelocationKind::PltRelative,
417            RelocationSize::ByteSize(4),
418            None,
419            // Can represent signed or unsigned value.
420            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
421            1,
422        ),
423        // We process the subtraction in the SUB_ULEB128 relocation,
424        // thus we skip the first relocation in the pair.
425        object::elf::R_RISCV_SET_ULEB128 => (
426            RelocationKind::Relative,
427            RelocationSize::ByteSize(0),
428            None,
429            AllowedRange::no_check(),
430            1,
431        ),
432        object::elf::R_RISCV_SUB_ULEB128 => (
433            RelocationKind::PairSubtractionULEB128(object::elf::R_RISCV_SET_ULEB128),
434            RelocationSize::ByteSize(8),
435            None,
436            AllowedRange::no_check(),
437            1,
438        ),
439        // TODO: #712: support TLSDESC once glibc supports the feature
440        object::elf::R_RISCV_TLSDESC_HI20 => return None,
441        object::elf::R_RISCV_TLSDESC_LOAD_LO12 => return None,
442        object::elf::R_RISCV_TLSDESC_ADD_LO12 => return None,
443        object::elf::R_RISCV_TLSDESC_CALL => return None,
444        _ => return None,
445    };
446
447    Some(RelocationKindInfo {
448        kind,
449        size,
450        mask,
451        range,
452        alignment,
453        bias: 0,
454        thunkable: false,
455    })
456}
457
458/// Mask for the rs1 field (bits [19:15]) in R/I/S/B-type instructions.
459const RS1_MASK: u32 = 0x1f << 15;
460
461const UTYPE_IMMEDIATE_MASK: u32 = 0b0000_0000_0000_0000_0000_1111_1111_1111;
462const ITYPE_IMMEDIATE_MASK: u32 = 0b0000_0000_0000_1111_1111_1111_1111_1111;
463const STYPE_IMMEDIATE_MASK: u32 = 0b0000_0001_1111_1111_1111_0000_0111_1111;
464const BTYPE_IMMEDIATE_MASK: u32 = 0b0000_0001_1111_1111_1111_0000_0111_1111;
465const JTYPE_IMMEDIATE_MASK: u32 = 0b0000_0000_0000_0000_0000_1111_1111_1111;
466
467const CBTYPE_IMMEDIATE_MASK: u16 = 0b1110_0011_1000_0011;
468const CJTYPE_IMMEDIATE_MASK: u16 = 0b1110_0000_0000_0011;
469// c.lui: [15:13]=funct3 [12]=nzimm[5] [11:7]=rd [6:2]=nzimm[4:0] [1:0]=op
470const CLUITYPE_IMMEDIATE_MASK: u16 = 0b1110_1111_1000_0011;
471
472impl RiscVInstruction {
473    // Encode computed relocation value and store it based on the encoding of an instruction.
474    // A handy page where one can easily find instruction encoding:
475    // https://msyksphinz-self.github.io/riscv-isadoc/html/index.html.
476
477    // During the build of the static libc.a, there are various places where the immediate operand
478    // of an instruction is already filled up. Thus, we zero the bits before a relocation value is
479    // applied.
480    pub fn write_to_value(self, extracted_value: u64, _negative: bool, dest: &mut [u8]) {
481        match self {
482            RiscVInstruction::UiType => {
483                RiscVInstruction::UType.write_to_value(extracted_value, _negative, &mut dest[..4]);
484                RiscVInstruction::IType.write_to_value(extracted_value, _negative, &mut dest[4..]);
485            }
486            RiscVInstruction::UType => {
487                // A final address calculation is represented as addition of HI20 and LO12, where
488                // we must prevent add 0x800 in order to not make HI20 a huge negative if the final
489                // value is a small negative value.
490                // For instance, -10i32 (0xfffffff6) should become 0x0 (HI20) and 0xff6 (LO12).
491                let mask = (extracted_value
492                    .wrapping_add(0x800)
493                    .extract_bit_range(12..32) as u32)
494                    << 12;
495                and_from_slice(dest, UTYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
496                or_from_slice(dest, &mask.to_le_bytes());
497            }
498            RiscVInstruction::IType => {
499                let mask = extracted_value << 20;
500                and_from_slice(dest, ITYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
501                or_from_slice(dest, &(mask as u32).to_le_bytes());
502            }
503            RiscVInstruction::SType => {
504                let mut mask = extracted_value.extract_bit_range(0..5) << 7;
505                mask |= extracted_value.extract_bit_range(5..12) << 25;
506                and_from_slice(dest, STYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
507                or_from_slice(dest, &(mask as u32).to_le_bytes());
508            }
509            RiscVInstruction::BType => {
510                let mut mask = extracted_value.extract_bit(11) << 7;
511                mask |= extracted_value.extract_bit_range(1..5) << 8;
512                mask |= extracted_value.extract_bit_range(5..11) << 25;
513                mask |= extracted_value.extract_bit(12) << 31;
514                and_from_slice(dest, BTYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
515                or_from_slice(dest, &(mask as u32).to_le_bytes());
516            }
517            RiscVInstruction::JType => {
518                let mut mask = extracted_value.extract_bit_range(12..20) << 12;
519                mask |= extracted_value.extract_bit(11) << 20;
520                mask |= extracted_value.extract_bit_range(1..11) << 21;
521                mask |= extracted_value.extract_bit(20) << 31;
522                and_from_slice(dest, JTYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
523                or_from_slice(dest, &(mask as u32).to_le_bytes());
524            }
525            RiscVInstruction::CbType => {
526                let mut mask = extracted_value.extract_bit(5) << 2;
527                mask |= extracted_value.extract_bit_range(1..3) << 3;
528                mask |= extracted_value.extract_bit_range(6..8) << 5;
529                // rs1' register takes 3 bits here
530                mask |= extracted_value.extract_bit_range(3..5) << 10;
531                mask |= extracted_value.extract_bit(8) << 12;
532                // The compressed instruction only takes 2 bytes.
533                and_from_slice(dest, CBTYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
534                or_from_slice(dest, &mask.to_le_bytes()[..2]);
535            }
536            RiscVInstruction::CjType => {
537                let mut mask = extracted_value.extract_bit(5) << 2;
538                mask |= extracted_value.extract_bit_range(1..4) << 3;
539                mask |= extracted_value.extract_bit(7) << 6;
540                mask |= extracted_value.extract_bit(6) << 7;
541                mask |= extracted_value.extract_bit(10) << 8;
542                mask |= extracted_value.extract_bit_range(8..10) << 9;
543                mask |= extracted_value.extract_bit(4) << 11;
544                mask |= extracted_value.extract_bit(11) << 12;
545                // The compressed instruction only takes 2 bytes.
546                and_from_slice(dest, CJTYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
547                or_from_slice(dest, &mask.to_le_bytes()[..2]);
548            }
549            RiscVInstruction::CluiType => {
550                let hi20 = extracted_value.wrapping_add(0x800) >> 12;
551                let mut mask = (hi20 & 0x1f) << 2; // nzimm[4:0]
552                mask |= ((hi20 >> 5) & 1) << 12; // nzimm[5]
553                and_from_slice(dest, CLUITYPE_IMMEDIATE_MASK.to_le_bytes().as_slice());
554                or_from_slice(dest, &(mask as u16).to_le_bytes());
555            }
556        };
557    }
558
559    /// The inverse of `write_to_value`. Returns `(extracted_value, negative)`. Supplied `bytes`
560    /// must be at least 4 bytes, otherwise we panic.
561    #[must_use]
562    pub fn read_value(self, bytes: &[u8]) -> (u64, bool) {
563        match self {
564            RiscVInstruction::UiType => {
565                let (hi, _) = RiscVInstruction::UType.read_value(&bytes[..4]);
566                let (lo, _) = RiscVInstruction::IType.read_value(&bytes[4..]);
567                (hi << 12 | lo, false)
568            }
569            RiscVInstruction::UType => {
570                let value = u32_from_slice(bytes);
571                let imm = (value >> 12) & 0xfffff;
572                let adjusted = ((imm as i32) << 12) >> 12;
573                ((adjusted as u64).wrapping_sub(0x800), false)
574            }
575            RiscVInstruction::IType => {
576                let value = u32_from_slice(bytes);
577                let imm = (value >> 20) & 0xfff;
578                let sign_extended = ((imm as i32) << 20) >> 20;
579                (sign_extended as u64, sign_extended < 0)
580            }
581            RiscVInstruction::SType => {
582                let value = u32_from_slice(bytes);
583                let imm_low = (value >> 7) & 0x1f;
584                let imm_high = (value >> 25) & 0x7f;
585                let imm = (imm_high << 5) | imm_low;
586                let sign_extended = ((imm as i32) << 20) >> 20;
587                (sign_extended as u64, sign_extended < 0)
588            }
589            RiscVInstruction::BType => {
590                let value = u32_from_slice(bytes);
591                let imm11 = (value >> 7) & 0x1;
592                let imm1_4 = (value >> 8) & 0xf;
593                let imm5_10 = (value >> 25) & 0x3f;
594                let imm12 = (value >> 31) & 0x1;
595
596                let imm = (imm12 << 12) | (imm11 << 11) | (imm5_10 << 5) | (imm1_4 << 1);
597                let sign_extended = ((imm as i32) << 19) >> 19;
598                (sign_extended as u64, sign_extended < 0)
599            }
600            RiscVInstruction::JType => {
601                let value = u32_from_slice(bytes);
602                let imm12_19 = (value >> 12) & 0xff;
603                let imm11 = (value >> 20) & 0x1;
604                let imm1_10 = (value >> 21) & 0x3ff;
605                let imm20 = (value >> 31) & 0x1;
606
607                let imm = (imm20 << 20) | (imm12_19 << 12) | (imm11 << 11) | (imm1_10 << 1);
608                let sign_extended = ((imm as i32) << 11) >> 11;
609                (sign_extended as u64, sign_extended < 0)
610            }
611            RiscVInstruction::CbType => {
612                let value = u16::from_le_bytes([bytes[0], bytes[1]]);
613                let imm5 = (value >> 2) & 0x1;
614                let imm1_2 = (value >> 3) & 0x3;
615                let imm6_7 = (value >> 5) & 0x3;
616                let imm3_4 = (value >> 10) & 0x3;
617                let imm8 = (value >> 12) & 0x1;
618
619                let imm = (imm8 << 8) | (imm6_7 << 6) | (imm5 << 5) | (imm3_4 << 3) | (imm1_2 << 1);
620                let sign_extended = (i32::from(imm) << 23) >> 23;
621                (sign_extended as u64, sign_extended < 0)
622            }
623            RiscVInstruction::CjType => {
624                let value = u16::from_le_bytes([bytes[0], bytes[1]]);
625                let imm5 = (value >> 2) & 0x1;
626                let imm1_3 = (value >> 3) & 0x7;
627                let imm7 = (value >> 6) & 0x1;
628                let imm6 = (value >> 7) & 0x1;
629                let imm10 = (value >> 8) & 0x1;
630                let imm8_9 = (value >> 9) & 0x3;
631                let imm4 = (value >> 11) & 0x1;
632                let imm11 = (value >> 12) & 0x1;
633
634                let imm = (imm11 << 11)
635                    | (imm10 << 10)
636                    | (imm8_9 << 8)
637                    | (imm7 << 7)
638                    | (imm6 << 6)
639                    | (imm5 << 5)
640                    | (imm4 << 4)
641                    | (imm1_3 << 1);
642                let sign_extended = (i32::from(imm) << 20) >> 20;
643                (sign_extended as u64, sign_extended < 0)
644            }
645            RiscVInstruction::CluiType => {
646                let value = u16::from_le_bytes([bytes[0], bytes[1]]);
647                let nzimm4_0 = (value >> 2) & 0x1f;
648                let nzimm5 = (value >> 12) & 0x1;
649                let nzimm = (nzimm5 << 5) | nzimm4_0;
650                let hi20 = ((i32::from(nzimm) << 26) >> 26) as u64;
651                let extracted = (hi20 << 12).wrapping_sub(0x800);
652                (extracted, (hi20 as i64) < 0)
653            }
654        }
655    }
656}
657
658#[test]
659fn test_riscv_insn_immediate_mask() {
660    for (mask, insn) in &[
661        (UTYPE_IMMEDIATE_MASK, RiscVInstruction::UType),
662        (ITYPE_IMMEDIATE_MASK, RiscVInstruction::IType),
663        (STYPE_IMMEDIATE_MASK, RiscVInstruction::SType),
664        (BTYPE_IMMEDIATE_MASK, RiscVInstruction::BType),
665        (JTYPE_IMMEDIATE_MASK, RiscVInstruction::JType),
666    ] {
667        let mut dest = [0u8; 4];
668        let value = if matches!(insn, RiscVInstruction::UType) {
669            u64::MAX.wrapping_sub(0x800)
670        } else {
671            u64::MAX
672        };
673        insn.write_to_value(value, false, &mut dest);
674        assert_eq!(!mask, u32::from_le_bytes(dest));
675    }
676}
677
678#[test]
679fn test_riscv_insn_rvcimmediate_mask() {
680    for (mask, insn) in &[
681        (CBTYPE_IMMEDIATE_MASK, RiscVInstruction::CbType),
682        (CJTYPE_IMMEDIATE_MASK, RiscVInstruction::CjType),
683    ] {
684        let mut dest = [0u8; 2];
685        insn.write_to_value(u64::from(u16::MAX), false, &mut dest);
686        assert_eq!(!mask, u16::from_le_bytes(dest));
687    }
688}