Skip to main content

asmkit/riscv/
assembler.rs

1use super::opcodes::Opcode;
2use crate::AsmError;
3use crate::core::arch_traits::Arch;
4use crate::core::buffer::{CodeBuffer, CodeOffset, ConstantData, LabelUse, Reloc, RelocTarget};
5use crate::core::operand::*;
6use crate::core::operand::{Imm, Sym};
7use crate::core::patch::{PatchableBlock, PatchableSite};
8use crate::core::target::Environment;
9use crate::riscv::instdb::{ANY, OPCODE_FEATURE_CONTEXT, OPCODE_FEATURE_MASKS, SIGNATURE_TABLE};
10use crate::riscv::opcodes::Inst;
11use crate::riscv::opcodes::{ALL_OPCODES, Encoding, OPCODE_XLEN, SHORT_OPCODE};
12use crate::riscv::{Gp, RA, ZERO};
13pub struct Assembler<'a> {
14    pub(crate) buffer: &'a mut CodeBuffer,
15    /// Scratch error set by the generated emitter during one checked attempt.
16    last_error: Option<AsmError>,
17}
18
19fn validate_raw_operand(op: &Operand) -> bool {
20    let Some(op_type) = op.signature.try_op_type() else {
21        return false;
22    };
23
24    match op_type {
25        OperandType::None => op.signature.bits() == 0,
26        OperandType::Reg => {
27            let Some(reg_type) = op.signature.try_reg_type() else {
28                return false;
29            };
30            let Some(_) = op.signature.try_reg_group() else {
31                return false;
32            };
33            let expected = crate::riscv::Reg::signature_of(reg_type);
34            let mask = OperandSignature::OP_TYPE_MASK
35                | OperandSignature::REG_TYPE_MASK
36                | OperandSignature::REG_GROUP_MASK
37                | OperandSignature::SIZE_MASK;
38            expected.bits() != 0
39                && op.signature.subset(mask) == expected.subset(mask)
40                && op.id() <= 31
41        }
42        OperandType::Mem => {
43            op.signature.try_mem_base_type().is_some()
44                && op.signature.try_mem_index_type().is_some()
45        }
46        OperandType::Imm | OperandType::Label | OperandType::Sym => true,
47        OperandType::RegList => false,
48    }
49}
50
51impl<'a> Assembler<'a> {
52    pub fn new(buffer: &'a mut CodeBuffer) -> Self {
53        if !matches!(buffer.env().arch(), Arch::RISCV32 | Arch::RISCV64) {
54            return Self::poisoned(buffer, AsmError::InvalidArch);
55        }
56        Self::unchecked(buffer)
57    }
58
59    pub fn try_new(buffer: &'a mut CodeBuffer) -> Result<Self, AsmError> {
60        if !matches!(buffer.env().arch(), Arch::RISCV32 | Arch::RISCV64) {
61            return Err(AsmError::InvalidArch);
62        }
63        Ok(Self::unchecked(buffer))
64    }
65
66    fn unchecked(buffer: &'a mut CodeBuffer) -> Self {
67        Self {
68            buffer,
69            last_error: None,
70        }
71    }
72
73    fn poisoned(buffer: &'a mut CodeBuffer, error: AsmError) -> Self {
74        buffer.record_error(error);
75        Self::unchecked(buffer)
76    }
77
78    /// Returns the environment (arch/mode) this assembler targets.
79    pub fn environment(&self) -> &Environment {
80        self.buffer.env()
81    }
82
83    /// Tests whether the assembler targets rv32 mode.
84    pub fn is_32bit(&self) -> bool {
85        self.buffer.env().is_32bit()
86    }
87
88    /// Tests whether the assembler targets rv64 mode.
89    pub fn is_64bit(&self) -> bool {
90        self.buffer.env().is_64bit()
91    }
92
93    #[cfg(test)]
94    fn last_error(&self) -> Option<AsmError> {
95        self.buffer.error().cloned()
96    }
97
98    pub fn get_label(&mut self) -> Label {
99        self.buffer.get_label()
100    }
101
102    pub fn bind_label(&mut self, label: Label) {
103        if let Err(error) = self.try_bind_label(label) {
104            self.buffer.record_error(error);
105        }
106    }
107
108    pub fn try_bind_label(&mut self, label: Label) -> Result<(), AsmError> {
109        self.buffer.try_bind_label(label)
110    }
111
112    pub fn add_constant(&mut self, c: impl Into<ConstantData>) -> Label {
113        let c = self.buffer.add_constant(c);
114        self.buffer.get_label_for_constant(c)
115    }
116
117    pub fn label_offset(&self, label: Label) -> CodeOffset {
118        self.buffer.label_offset(label)
119    }
120
121    pub fn data(&self) -> &[u8] {
122        self.buffer.data()
123    }
124
125    pub fn error(&self) -> Option<&AsmError> {
126        self.buffer.error()
127    }
128
129    /// Reserve a nop-filled island for later custom rewriting.
130    pub fn reserve_patch_block(
131        &mut self,
132        size: CodeOffset,
133        align: CodeOffset,
134    ) -> Result<PatchableBlock, AsmError> {
135        self.buffer.reserve_patch_block(size, align)
136    }
137
138    pub fn patchable_j(&mut self, label: Label) -> PatchableSite {
139        if self.buffer.error().is_some() {
140            // SAFETY: poisoned handle for an error path; `u32::MAX` is not a
141            // valid offset into any real image, so applying it fails bounds checks.
142            return unsafe { PatchableSite::new(u32::MAX, LabelUse::RVJal20, 0) };
143        }
144        let checkpoint = self.buffer.checkpoint();
145        let offset = self.buffer.cur_offset();
146        self.j(label);
147        let _ = self
148            .buffer
149            .record_label_patch_site(offset, label, LabelUse::RVJal20);
150        if self.buffer.error().is_some() {
151            self.buffer.rollback(checkpoint);
152            // SAFETY: poisoned handle for an error path; `u32::MAX` is not a
153            // valid offset into any real image, so applying it fails bounds checks.
154            return unsafe { PatchableSite::new(u32::MAX, LabelUse::RVJal20, 0) };
155        }
156        // SAFETY: `j` emits a JAL-style instruction at `offset`.
157        unsafe { PatchableSite::new(offset, LabelUse::RVJal20, 0) }
158    }
159
160    pub fn patchable_call(&mut self, label: Label) -> PatchableSite {
161        if self.buffer.error().is_some() {
162            // SAFETY: poisoned handle for an error path; `u32::MAX` is not a
163            // valid offset into any real image, so applying it fails bounds checks.
164            return unsafe { PatchableSite::new(u32::MAX, LabelUse::RVJal20, 0) };
165        }
166        let checkpoint = self.buffer.checkpoint();
167        let offset = self.buffer.cur_offset();
168        self.jal(RA, label);
169        let _ = self
170            .buffer
171            .record_label_patch_site(offset, label, LabelUse::RVJal20);
172        if self.buffer.error().is_some() {
173            self.buffer.rollback(checkpoint);
174            // SAFETY: poisoned handle for an error path; `u32::MAX` is not a
175            // valid offset into any real image, so applying it fails bounds checks.
176            return unsafe { PatchableSite::new(u32::MAX, LabelUse::RVJal20, 0) };
177        }
178        // SAFETY: `jal` emits a JAL instruction at `offset`.
179        unsafe { PatchableSite::new(offset, LabelUse::RVJal20, 0) }
180    }
181
182    /// Materialize `imm` into `rd` with a fixed-size sequence and a patchable literal.
183    ///
184    /// Layout (RV64): `auipc; ld; jal; .dword`: the returned block covers the 8-byte literal.
185    /// Layout (RV32): `auipc; lw; jal; .word`: the returned block covers the 4-byte literal.
186    ///
187    /// Rewrite with [`PatchableBlock::repatch_u64`] / [`PatchableBlock::repatch_u32`].
188    pub fn patchable_li(&mut self, rd: Gp, imm: impl Into<i64>) -> PatchableBlock {
189        let arch = self.buffer.env().arch();
190        let value = imm.into();
191        if self.buffer.error().is_some() {
192            // SAFETY: poisoned handle for an error path; the sentinel offset is
193            // rejected by the bounds checks when applied.
194            return unsafe { PatchableBlock::new(u32::MAX, 4, arch) };
195        }
196        let checkpoint = self.buffer.checkpoint();
197
198        if self.is_32bit() {
199            self.auipc(rd, crate::core::operand::imm(0));
200            self.lw(rd, rd, crate::core::operand::imm(12));
201            self.jal(ZERO, crate::core::operand::imm(8));
202            let lit = self.buffer.cur_offset();
203            self.buffer.write_u32(value as u32);
204            let _ = self.buffer.record_patch_block(lit, 4, 4);
205            if self.buffer.error().is_some() {
206                self.buffer.rollback(checkpoint);
207                // SAFETY: poisoned handle for the rollback path; the sentinel
208                // offset is rejected by the bounds checks when applied.
209                return unsafe { PatchableBlock::new(u32::MAX, 4, arch) };
210            }
211            // SAFETY: literal word recorded as a patch block at `lit`.
212            unsafe { PatchableBlock::new(lit, 4, arch) }
213        } else {
214            self.auipc(rd, crate::core::operand::imm(0));
215            self.ld(rd, rd, crate::core::operand::imm(12));
216            self.jal(ZERO, crate::core::operand::imm(12));
217            let lit = self.buffer.cur_offset();
218            self.buffer.write_u64(value as u64);
219            let _ = self.buffer.record_patch_block(lit, 8, 4);
220            if self.buffer.error().is_some() {
221                self.buffer.rollback(checkpoint);
222                // SAFETY: poisoned handle for the rollback path; the sentinel
223                // offset is rejected by the bounds checks when applied.
224                return unsafe { PatchableBlock::new(u32::MAX, 8, arch) };
225            }
226            // SAFETY: literal dword recorded as a patch block at `lit`.
227            unsafe { PatchableBlock::new(lit, 8, arch) }
228        }
229    }
230
231    pub fn la(&mut self, rd: Gp, target: impl OperandCast) {
232        if self.buffer.error().is_some() {
233            return;
234        }
235        let checkpoint = self.buffer.checkpoint();
236        let target = target.as_operand();
237
238        if target.is_label() {
239            let off = self.buffer.cur_offset();
240            self.buffer
241                .use_label_at_offset(off, target.as_::<Label>(), LabelUse::RVPCRelHi20);
242            self.auipc(rd, imm(0));
243            let off = self.buffer.cur_offset();
244            self.buffer
245                .use_label_at_offset(off, target.as_::<Label>(), LabelUse::RVPCRelLo12I);
246            self.addi(rd, rd, imm(0));
247        } else if target.is_sym() {
248            if self.buffer.env().pic() {
249                // Load a PC-relative address into a register.
250                // RISC-V does this slightly differently from other arches. We emit a relocation
251                // with a label, instead of the symbol itself.
252                //
253                // See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
254                //
255                // Emit the following code:
256                // label:
257                //   auipc rd, 0              # R_RISCV_GOT_HI20 (symbol_name)
258                //   ld    rd, rd, 0          # R_RISCV_PCREL_LO12_I (label)
259
260                let sym = target.as_::<Sym>();
261
262                // Create the label that is going to be published to the final binary object.
263                let auipc_label = self.get_label();
264                self.bind_label(auipc_label);
265                self.buffer
266                    .add_reloc(Reloc::RiscvGotHi20, RelocTarget::Sym(sym), 0);
267
268                self.auipc(rd, imm(0));
269                // The `ld`/`lw` here, points to the `auipc` label instead of directly to the symbol.
270                self.buffer
271                    .add_reloc(Reloc::RiscvPCRelLo12I, RelocTarget::Label(auipc_label), 0);
272                if self.is_32bit() {
273                    self.lw(rd, rd, imm(0));
274                } else {
275                    self.ld(rd, rd, imm(0));
276                }
277            } else {
278                // In the non PIC sequence we relocate the absolute address into
279                // a prealocatted space, load it into a register and jump over it.
280                //
281                // Emit the following code:
282                //   ld rd, label_data        # (lw on rv32)
283                //   j label_end
284                // label_data:
285                //   <word space>             # ABS8 (ABS4 on rv32)
286                // label_end:
287                let label_data = self.get_label();
288                let label_end = self.get_label();
289
290                if self.is_32bit() {
291                    self.emit_n(
292                        Opcode::LW as i64,
293                        &[rd.as_operand(), rd.as_operand(), label_data.as_operand()],
294                    );
295                } else {
296                    self.emit_n(
297                        Opcode::LD as i64,
298                        &[rd.as_operand(), rd.as_operand(), label_data.as_operand()],
299                    );
300                }
301                self.j(label_end);
302                self.bind_label(label_data);
303                if self.is_32bit() {
304                    self.buffer
305                        .add_reloc(Reloc::Abs4, RelocTarget::Sym(target.as_::<Sym>()), 0);
306                    self.buffer.put4(0);
307                } else {
308                    self.buffer
309                        .add_reloc(Reloc::Abs8, RelocTarget::Sym(target.as_::<Sym>()), 0);
310                    self.buffer.put8(0);
311                }
312                self.bind_label(label_end);
313            }
314        } else {
315            self.buffer.record_error(AsmError::InvalidOperand);
316        }
317        if self.buffer.error().is_some() {
318            self.buffer.rollback(checkpoint);
319        }
320    }
321
322    pub fn call(&mut self, target: impl OperandCast) {
323        if self.buffer.error().is_some() {
324            return;
325        }
326        let checkpoint = self.buffer.checkpoint();
327        let target = target.as_operand();
328
329        if target.is_label() {
330            let off = self.buffer.cur_offset();
331            self.buffer
332                .use_label_at_offset(off, target.as_::<Label>(), LabelUse::RVPCRelHi20);
333            self.auipc(RA, imm(0));
334            let off = self.buffer.cur_offset();
335            self.buffer
336                .use_label_at_offset(off, target.as_::<Label>(), LabelUse::RVPCRelLo12I);
337            self.jalr(RA, RA, imm(0));
338        } else if target.is_sym() {
339            let sym = target.as_::<Sym>();
340
341            let reloc = Reloc::RiscvCallPlt;
342
343            self.buffer.add_reloc(reloc, RelocTarget::Sym(sym), 0);
344            self.auipc(RA, imm(0));
345            self.jalr(RA, RA, imm(0));
346        } else if target.is_imm() {
347            self.jalr(RA, RA, target.as_::<Imm>());
348        } else if target.is_reg() {
349            self.jalr(RA, target.as_::<Gp>(), imm(0));
350        } else {
351            self.buffer.record_error(AsmError::InvalidOperand);
352        }
353        if self.buffer.error().is_some() {
354            self.buffer.rollback(checkpoint);
355        }
356    }
357}
358macro_rules! enc_ops1 {
359    ($op0:ident) => {
360        OperandType::$op0 as u32
361    };
362}
363
364macro_rules! enc_ops2 {
365    ($op0:ident, $op1:ident) => {
366        (OperandType::$op0 as u32) | ((OperandType::$op1 as u32) << 3)
367    };
368}
369
370macro_rules! enc_ops3 {
371    ($op0:ident, $op1:ident, $op2:ident) => {
372        (OperandType::$op0 as u32)
373            | ((OperandType::$op1 as u32) << 3)
374            | ((OperandType::$op2 as u32) << 6)
375    };
376}
377
378macro_rules! enc_ops4 {
379    ($op0:ident, $op1:ident, $op2:ident, $op3:ident) => {
380        (OperandType::$op0 as u32)
381            | ((OperandType::$op1 as u32) << 3)
382            | ((OperandType::$op2 as u32) << 6)
383            | ((OperandType::$op3 as u32) << 9)
384    };
385}
386
387impl<'a> Assembler<'a> {
388    pub fn emit_n(&mut self, opcode: i64, ops: &[&Operand]) {
389        if let Err(error) = self.try_emit_n(opcode, ops) {
390            self.buffer.record_error(error);
391        }
392    }
393
394    pub fn try_emit_n(&mut self, opcode: i64, ops: &[&Operand]) -> Result<(), AsmError> {
395        if let Some(error) = self.buffer.error().cloned() {
396            return Err(error);
397        }
398        if ops.len() > 5 || ops.iter().any(|op| !validate_raw_operand(op)) {
399            return Err(AsmError::InvalidOperand);
400        }
401        let checkpoint = self.buffer.checkpoint();
402        self.last_error = None;
403        self.emit_n_inner(opcode, ops);
404        if let Some(error) = self.last_error.take() {
405            self.buffer.rollback(checkpoint);
406            return Err(error);
407        }
408        if let Some(error) = self.buffer.error().cloned() {
409            self.buffer.rollback(checkpoint);
410            return Err(error);
411        }
412        Ok(())
413    }
414
415    #[allow(unused_assignments)]
416    fn emit_n_inner(&mut self, opcode: i64, ops: &[&crate::core::operand::Operand]) {
417        let Ok(opcode) = usize::try_from(opcode) else {
418            self.last_error = Some(AsmError::InvalidInstruction);
419            return;
420        };
421        let Some(opcode) = ALL_OPCODES.get(opcode).copied() else {
422            self.last_error = Some(AsmError::InvalidInstruction);
423            return;
424        };
425        if !self
426            .environment()
427            .supports_any_riscv_feature(&OPCODE_FEATURE_MASKS[opcode as usize])
428        {
429            self.last_error = Some(AsmError::MissingCpuFeature {
430                feature: OPCODE_FEATURE_CONTEXT[opcode as usize],
431            });
432            return;
433        }
434        let signature = &SIGNATURE_TABLE[opcode.inst_info().signature_index as usize];
435        let expected_operands = signature
436            .iter()
437            .position(|operand_class| *operand_class == ANY)
438            .unwrap_or(signature.len());
439        if ops.len() != expected_operands {
440            self.last_error = Some(AsmError::InvalidOperand);
441            return;
442        }
443
444        // Reject instructions that have no encoding on the target XLEN (e.g.
445        // `ld` on rv32, or the rv32-only `slli.rv32` variants on rv64).
446        let xlen_bit = if self.is_32bit() { 1 } else { 2 };
447        if OPCODE_XLEN[opcode as usize] & xlen_bit == 0 {
448            self.last_error = Some(AsmError::InvalidInstruction);
449            return;
450        }
451
452        let encoding = opcode.encoding();
453        let is_prime_register = |id| (8..=15).contains(&id);
454
455        let mut inst = Inst::new(opcode).encode();
456        let mut label_use = None;
457
458        let isign3 = match ops {
459            [] => 0,
460            [op0] => op0.op_type() as u32,
461            [op0, op1] => op0.op_type() as u32 + ((op1.op_type() as u32) << 3),
462            [op0, op1, op2, ..] => {
463                op0.op_type() as u32 + ((op1.op_type() as u32) << 3) + ((op2.op_type() as u32) << 6)
464            }
465        };
466
467        let isign4 = match ops {
468            [] => 0,
469            [op0] => op0.op_type() as u32,
470            [op0, op1] => op0.op_type() as u32 + ((op1.op_type() as u32) << 3),
471            [op0, op1, op2] => {
472                op0.op_type() as u32 + ((op1.op_type() as u32) << 3) + ((op2.op_type() as u32) << 6)
473            }
474            [op0, op1, op2, op3, ..] => {
475                op0.op_type() as u32
476                    + ((op1.op_type() as u32) << 3)
477                    + ((op2.op_type() as u32) << 6)
478                    + ((op3.op_type() as u32) << 9)
479            }
480        };
481        let mut short = SHORT_OPCODE[opcode as usize];
482        match encoding {
483            Encoding::Bimm12HiRs1Bimm12lo => {
484                let rs1 = ops[0].id();
485                let imm = if ops[1].is_imm() {
486                    ops[1].as_::<Imm>().value() as i32
487                } else if ops[1].is_label() {
488                    label_use = Some((ops[1], LabelUse::RVB12));
489                    0
490                } else {
491                    self.last_error = Some(AsmError::InvalidOperand);
492                    return;
493                };
494
495                inst = inst.set_rs1(rs1).set_bimm12lohi(imm);
496            }
497
498            Encoding::Bimm12HiRs1Rs2Bimm12lo => {
499                let rs1 = ops[0].id();
500                let rs2 = ops[1].id();
501
502                let imm = if ops[2].is_imm() {
503                    ops[2].as_::<Imm>().value() as i32
504                } else if ops[2].is_label() {
505                    label_use = Some((ops[2], LabelUse::RVB12));
506                    0
507                } else {
508                    self.last_error = Some(AsmError::InvalidOperand);
509                    return;
510                };
511
512                inst = inst.set_rs1(rs1).set_rs2(rs2).set_bimm12lohi(imm);
513            }
514
515            Encoding::Bimm12HiRs2Rs1Bimm12lo => {
516                let rs1 = ops[0].id();
517                let rs2 = ops[1].id();
518                let imm = if ops[2].is_imm() {
519                    ops[2].as_::<Imm>().value() as i32
520                } else if ops[2].is_label() {
521                    label_use = Some((ops[2], LabelUse::RVB12));
522                    0
523                } else {
524                    self.last_error = Some(AsmError::InvalidOperand);
525                    return;
526                };
527
528                inst = inst.set_rs2(rs2).set_rs1(rs1).set_bimm12lohi(imm);
529            }
530
531            Encoding::Bimm12HiRs2Bimm12lo => {
532                let rs2 = ops[0].id();
533                let imm = if ops[1].is_imm() {
534                    ops[1].as_::<Imm>().value() as i32
535                } else if ops[1].is_label() {
536                    label_use = Some((ops[1], LabelUse::RVB12));
537                    0
538                } else {
539                    self.last_error = Some(AsmError::InvalidOperand);
540                    return;
541                };
542
543                inst = inst.set_rs2(rs2).set_bimm12lohi(imm);
544            }
545
546            Encoding::CImm12 => {
547                short = true;
548                let imm = if ops[0].is_imm() {
549                    ops[0].as_::<Imm>().value() as i32
550                } else if ops[0].is_label() {
551                    label_use = Some((ops[0], LabelUse::RVCJump));
552                    0
553                } else {
554                    self.last_error = Some(AsmError::InvalidOperand);
555                    return;
556                };
557
558                inst = inst.set_c_imm12(imm)
559            }
560
561            Encoding::CIndex => {
562                short = true;
563                let imm = if ops[0].is_imm() {
564                    ops[0].as_::<Imm>().value() as i32
565                } else {
566                    self.last_error = Some(AsmError::InvalidOperand);
567                    return;
568                };
569                inst = inst.set_c_index(imm as _);
570            }
571
572            Encoding::CMopT => {
573                short = true;
574                let imm = if ops[0].is_imm() {
575                    ops[0].as_::<Imm>().value() as i32
576                } else {
577                    self.last_error = Some(AsmError::InvalidOperand);
578                    return;
579                };
580
581                inst = inst.set_c_mop_t(imm as _);
582            }
583
584            Encoding::CNzimm10hiCNzimm10lo => {
585                short = true;
586                let imm = if ops[0].is_imm() {
587                    ops[0].as_::<Imm>().value() as i32
588                } else {
589                    self.last_error = Some(AsmError::InvalidOperand);
590                    return;
591                };
592
593                if imm == 0 || !(-1024..=1024).contains(&imm) {
594                    self.last_error = Some(AsmError::InvalidOperand);
595                    return;
596                }
597
598                inst = inst.set_c_nzimm10lohi(imm);
599            }
600
601            Encoding::CNzimm6hiCNzimm6lo => {
602                short = true;
603                let imm = if ops[0].is_imm() {
604                    ops[0].as_::<Imm>().value() as i32
605                } else {
606                    self.last_error = Some(AsmError::InvalidOperand);
607                    return;
608                };
609
610                if imm == 0 || imm > 64 {
611                    self.last_error = Some(AsmError::InvalidOperand);
612                    return;
613                }
614
615                inst = inst.set_c_nzimm6lohi(imm)
616            }
617
618            Encoding::CRlistCSpimm => {
619                self.last_error = Some(AsmError::UnsupportedInstruction {
620                    reason: "RISC-V compressed register-list instructions are not implemented",
621                });
622                return;
623            }
624
625            Encoding::CRs1N0 => {
626                short = true;
627                let rs1 = ops[0].id();
628                inst = inst.set_rs1_n0(rs1);
629            }
630
631            Encoding::CRs2CUimm8spS => {
632                short = true;
633                let rs2 = ops[0].id();
634                let imm = if ops[1].is_imm() {
635                    ops[1].as_::<Imm>().value() as i32
636                } else {
637                    self.last_error = Some(AsmError::InvalidOperand);
638                    return;
639                };
640                if !(0..=256).contains(&imm) {
641                    self.last_error = Some(AsmError::InvalidOperand);
642                    return;
643                }
644                inst = inst.set_c_uimm8lohi(imm as _).set_c_rs2(rs2);
645            }
646
647            Encoding::CRs2CUimm9spS => {
648                short = true;
649                let rs2 = ops[0].id();
650                let imm = if ops[1].is_imm() {
651                    ops[1].as_::<Imm>().value() as i32
652                } else {
653                    self.last_error = Some(AsmError::InvalidOperand);
654                    return;
655                };
656                if !(0..=511).contains(&imm) {
657                    self.last_error = Some(AsmError::InvalidOperand);
658                    return;
659                }
660                inst = inst.set_c_rs2(rs2).set_c_uimm9sp_s(imm as _);
661            }
662
663            Encoding::CSreg1CSreg2 => {
664                self.last_error = Some(AsmError::UnsupportedInstruction {
665                    reason: "RISC-V compressed saved-register moves are not implemented",
666                });
667                return;
668            }
669
670            Encoding::CsrZimm5 => {
671                let csr_imm = if ops[0].is_imm() {
672                    ops[0].as_::<Imm>().value() as i32
673                } else {
674                    self.last_error = Some(AsmError::InvalidOperand);
675                    return;
676                };
677
678                let zimm = if ops[1].is_imm() {
679                    ops[1].as_::<Imm>().value()
680                } else {
681                    self.last_error = Some(AsmError::InvalidOperand);
682                    return;
683                };
684
685                inst = inst.set_csr(csr_imm as _).set_zimm5(zimm as _);
686            }
687
688            Encoding::Empty => {}
689            Encoding::FmPredSuccRs1Rd => {
690                let fm = if ops[0].is_imm() {
691                    ops[0].as_::<Imm>().value() as u8
692                } else {
693                    self.last_error = Some(AsmError::InvalidOperand);
694                    return;
695                };
696
697                let pred = if ops[1].is_imm() {
698                    ops[1].as_::<Imm>().value() as u8
699                } else {
700                    self.last_error = Some(AsmError::InvalidOperand);
701                    return;
702                };
703
704                let succ = if ops[2].is_imm() {
705                    ops[2].as_::<Imm>().value() as u8
706                } else {
707                    self.last_error = Some(AsmError::InvalidOperand);
708                    return;
709                };
710
711                let rs1 = ops[3].id();
712                let rd = ops[4].id();
713
714                inst = inst
715                    .set_fm(fm as _)
716                    .set_pred(pred as _)
717                    .set_succ(succ as _)
718                    .set_rs1(rs1)
719                    .set_rd(rd);
720            }
721
722            Encoding::Imm12HiRs1Rs2Imm12lo => {
723                if isign3 == enc_ops3!(Reg, Reg, Imm) {
724                    let rs1 = ops[0].id();
725                    let rs2 = ops[1].id();
726                    let imm = ops[2].as_::<Imm>().value() as i32;
727
728                    inst = inst.set_rs1(rs1).set_rs2(rs2).set_imm12lohi(imm);
729                } else {
730                    self.last_error = Some(AsmError::InvalidOperand);
731                    return;
732                };
733            }
734
735            Encoding::Imm12Rs1Rd => {
736                if opcode == Opcode::FENCEI {
737                    // imm12, rs1, and rd are reserved and canonically zero.
738                } else if isign3 == enc_ops3!(Reg, Reg, Imm) {
739                    let rs1 = ops[0].id();
740                    let rd = ops[1].id();
741                    let imm = ops[2].as_::<Imm>().value() as i32;
742
743                    inst = inst.set_rs1(rs1).set_rd(rd).set_imm12(imm);
744                } else {
745                    self.last_error = Some(AsmError::InvalidOperand);
746                    return;
747                }
748            }
749
750            Encoding::Imm20 => {
751                if isign3 == enc_ops1!(Imm) {
752                    let imm = ops[0].as_::<Imm>().value() as i32;
753                    inst = inst.set_imm20(imm);
754                } else {
755                    self.last_error = Some(AsmError::InvalidOperand);
756                    return;
757                }
758            }
759
760            Encoding::Jimm20 => {
761                if isign3 == enc_ops1!(Imm) {
762                    let imm = ops[0].as_::<Imm>().value() as i32;
763                    inst = inst.set_jimm20(imm);
764                } else if isign3 == enc_ops1!(Label) {
765                    label_use = Some((ops[0], LabelUse::RVJal20));
766                    inst = inst.set_jimm20(0);
767                } else {
768                    self.last_error = Some(AsmError::InvalidOperand);
769                    return;
770                }
771            }
772
773            Encoding::MopRT30MopRT2726MopRT2120RdRs1 => {
774                self.last_error = Some(AsmError::UnsupportedInstruction {
775                    reason: "RISC-V MOP.RN instructions are not implemented",
776                });
777                return;
778            }
779            Encoding::MopRrT30MopRrT2726RdRs1Rs2 => {
780                self.last_error = Some(AsmError::UnsupportedInstruction {
781                    reason: "RISC-V MOP.RR.N instructions are not implemented",
782                });
783                return;
784            }
785
786            Encoding::NfVmRs1Vd => {
787                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
788                    let vd = ops[0].id();
789                    let rs1 = ops[1].id();
790                    let vm = ops[2].as_::<Imm>().value();
791                    let nf = ops[3].as_::<Imm>().value();
792                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
793                        self.last_error = Some(AsmError::InvalidOperand);
794                        return;
795                    }
796
797                    inst = inst.set_vd(vd).set_rs1(rs1).set_vm(vm as _).set_nf(nf as _);
798                } else {
799                    self.last_error = Some(AsmError::InvalidOperand);
800                    return;
801                }
802            }
803
804            Encoding::NfVmRs1Vs3 => {
805                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
806                    let vs3 = ops[0].id();
807                    let rs1 = ops[1].id();
808                    let vm = ops[2].as_::<Imm>().value();
809                    let nf = ops[3].as_::<Imm>().value();
810                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
811                        self.last_error = Some(AsmError::InvalidOperand);
812                        return;
813                    }
814
815                    inst = inst
816                        .set_vs3(vs3)
817                        .set_rs1(rs1)
818                        .set_vm(vm as _)
819                        .set_nf(nf as _);
820                } else {
821                    self.last_error = Some(AsmError::InvalidOperand);
822                    return;
823                }
824            }
825
826            Encoding::NfVmRs2Rs1Vd => {
827                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm)
828                    && ops.get(4).is_some_and(|op| op.is_imm())
829                {
830                    let vd = ops[0].id();
831                    let rs1 = ops[1].id();
832                    let vm = ops[3].as_::<Imm>().value();
833                    let nf = ops[4].as_::<Imm>().value();
834                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
835                        self.last_error = Some(AsmError::InvalidOperand);
836                        return;
837                    }
838                    let rs2 = ops[2].id();
839                    inst = inst
840                        .set_rs1(rs1)
841                        .set_rs2(rs2)
842                        .set_vd(vd)
843                        .set_vm(vm as _)
844                        .set_nf(nf as _);
845                } else {
846                    self.last_error = Some(AsmError::InvalidOperand);
847                    return;
848                }
849            }
850
851            Encoding::NfVmRs2Rs1Vs3 => {
852                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm)
853                    && ops.get(4).is_some_and(|op| op.is_imm())
854                {
855                    let vs3 = ops[0].id();
856                    let rs1 = ops[1].id();
857                    let vm = ops[3].as_::<Imm>().value();
858                    let nf = ops[4].as_::<Imm>().value();
859                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
860                        self.last_error = Some(AsmError::InvalidOperand);
861                        return;
862                    }
863                    let rs2 = ops[2].id();
864                    inst = inst
865                        .set_rs1(rs1)
866                        .set_rs2(rs2)
867                        .set_vs3(vs3)
868                        .set_vm(vm as _)
869                        .set_nf(nf as _);
870                } else {
871                    self.last_error = Some(AsmError::InvalidOperand);
872                    return;
873                }
874            }
875
876            Encoding::NfVmVs2Rs1Vd => {
877                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm)
878                    && ops.get(4).is_some_and(|op| op.is_imm())
879                {
880                    let vd = ops[0].id();
881                    let rs1 = ops[1].id();
882                    let vs2 = ops[2].id();
883                    let vm = ops[3].as_::<Imm>().value();
884                    let nf = ops[4].as_::<Imm>().value();
885                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
886                        self.last_error = Some(AsmError::InvalidOperand);
887                        return;
888                    }
889
890                    inst = inst
891                        .set_rs1(rs1)
892                        .set_vd(vd)
893                        .set_vs2(vs2)
894                        .set_vm(vm as _)
895                        .set_nf(nf as _);
896                } else {
897                    self.last_error = Some(AsmError::InvalidOperand);
898                    return;
899                }
900            }
901
902            Encoding::NfVmVs2Rs1Vs3 => {
903                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm)
904                    && ops.get(4).is_some_and(|op| op.is_imm())
905                {
906                    let vs3 = ops[0].id();
907                    let rs1 = ops[1].id();
908                    let vs2 = ops[2].id();
909                    let vm = ops[3].as_::<Imm>().value();
910                    let nf = ops[4].as_::<Imm>().value();
911                    if !(0..=1).contains(&vm) || !(0..=7).contains(&nf) {
912                        self.last_error = Some(AsmError::InvalidOperand);
913                        return;
914                    }
915
916                    inst = inst
917                        .set_vs3(vs3)
918                        .set_rs1(rs1)
919                        .set_vs2(vs2)
920                        .set_vm(vm as _)
921                        .set_nf(nf as _);
922                } else {
923                    self.last_error = Some(AsmError::InvalidOperand);
924                    return;
925                }
926            }
927
928            Encoding::Rd => {
929                if isign3 == enc_ops1!(Reg) {
930                    let rd = ops[0].id();
931                    inst = inst.set_rd(rd);
932                } else {
933                    self.last_error = Some(AsmError::InvalidOperand);
934                    return;
935                }
936            }
937
938            Encoding::RdCUimm8sphiCUimm8splo => {
939                if isign3 == enc_ops2!(Reg, Imm) {
940                    let rd = ops[0].id();
941                    let imm = ops[1].as_::<Imm>().value() as u32;
942
943                    inst = inst.set_rd(rd).set_c_uimm8splohi(imm);
944                } else {
945                    self.last_error = Some(AsmError::InvalidOperand);
946                    return;
947                }
948            }
949
950            Encoding::RdCUimm9sphiCUimm9splo => {
951                if isign3 == enc_ops2!(Reg, Imm) {
952                    let rd = ops[0].id();
953                    let imm = ops[1].as_::<Imm>().value() as u32;
954
955                    inst = inst.set_rd(rd).set_c_uimm9splohi(imm);
956                } else {
957                    self.last_error = Some(AsmError::InvalidOperand);
958                    return;
959                }
960            }
961
962            Encoding::RdCsr => {
963                if isign3 == enc_ops2!(Reg, Imm) {
964                    let rd = ops[0].id();
965                    let csr = ops[1].as_::<Imm>().value() as u32;
966                    inst = inst.set_rd(rd).set_csr(csr);
967                } else {
968                    self.last_error = Some(AsmError::InvalidOperand);
969                    return;
970                }
971            }
972
973            Encoding::RdCsrZimm5 => {
974                if isign3 == enc_ops3!(Reg, Imm, Imm) {
975                    let rd = ops[0].id();
976                    let csr = ops[1].as_::<Imm>().value() as u32;
977                    let zimm = ops[2].as_::<Imm>().value() as i32;
978                    inst = inst.set_rd(rd).set_csr(csr).set_zimm5(zimm);
979                } else {
980                    self.last_error = Some(AsmError::InvalidOperand);
981                    return;
982                }
983            }
984            Encoding::RdImm20 => {
985                if isign3 == enc_ops2!(Reg, Imm) {
986                    let rd = ops[0].id();
987                    let imm = ops[1].as_::<Imm>().value() as i32;
988                    inst = inst.set_rd(rd).set_imm20(imm);
989                } else if isign3 == enc_ops2!(Reg, Label) {
990                    let rd = ops[0].id();
991                    label_use = Some((ops[1], LabelUse::RVPCRelHi20));
992                    inst = inst.set_rd(rd).set_imm20(0);
993                } else {
994                    self.last_error = Some(AsmError::InvalidOperand);
995                    return;
996                };
997            }
998
999            Encoding::RdJimm20 => {
1000                if isign3 == enc_ops2!(Reg, Imm) {
1001                    let rd = ops[0].id();
1002                    let imm = ops[1].as_::<Imm>().value() as i32;
1003                    inst = inst.set_rd(rd).set_jimm20(imm);
1004                } else if isign3 == enc_ops2!(Reg, Label) {
1005                    let rd = ops[0].id();
1006                    label_use = Some((ops[1], LabelUse::RVJal20));
1007                    inst = inst.set_rd(rd).set_jimm20(0);
1008                } else {
1009                    self.last_error = Some(AsmError::InvalidOperand);
1010                    return;
1011                };
1012            }
1013
1014            Encoding::RdN0 => {
1015                if isign3 == enc_ops1!(Reg) {
1016                    let rd = ops[0].id();
1017                    inst = inst.set_rd_n0(rd);
1018                } else {
1019                    self.last_error = Some(AsmError::InvalidOperand);
1020                    return;
1021                }
1022            }
1023
1024            Encoding::RdN0CImm6loCImm6hi => {
1025                short = true;
1026                if isign3 == enc_ops2!(Reg, Imm) {
1027                    let rd = ops[0].id();
1028                    let imm = ops[1].as_::<Imm>().value() as i32;
1029                    inst = inst.set_rd_n0(rd).set_c_imm6lohi(imm);
1030                } else {
1031                    self.last_error = Some(AsmError::InvalidOperand);
1032                    return;
1033                }
1034            }
1035
1036            Encoding::RdN0CRs2N0 => {
1037                short = true;
1038                if isign3 == enc_ops2!(Reg, Reg) {
1039                    let rd = ops[0].id();
1040                    let rs1 = ops[1].id();
1041                    inst = inst.set_rd_n0(rd).set_c_rs2(rs1);
1042                } else {
1043                    self.last_error = Some(AsmError::InvalidOperand);
1044                    return;
1045                }
1046            }
1047
1048            Encoding::RdN0CUimm8sphiCUimm8splo => {
1049                short = true;
1050                if isign3 == enc_ops2!(Reg, Imm) {
1051                    let rd = ops[0].id();
1052                    let imm = ops[1].as_::<Imm>().value() as i32;
1053                    inst = inst.set_rd_n0(rd).set_c_uimm8splohi(imm as _);
1054                } else {
1055                    self.last_error = Some(AsmError::InvalidOperand);
1056                    return;
1057                }
1058            }
1059
1060            Encoding::RdN0CUimm9sphiCUimm9splo => {
1061                short = true;
1062                if isign3 == enc_ops2!(Reg, Imm) {
1063                    let rd = ops[0].id();
1064                    let imm = ops[1].as_::<Imm>().value() as i32;
1065                    inst = inst.set_rd_n0(rd).set_c_uimm9splohi(imm as _);
1066                } else {
1067                    self.last_error = Some(AsmError::InvalidOperand);
1068                    return;
1069                }
1070            }
1071
1072            Encoding::RdN2CNzimm18hiCNzimm18lo => {
1073                short = true;
1074                if isign3 == enc_ops2!(Reg, Imm) {
1075                    let rd = ops[0].id();
1076                    let imm = ops[1].as_::<Imm>().value() as i32;
1077                    if imm == 0 {
1078                        self.last_error = Some(AsmError::InvalidOperand);
1079                        return;
1080                    } else {
1081                        inst = inst.set_rd_n2(rd).set_c_nzimm18lohi(imm);
1082                    }
1083                } else {
1084                    self.last_error = Some(AsmError::InvalidOperand);
1085                    return;
1086                }
1087            }
1088
1089            Encoding::RdPCNzuimm10 => {
1090                if isign3 == enc_ops2!(Reg, Imm) {
1091                    let rd = ops[0].id();
1092                    if !is_prime_register(rd) {
1093                        self.last_error = Some(AsmError::InvalidOperand);
1094                        return;
1095                    }
1096                    let imm = ops[1].as_::<Imm>().value() as i32;
1097                    inst = inst.set_rd_p(rd).set_c_nzimm10lohi(imm);
1098                } else {
1099                    self.last_error = Some(AsmError::InvalidOperand);
1100                    return;
1101                }
1102            }
1103
1104            Encoding::RdPRs1PCUimm1 => {
1105                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1106                    let rd = ops[0].id();
1107                    let rs1 = ops[1].id();
1108                    if !is_prime_register(rd) || !is_prime_register(rs1) {
1109                        self.last_error = Some(AsmError::InvalidOperand);
1110                        return;
1111                    }
1112                    let imm = ops[2].as_::<Imm>().value() as i32;
1113
1114                    inst = inst.set_rd_p(rd).set_rs1_p(rs1).set_c_uimm1(imm as _);
1115                } else {
1116                    self.last_error = Some(AsmError::InvalidOperand);
1117                    return;
1118                }
1119            }
1120
1121            Encoding::RdPRs1PCUimm2 => {
1122                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1123                    let rd = ops[0].id();
1124                    let rs1 = ops[1].id();
1125                    if !is_prime_register(rd) || !is_prime_register(rs1) {
1126                        self.last_error = Some(AsmError::InvalidOperand);
1127                        return;
1128                    }
1129                    let imm = ops[2].as_::<Imm>().value() as i32;
1130
1131                    inst = inst.set_rd_p(rd).set_rs1_p(rs1).set_c_uimm2(imm as _);
1132                } else {
1133                    self.last_error = Some(AsmError::InvalidOperand);
1134                    return;
1135                }
1136            }
1137
1138            Encoding::RdPRs1PCUimm7loCUimm7hi => {
1139                short = true;
1140                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1141                    let rd = ops[0].id();
1142                    let rs1 = ops[1].id();
1143                    if !is_prime_register(rd) || !is_prime_register(rs1) {
1144                        self.last_error = Some(AsmError::InvalidOperand);
1145                        return;
1146                    }
1147                    let imm = ops[2].as_::<Imm>().value() as i32;
1148                    inst = inst.set_rd_p(rd).set_rs1_p(rs1).set_c_uimm7lohi(imm as _);
1149                } else {
1150                    self.last_error = Some(AsmError::InvalidOperand);
1151                    return;
1152                }
1153            }
1154
1155            Encoding::RdPRs1PCUimm8loCUimm8hi => {
1156                short = true;
1157                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1158                    let rd = ops[0].id();
1159                    let rs1 = ops[1].id();
1160                    if !is_prime_register(rd) || !is_prime_register(rs1) {
1161                        self.last_error = Some(AsmError::InvalidOperand);
1162                        return;
1163                    }
1164                    let imm = ops[2].as_::<Imm>().value() as i32;
1165                    inst = inst.set_rd_p(rd).set_rs1_p(rs1).set_c_uimm8lohi(imm as _);
1166                } else {
1167                    self.last_error = Some(AsmError::InvalidOperand);
1168                    return;
1169                }
1170            }
1171
1172            Encoding::RdRs1 => {
1173                if isign3 == enc_ops2!(Reg, Reg) {
1174                    let rd = ops[0].id();
1175                    let rs1 = ops[1].id();
1176                    inst = inst.set_rd(rd).set_rs1(rs1);
1177                } else {
1178                    self.last_error = Some(AsmError::InvalidOperand);
1179                    return;
1180                }
1181            }
1182
1183            Encoding::RdRs1AqRl => {
1184                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
1185                    let rd = ops[0].id();
1186                    let rs1 = ops[1].id();
1187                    let aq = ops[2].as_::<Imm>().value();
1188                    let rl = ops[3].as_::<Imm>().value();
1189                    if !(0..=1).contains(&aq) || !(0..=1).contains(&rl) {
1190                        self.last_error = Some(AsmError::InvalidOperand);
1191                        return;
1192                    }
1193                    inst = inst.set_rd(rd).set_rs1(rs1).set_aq(aq as _).set_rl(rl as _);
1194                } else {
1195                    self.last_error = Some(AsmError::InvalidOperand);
1196                    return;
1197                }
1198            }
1199
1200            Encoding::RdRs1Csr => {
1201                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1202                    let rd = ops[0].id();
1203                    let rs1 = ops[1].id();
1204                    let imm = ops[2].as_::<Imm>().value() as i32;
1205                    inst = inst.set_rd(rd).set_rs1(rs1).set_csr(imm as _);
1206                } else {
1207                    self.last_error = Some(AsmError::InvalidOperand);
1208                    return;
1209                }
1210            }
1211
1212            Encoding::RdRs1Imm12 => {
1213                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1214                    let rd = ops[0].id();
1215                    let rs1 = ops[1].id();
1216                    let imm = ops[2].as_::<Imm>().value() as i32;
1217                    inst = inst.set_rd(rd).set_rs1(rs1).set_imm12(imm);
1218                } else if isign3 == enc_ops3!(Reg, Reg, Label) {
1219                    let rd = ops[0].id();
1220                    let rs1 = ops[1].id();
1221                    if rd != rs1 {
1222                        self.last_error = Some(AsmError::InvalidOperand);
1223                        return;
1224                    }
1225                    let off = self.buffer.cur_offset();
1226                    self.buffer
1227                        .use_label_at_offset(off, ops[2].as_(), LabelUse::RVPCRelHi20);
1228                    self.auipc(ops[0].as_::<Gp>(), imm(0));
1229                    label_use = Some((ops[2], LabelUse::RVPCRelLo12I));
1230                    inst = inst.set_rd(rd).set_rs1(rs1).set_imm12(0);
1231                } else {
1232                    self.last_error = Some(AsmError::InvalidOperand);
1233                    return;
1234                }
1235            }
1236
1237            Encoding::RdRs1N0 => {
1238                self.last_error = Some(AsmError::UnsupportedInstruction {
1239                    reason: "RISC-V RdRs1N0 instructions are not implemented",
1240                });
1241                return;
1242            }
1243
1244            Encoding::RdRs1Rm => {
1245                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1246                    let rd = ops[0].id();
1247                    let rs1 = ops[1].id();
1248                    let rm = ops[2].as_::<Imm>().value() as i32;
1249                    if !matches!(rm, 0..=4 | 7) {
1250                        self.last_error = Some(AsmError::InvalidOperand);
1251                        return;
1252                    }
1253                    inst = inst.set_rd(rd).set_rs1(rs1).set_rm(rm as _);
1254                } else {
1255                    self.last_error = Some(AsmError::InvalidOperand);
1256                    return;
1257                }
1258            }
1259            Encoding::RdRs1Rnum => {
1260                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1261                    let rd = ops[0].id();
1262                    let rs1 = ops[1].id();
1263                    let rm = ops[2].as_::<Imm>().value() as i32;
1264                    inst = inst.set_rd(rd).set_rs1(rs1).set_rnum(rm as _);
1265                } else {
1266                    self.last_error = Some(AsmError::InvalidOperand);
1267                    return;
1268                }
1269            }
1270
1271            Encoding::RdRs1Rs2 => {
1272                if isign3 == enc_ops3!(Reg, Reg, Reg) {
1273                    let rd = ops[0].id();
1274                    let rs1 = ops[1].id();
1275                    let rs2 = ops[2].id();
1276                    inst = inst.set_rd(rd).set_rs1(rs1).set_rs2(rs2);
1277                } else {
1278                    self.last_error = Some(AsmError::InvalidOperand);
1279                    return;
1280                }
1281            }
1282
1283            Encoding::RdRs1Rs2AqRl => {
1284                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm)
1285                    && ops.get(4).is_some_and(|op| op.is_imm())
1286                {
1287                    let rd = ops[0].id();
1288                    let rs1 = ops[1].id();
1289                    let rs2 = ops[2].id();
1290                    let aq = ops[3].as_::<Imm>().value();
1291                    let rl = ops[4].as_::<Imm>().value();
1292                    if !(0..=1).contains(&aq) || !(0..=1).contains(&rl) {
1293                        self.last_error = Some(AsmError::InvalidOperand);
1294                        return;
1295                    }
1296                    inst = inst
1297                        .set_rd(rd)
1298                        .set_rs1(rs1)
1299                        .set_rs2(rs2)
1300                        .set_aq(aq as _)
1301                        .set_rl(rl as _);
1302                } else {
1303                    self.last_error = Some(AsmError::InvalidOperand);
1304                    return;
1305                }
1306            }
1307
1308            Encoding::RdRs1Rs2Bs => {
1309                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm) {
1310                    let rd = ops[0].id();
1311                    let rs1 = ops[1].id();
1312                    let rs2 = ops[2].id();
1313                    let imm = ops[3].as_::<Imm>().value() as i32;
1314                    inst = inst.set_rd(rd).set_rs1(rs1).set_rs2(rs2).set_bs(imm as _);
1315                } else {
1316                    self.last_error = Some(AsmError::InvalidOperand);
1317                    return;
1318                }
1319            }
1320
1321            Encoding::RdRs1Rs2EqRs1 => {
1322                if isign3 == enc_ops3!(Reg, Reg, Reg) {
1323                    let rd = ops[0].id();
1324                    let rs1 = ops[1].id();
1325                    let rs2 = ops[2].id();
1326                    inst = inst.set_rd(rd).set_rs1(rs1).set_rs2_eq_rs1(rs2);
1327                } else {
1328                    self.last_error = Some(AsmError::InvalidOperand);
1329                    return;
1330                }
1331            }
1332
1333            Encoding::RdRs1Rs2Rm => {
1334                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm) {
1335                    let rd = ops[0].id();
1336                    let rs1 = ops[1].id();
1337                    let rs2 = ops[2].id();
1338                    let rm = ops[3].as_::<Imm>().value() as i32;
1339                    if !matches!(rm, 0..=4 | 7) {
1340                        self.last_error = Some(AsmError::InvalidOperand);
1341                        return;
1342                    }
1343                    inst = inst.set_rd(rd).set_rs1(rs1).set_rs2(rs2).set_rm(rm as _);
1344                } else {
1345                    self.last_error = Some(AsmError::InvalidOperand);
1346                    return;
1347                }
1348            }
1349
1350            Encoding::RdRs1Rs2Rs3Rm => {
1351                if isign4 == enc_ops4!(Reg, Reg, Reg, Reg) && ops[4].op_type() == OperandType::Imm {
1352                    let rd = ops[0].id();
1353                    let rs1 = ops[1].id();
1354                    let rs2 = ops[2].id();
1355                    let rs3 = ops[3].id();
1356                    let rm = ops[4].as_::<Imm>().value() as i32;
1357                    if !matches!(rm, 0..=4 | 7) {
1358                        self.last_error = Some(AsmError::InvalidOperand);
1359                        return;
1360                    }
1361
1362                    inst = inst
1363                        .set_rd(rd)
1364                        .set_rs1(rs1)
1365                        .set_rs2(rs2)
1366                        .set_rs3(rs3)
1367                        .set_rm(rm as _);
1368                } else {
1369                    self.last_error = Some(AsmError::InvalidOperand);
1370                    return;
1371                }
1372            }
1373
1374            Encoding::RdRs1Shamtw => {
1375                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1376                    let rd = ops[0].id();
1377                    let rs1 = ops[1].id();
1378                    let shamt = ops[2].as_::<Imm>().value() as i32;
1379                    inst = inst.set_rd(rd).set_rs1(rs1).set_shamtw(shamt as _);
1380                } else {
1381                    self.last_error = Some(AsmError::InvalidOperand);
1382                    return;
1383                }
1384            }
1385            Encoding::RdRs2 => {
1386                if isign3 == enc_ops2!(Reg, Reg) {
1387                    let rd = ops[0].id();
1388                    let rs2 = ops[1].id();
1389                    inst = inst.set_rd(rd).set_rs2(rs2);
1390                } else {
1391                    self.last_error = Some(AsmError::InvalidOperand);
1392                    return;
1393                }
1394            }
1395
1396            Encoding::RdZimm5 => {
1397                if isign3 == enc_ops2!(Reg, Imm) {
1398                    let rd = ops[0].id();
1399                    let imm = ops[1].as_::<Imm>().value() as i32;
1400                    inst = inst.set_rd(rd).set_zimm5(imm);
1401                } else {
1402                    self.last_error = Some(AsmError::InvalidOperand);
1403                    return;
1404                }
1405            }
1406
1407            Encoding::Rs1 => {
1408                if isign3 == enc_ops1!(Reg) {
1409                    let rs1 = ops[0].id();
1410                    inst = inst.set_rs1(rs1);
1411                } else {
1412                    self.last_error = Some(AsmError::InvalidOperand);
1413                    return;
1414                }
1415            }
1416
1417            Encoding::Rs1Csr => {
1418                if isign3 == enc_ops2!(Reg, Imm) {
1419                    let rs1 = ops[0].id();
1420                    let csr = ops[1].as_::<Imm>().value() as i32;
1421                    inst = inst.set_rs1(rs1).set_csr(csr as _);
1422                } else {
1423                    self.last_error = Some(AsmError::InvalidOperand);
1424                    return;
1425                }
1426            }
1427
1428            Encoding::Rs1Imm12hi => {
1429                if isign3 == enc_ops2!(Reg, Imm) {
1430                    let rs1 = ops[0].id();
1431                    let imm = ops[1].as_::<Imm>().value() as i32;
1432                    inst = inst.set_rs1(rs1).set_imm12hi_raw(imm as _);
1433                } else {
1434                    self.last_error = Some(AsmError::InvalidOperand);
1435                    return;
1436                }
1437            }
1438
1439            Encoding::Rs1N0 => {
1440                short = true;
1441                if isign3 == enc_ops1!(Reg) {
1442                    let rs1 = ops[0].id();
1443                    inst = inst.set_rs1_n0(rs1);
1444                } else {
1445                    self.last_error = Some(AsmError::InvalidOperand);
1446                    return;
1447                }
1448            }
1449
1450            Encoding::Rs1PCBimm9loCBimm9hi => {
1451                short = true;
1452                if isign3 == enc_ops2!(Reg, Imm) {
1453                    let rs1 = ops[0].id();
1454                    let imm = ops[1].as_::<Imm>().value();
1455
1456                    inst = inst.set_rs1(rs1).set_c_bimm9lohi(imm as _);
1457                } else if isign3 == enc_ops2!(Reg, Label) {
1458                    let rs1 = ops[0].id();
1459                    label_use = Some((ops[1], LabelUse::RVCB9));
1460                    inst = inst.set_rs1(rs1).set_c_bimm9lohi(0);
1461                } else {
1462                    self.last_error = Some(AsmError::InvalidOperand);
1463                    return;
1464                }
1465            }
1466
1467            Encoding::Rs1PRs2PCUimm7loCUimm7hi => {
1468                short = true;
1469                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1470                    let rs1 = ops[0].id();
1471                    let rs2 = ops[1].id();
1472                    if !is_prime_register(rs1) || !is_prime_register(rs2) {
1473                        self.last_error = Some(AsmError::InvalidOperand);
1474                        return;
1475                    }
1476                    let imm = ops[2].as_::<Imm>().value() as i32;
1477                    inst = inst.set_rs1_p(rs1).set_rs2_p(rs2).set_c_uimm7lohi(imm as _);
1478                } else {
1479                    self.last_error = Some(AsmError::InvalidOperand);
1480                    return;
1481                }
1482            }
1483
1484            Encoding::Rs1PRs2PCUimm8loCUimm8hi => {
1485                short = true;
1486                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1487                    let rs1 = ops[0].id();
1488                    let rs2 = ops[1].id();
1489                    if !is_prime_register(rs1) || !is_prime_register(rs2) {
1490                        self.last_error = Some(AsmError::InvalidOperand);
1491                        return;
1492                    }
1493                    let imm = ops[2].as_::<Imm>().value() as i32;
1494                    inst = inst.set_rs1_p(rs1).set_rs2_p(rs2).set_c_uimm8lohi(imm as _);
1495                } else {
1496                    self.last_error = Some(AsmError::InvalidOperand);
1497                    return;
1498                }
1499            }
1500
1501            Encoding::Rs1PRs2PCUimm8hiCUimm8lo => {
1502                short = true;
1503                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1504                    let rs1 = ops[0].id();
1505                    let rs2 = ops[1].id();
1506                    if !is_prime_register(rs1) || !is_prime_register(rs2) {
1507                        self.last_error = Some(AsmError::InvalidOperand);
1508                        return;
1509                    }
1510                    let imm = ops[2].as_::<Imm>().value() as i32;
1511                    inst = inst.set_rs1_p(rs1).set_rs2_p(rs2).set_c_uimm8lohi(imm as _);
1512                } else {
1513                    self.last_error = Some(AsmError::InvalidOperand);
1514                    return;
1515                }
1516            }
1517
1518            Encoding::Rs1Rd => {
1519                if opcode == Opcode::FENCETSO {
1520                    // rs1 and rd are unused and canonically zero.
1521                } else if isign3 == enc_ops2!(Reg, Reg) {
1522                    let rd = ops[0].id();
1523                    let rs1 = ops[1].id();
1524
1525                    inst = inst.set_rd(rd).set_rs1(rs1);
1526                } else {
1527                    self.last_error = Some(AsmError::InvalidOperand);
1528                    return;
1529                }
1530            }
1531
1532            Encoding::Rs1Rs2 => {
1533                if isign3 == enc_ops2!(Reg, Reg) {
1534                    let rs1 = ops[0].id();
1535                    let rs2 = ops[1].id();
1536                    inst = inst.set_rs1(rs1).set_rs2(rs2);
1537                } else {
1538                    self.last_error = Some(AsmError::InvalidOperand);
1539                    return;
1540                }
1541            }
1542
1543            Encoding::Rs1Vd => {
1544                if isign3 == enc_ops2!(Reg, Reg) {
1545                    let rs1 = ops[1].id();
1546                    let vd = ops[0].id();
1547                    inst = inst.set_vd(vd).set_rs1(rs1);
1548                } else {
1549                    self.last_error = Some(AsmError::InvalidOperand);
1550                    return;
1551                }
1552            }
1553            Encoding::Rs1Vs3 => {
1554                if isign3 == enc_ops2!(Reg, Reg) {
1555                    let rs1 = ops[1].id();
1556                    let vs3 = ops[0].id();
1557                    inst = inst.set_rs1(rs1).set_vs3(vs3);
1558                } else {
1559                    self.last_error = Some(AsmError::InvalidOperand);
1560                    return;
1561                }
1562            }
1563
1564            Encoding::Rs2PRs1PCUimm1 => {
1565                short = true;
1566                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1567                    let rs1 = ops[0].id();
1568                    let rs2 = ops[1].id();
1569                    if !is_prime_register(rs1) || !is_prime_register(rs2) {
1570                        self.last_error = Some(AsmError::InvalidOperand);
1571                        return;
1572                    }
1573                    let imm = ops[2].as_::<Imm>().value() as i32;
1574                    inst = inst.set_rs1_p(rs1).set_rs2_p(rs2).set_c_uimm1(imm as _);
1575                } else {
1576                    self.last_error = Some(AsmError::InvalidOperand);
1577                    return;
1578                }
1579            }
1580
1581            Encoding::Rs2PRs1PCUimm2 => {
1582                short = true;
1583                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1584                    let rs1 = ops[0].id();
1585                    let rs2 = ops[1].id();
1586                    if !is_prime_register(rs1) || !is_prime_register(rs2) {
1587                        self.last_error = Some(AsmError::InvalidOperand);
1588                        return;
1589                    }
1590                    let imm = ops[2].as_::<Imm>().value() as i32;
1591                    inst = inst.set_rs1_p(rs1).set_rs2_p(rs2).set_c_uimm2(imm as _);
1592                } else {
1593                    self.last_error = Some(AsmError::InvalidOperand);
1594                    return;
1595                }
1596            }
1597            Encoding::Rs2Rs1Rd => {
1598                if isign3 == enc_ops3!(Reg, Reg, Reg) {
1599                    let rd = ops[0].id();
1600                    let rs1 = ops[1].id();
1601                    let rs2 = ops[2].id();
1602
1603                    inst = inst.set_rd(rd).set_rs1(rs1).set_rs2(rs2);
1604                } else {
1605                    self.last_error = Some(AsmError::InvalidOperand);
1606                    return;
1607                }
1608            }
1609
1610            Encoding::Simm5Vd => {
1611                if isign3 == enc_ops2!(Reg, Imm) {
1612                    let vd = ops[0].id();
1613                    let imm = ops[1].as_::<Imm>().value() as i8;
1614                    inst = inst.set_vd(vd).set_simm5(imm as _);
1615                } else {
1616                    self.last_error = Some(AsmError::InvalidOperand);
1617                    return;
1618                }
1619            }
1620
1621            Encoding::VmVs2Rd => {
1622                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1623                    let rd = ops[0].id();
1624                    let vs2 = ops[1].id();
1625                    let vm = ops[2].as_::<Imm>().value();
1626                    if !(0..=1).contains(&vm) {
1627                        self.last_error = Some(AsmError::InvalidOperand);
1628                        return;
1629                    }
1630                    inst = inst.set_rd(rd).set_vs2(vs2).set_vm(vm as _);
1631                } else {
1632                    self.last_error = Some(AsmError::InvalidOperand);
1633                    return;
1634                }
1635            }
1636
1637            Encoding::VmVd => {
1638                if isign3 == enc_ops2!(Reg, Imm) {
1639                    let vd = ops[0].id();
1640                    let vm = ops[1].as_::<Imm>().value();
1641                    if !(0..=1).contains(&vm) {
1642                        self.last_error = Some(AsmError::InvalidOperand);
1643                        return;
1644                    }
1645                    inst = inst.set_vd(vd).set_vm(vm as _);
1646                } else {
1647                    self.last_error = Some(AsmError::InvalidOperand);
1648                    return;
1649                }
1650            }
1651
1652            Encoding::VmVs2Rs1Vd => {
1653                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm) {
1654                    let rs1 = ops[2].id();
1655                    let vs2 = ops[1].id();
1656                    let vm = ops[3].as_::<Imm>().value();
1657                    if !(0..=1).contains(&vm) {
1658                        self.last_error = Some(AsmError::InvalidOperand);
1659                        return;
1660                    }
1661                    let vd = ops[0].id();
1662                    inst = inst.set_vd(vd).set_vm(vm as _).set_rs1(rs1).set_vs2(vs2);
1663                } else {
1664                    self.last_error = Some(AsmError::InvalidOperand);
1665                    return;
1666                }
1667            }
1668
1669            Encoding::VmVs2Simm5Vd => {
1670                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
1671                    let simm5 = ops[2].as_::<Imm>().value() as i32;
1672                    let vs2 = ops[1].id();
1673                    let vm = ops[3].as_::<Imm>().value();
1674                    if !(0..=1).contains(&vm) {
1675                        self.last_error = Some(AsmError::InvalidOperand);
1676                        return;
1677                    }
1678                    let vd = ops[0].id();
1679                    inst = inst
1680                        .set_vd(vd)
1681                        .set_vm(vm as _)
1682                        .set_simm5(simm5)
1683                        .set_vs2(vs2);
1684                } else {
1685                    self.last_error = Some(AsmError::InvalidOperand);
1686                    return;
1687                }
1688            }
1689
1690            Encoding::VmVs2Vd => {
1691                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1692                    let vd = ops[0].id();
1693                    let vs2 = ops[1].id();
1694                    let vm = ops[2].as_::<Imm>().value();
1695                    if !(0..=1).contains(&vm) {
1696                        self.last_error = Some(AsmError::InvalidOperand);
1697                        return;
1698                    }
1699
1700                    inst = inst.set_vd(vd).set_vs2(vs2).set_vm(vm as _);
1701                } else {
1702                    self.last_error = Some(AsmError::InvalidOperand);
1703                    return;
1704                }
1705            }
1706
1707            Encoding::VmVs2Vs1Vd => {
1708                if isign4 == enc_ops4!(Reg, Reg, Reg, Imm) {
1709                    let vd = ops[0].id();
1710                    let vs1 = ops[1].id();
1711                    let vs2 = ops[2].id();
1712                    let vm = ops[3].as_::<Imm>().value();
1713                    if !(0..=1).contains(&vm) {
1714                        self.last_error = Some(AsmError::InvalidOperand);
1715                        return;
1716                    }
1717                    inst = inst.set_vd(vd).set_vs1(vs1).set_vs2(vs2).set_vm(vm as _);
1718                } else {
1719                    self.last_error = Some(AsmError::InvalidOperand);
1720                    return;
1721                }
1722            }
1723
1724            Encoding::VmVs2Zimm5Vd => {
1725                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
1726                    let vd = ops[0].id();
1727                    let vs2 = ops[1].id();
1728                    let vm = ops[3].as_::<Imm>().value();
1729                    if !(0..=1).contains(&vm) {
1730                        self.last_error = Some(AsmError::InvalidOperand);
1731                        return;
1732                    }
1733                    let zimm5 = ops[2].as_::<Imm>().value() as i8;
1734                    inst = inst
1735                        .set_vd(vd)
1736                        .set_vs2(vs2)
1737                        .set_vm(vm as _)
1738                        .set_zimm5(zimm5 as _);
1739                } else {
1740                    self.last_error = Some(AsmError::InvalidOperand);
1741                    return;
1742                }
1743            }
1744
1745            Encoding::Vs1Vd => {
1746                if isign3 == enc_ops2!(Reg, Reg) {
1747                    let vd = ops[0].id();
1748                    let vs1 = ops[1].id();
1749                    inst = inst.set_vd(vd).set_vs1(vs1);
1750                } else {
1751                    self.last_error = Some(AsmError::InvalidOperand);
1752                    return;
1753                }
1754            }
1755
1756            Encoding::Vs2Rd => {
1757                if isign3 == enc_ops2!(Reg, Reg) {
1758                    let rd = ops[0].id();
1759                    let vs2 = ops[1].id();
1760                    inst = inst.set_rd(rd).set_vs2(vs2);
1761                } else {
1762                    self.last_error = Some(AsmError::InvalidOperand);
1763                    return;
1764                }
1765            }
1766
1767            Encoding::Vs2Rs1Vd => {
1768                if isign4 == enc_ops3!(Reg, Reg, Reg) {
1769                    let rs1 = ops[1].id();
1770                    let vs2 = ops[2].id();
1771                    let vd = ops[0].id();
1772                    inst = inst.set_vd(vd).set_vs2(vs2).set_rs1(rs1);
1773                } else {
1774                    self.last_error = Some(AsmError::InvalidOperand);
1775                    return;
1776                }
1777            }
1778
1779            Encoding::Vs2Simm5Vd => {
1780                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1781                    let vd = ops[0].id();
1782                    let vs2 = ops[1].id();
1783                    let imm = ops[2].as_::<Imm>().value();
1784
1785                    inst = inst.set_vd(vd).set_vs2(vs2).set_simm5(imm as _);
1786                } else {
1787                    self.last_error = Some(AsmError::InvalidOperand);
1788                    return;
1789                }
1790            }
1791
1792            Encoding::Vs2Vd => {
1793                if isign3 == enc_ops2!(Reg, Reg) {
1794                    let vd = ops[0].id();
1795                    let vs2 = ops[1].id();
1796                    inst = inst.set_vd(vd).set_vs2(vs2);
1797                } else {
1798                    self.last_error = Some(AsmError::InvalidOperand);
1799                    return;
1800                }
1801            }
1802
1803            Encoding::Vs2Vs1Vd => {
1804                if isign4 == enc_ops3!(Reg, Reg, Reg) {
1805                    let vd = ops[0].id();
1806                    let vs1 = ops[1].id();
1807                    let vs2 = ops[2].id();
1808
1809                    inst = inst.set_vd(vd).set_vs1(vs1).set_vs2(vs2);
1810                } else {
1811                    self.last_error = Some(AsmError::InvalidOperand);
1812                    return;
1813                }
1814            }
1815
1816            Encoding::Vs2Zimm5Vd => {
1817                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1818                    let vd = ops[0].id();
1819                    let vs2 = ops[1].id();
1820                    let zimm5 = ops[2].as_::<Imm>().value() as i8;
1821                    inst = inst.set_vd(vd).set_vs2(vs2).set_zimm5(zimm5 as _);
1822                } else {
1823                    self.last_error = Some(AsmError::InvalidOperand);
1824                    return;
1825                }
1826            }
1827
1828            Encoding::Zimm10Zimm5Rd => {
1829                if isign3 == enc_ops3!(Reg, Imm, Imm) {
1830                    let rd = ops[0].id();
1831                    let uimm = ops[1].as_::<Imm>().value() as i8;
1832                    let vtypei = ops[2].as_::<Imm>().value() as i8;
1833                    inst = inst.set_rd(rd).set_zimm10(vtypei as _).set_zimm5(uimm as _);
1834                } else {
1835                    self.last_error = Some(AsmError::InvalidOperand);
1836                    return;
1837                }
1838            }
1839
1840            Encoding::Zimm11Rs1Rd => {
1841                if isign3 == enc_ops3!(Reg, Reg, Imm) {
1842                    let rd = ops[0].id();
1843                    let rs1 = ops[1].id();
1844                    let imm = ops[2].as_::<Imm>().value() as i32;
1845                    inst = inst.set_rd(rd).set_rs1(rs1).set_zimm11(imm);
1846                } else {
1847                    self.last_error = Some(AsmError::InvalidOperand);
1848                    return;
1849                }
1850            }
1851
1852            Encoding::Zimm6HiVmVs2Zimm6loVd => {
1853                if isign4 == enc_ops4!(Reg, Reg, Imm, Imm) {
1854                    let vd = ops[0].id();
1855                    let vs2 = ops[1].id();
1856                    let imm = ops[2].as_::<Imm>().value();
1857                    let vm = ops[3].as_::<Imm>().value();
1858                    if !(0..=1).contains(&vm) {
1859                        self.last_error = Some(AsmError::InvalidOperand);
1860                        return;
1861                    }
1862
1863                    inst = inst
1864                        .set_vd(vd)
1865                        .set_vs2(vs2)
1866                        .set_zimm6lohi(imm as _)
1867                        .set_vm(vm as _);
1868                } else {
1869                    self.last_error = Some(AsmError::InvalidOperand);
1870                    return;
1871                }
1872            }
1873            Encoding::RdRs1N0CNzimm6loCNzimm6hi => {
1874                short = true;
1875                if isign3 == enc_ops2!(Reg, Imm) {
1876                    let rd = ops[0].id();
1877                    let imm = ops[1].as_::<Imm>().value() as i32;
1878                    if imm == 0 {
1879                        self.last_error = Some(AsmError::InvalidOperand);
1880                        return;
1881                    } else {
1882                        inst = inst.set_rd_rs1_n0(rd).set_c_nzimm6lohi(imm);
1883                    }
1884                } else {
1885                    self.last_error = Some(AsmError::InvalidOperand);
1886                    return;
1887                }
1888            }
1889
1890            Encoding::RdRs1N0CImm6loCImm6hi => {
1891                short = true;
1892                if isign3 == enc_ops2!(Reg, Imm) {
1893                    let rd = ops[0].id();
1894                    let imm = ops[1].as_::<Imm>().value() as i32;
1895                    inst = inst.set_rd_rs1_n0(rd).set_c_imm6lohi(imm);
1896                } else {
1897                    self.last_error = Some(AsmError::InvalidOperand);
1898                    return;
1899                }
1900            }
1901
1902            Encoding::RdRs1N0CNzuimm6hiCNzuimm6lo => {
1903                short = true;
1904                if isign3 == enc_ops2!(Reg, Imm) {
1905                    let rd = ops[0].id();
1906                    let imm = ops[1].as_::<Imm>().value() as i32;
1907                    if imm == 0 {
1908                        self.last_error = Some(AsmError::InvalidOperand);
1909                        return;
1910                    } else {
1911                        inst = inst.set_rd_rs1_n0(rd).set_c_nzuimm6lohi(imm as u32);
1912                    }
1913                } else {
1914                    self.last_error = Some(AsmError::InvalidOperand);
1915                    return;
1916                }
1917            }
1918
1919            Encoding::RdRs1N0CNzuimm6lo => {
1920                short = true;
1921                if isign3 == enc_ops2!(Reg, Imm) {
1922                    let rd = ops[0].id();
1923                    let imm = ops[1].as_::<Imm>().value() as i32;
1924                    if imm == 0 {
1925                        self.last_error = Some(AsmError::InvalidOperand);
1926                        return;
1927                    } else {
1928                        inst = inst.set_rd_rs1_n0(rd).set_c_nzuimm6lo_raw(imm as u32);
1929                    }
1930                } else {
1931                    self.last_error = Some(AsmError::InvalidOperand);
1932                    return;
1933                }
1934            }
1935
1936            Encoding::RdRs1N0CRs2N0 => {
1937                short = true;
1938                if isign3 == enc_ops2!(Reg, Reg) {
1939                    let rd = ops[0].id();
1940                    let rs1 = ops[1].id();
1941                    inst = inst.set_rd_rs1_n0(rd).set_c_rs2_n0(rs1);
1942                } else {
1943                    self.last_error = Some(AsmError::InvalidOperand);
1944                    return;
1945                }
1946            }
1947
1948            Encoding::RdRs1P => {
1949                short = true;
1950                if isign3 == enc_ops1!(Reg) {
1951                    let rd = ops[0].id();
1952                    if !is_prime_register(rd) {
1953                        self.last_error = Some(AsmError::InvalidOperand);
1954                        return;
1955                    }
1956
1957                    inst = inst.set_rd_rs1_p(rd);
1958                } else {
1959                    self.last_error = Some(AsmError::InvalidOperand);
1960                    return;
1961                }
1962            }
1963
1964            Encoding::RdRs1PCImm6hiCImm6lo => {
1965                short = true;
1966                if isign3 == enc_ops2!(Reg, Imm) {
1967                    let rd = ops[0].id();
1968                    if !is_prime_register(rd) {
1969                        self.last_error = Some(AsmError::InvalidOperand);
1970                        return;
1971                    }
1972                    let imm = ops[1].as_::<Imm>().value() as i32;
1973                    inst = inst.set_rd_rs1_p(rd).set_c_imm6lohi(imm);
1974                } else {
1975                    self.last_error = Some(AsmError::InvalidOperand);
1976                    return;
1977                }
1978            }
1979
1980            Encoding::RdRs1PCNzuimm5 => {
1981                short = true;
1982                if isign3 == enc_ops2!(Reg, Imm) {
1983                    let rd = ops[0].id();
1984                    let imm = ops[1].as_::<Imm>().value() as i32;
1985                    if imm == 0 {
1986                        self.last_error = Some(AsmError::InvalidOperand);
1987                        return;
1988                    } else {
1989                        inst = inst.set_rd(rd).set_rs1(0).set_c_nzuimm5(imm as u32);
1990                    }
1991                } else {
1992                    self.last_error = Some(AsmError::InvalidOperand);
1993                    return;
1994                }
1995            }
1996
1997            Encoding::RdRs1PCNzuimm6loCNzuimm6hi => {
1998                short = true;
1999                if isign3 == enc_ops2!(Reg, Imm) {
2000                    let rd = ops[0].id();
2001                    if !is_prime_register(rd) {
2002                        self.last_error = Some(AsmError::InvalidOperand);
2003                        return;
2004                    }
2005                    let imm = ops[1].as_::<Imm>().value() as i32;
2006                    if imm == 0 {
2007                        self.last_error = Some(AsmError::InvalidOperand);
2008                        return;
2009                    } else {
2010                        inst = inst.set_rd_rs1_p(rd).set_c_nzuimm6lohi(imm as u32);
2011                    }
2012                } else {
2013                    self.last_error = Some(AsmError::InvalidOperand);
2014                    return;
2015                }
2016            }
2017
2018            Encoding::RdRs1PRs2P => {
2019                short = true;
2020                if isign3 == enc_ops2!(Reg, Reg) {
2021                    let rd = ops[0].id();
2022                    let rs2 = ops[1].id();
2023                    if !is_prime_register(rd) || !is_prime_register(rs2) {
2024                        self.last_error = Some(AsmError::InvalidOperand);
2025                        return;
2026                    }
2027                    inst = inst.set_rd_rs1_p(rd).set_rs2_p(rs2);
2028                } else {
2029                    self.last_error = Some(AsmError::InvalidOperand);
2030                    return;
2031                }
2032            }
2033
2034            Encoding::RdRs1Shamtd => {
2035                if isign3 == enc_ops3!(Reg, Reg, Imm) {
2036                    let rd = ops[0].id();
2037                    let rs1 = ops[1].id();
2038                    let shamt = ops[2].as_::<Imm>().value() as i32;
2039                    inst = inst.set_rd(rd).set_rs1(rs1).set_shamtd(shamt as _);
2040                } else {
2041                    self.last_error = Some(AsmError::InvalidOperand);
2042                    return;
2043                }
2044            }
2045        }
2046        let offset = self.buffer.cur_offset();
2047        if let Some((label, kind)) = label_use {
2048            self.buffer
2049                .use_label_at_offset(offset, label.as_::<Label>(), kind);
2050        }
2051
2052        if short {
2053            self.buffer.put2(inst.value as u16);
2054        } else {
2055            self.buffer.put4(inst.value);
2056        }
2057    }
2058}
2059
2060impl crate::core::builder::InstSink for Assembler<'_> {
2061    fn arch(&self) -> Arch {
2062        self.environment().arch()
2063    }
2064
2065    fn emit_inst(&mut self, inst: &crate::core::inst::Inst) -> Result<(), AsmError> {
2066        let ops = inst.operands();
2067        let mut refs: smallvec::SmallVec<[&Operand; 6]> = smallvec::SmallVec::new();
2068        refs.extend(ops.iter());
2069        self.try_emit_n(inst.id as i64, &refs)
2070    }
2071
2072    fn bind_label(&mut self, label: Label) -> Result<(), AsmError> {
2073        self.try_bind_label(label)
2074    }
2075}
2076
2077#[cfg(test)]
2078mod tests {
2079    use super::*;
2080    use crate::core::target::Environment;
2081    use crate::riscv::opcodes::{
2082        MATCH_C_LW, MATCH_C_NOT, MATCH_FADD_S, MATCH_FMADD_S, MATCH_FMV_W_X, MATCH_FMV_X_W,
2083        MATCH_LR_W, MATCH_SSAMOSWAP_W, MATCH_SSRDP, MATCH_VAESKF1_VI, MATCH_VFADD_VF, MATCH_VLE8_V,
2084    };
2085    use crate::riscv::operands::regs::*;
2086    use std::vec::Vec;
2087
2088    fn rv32(buf: &mut CodeBuffer) -> Assembler<'_> {
2089        Assembler::new(buf)
2090    }
2091
2092    fn data(buf: &mut CodeBuffer) -> Vec<u8> {
2093        buf.finish().unwrap().data().to_vec()
2094    }
2095
2096    #[test]
2097    fn default_environment_is_rv64() {
2098        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2099        let asm = Assembler::new(&mut buf);
2100        assert!(!asm.is_32bit());
2101        assert_eq!(asm.environment().arch(), Arch::RISCV64);
2102    }
2103
2104    #[test]
2105    fn fixed_fences_have_no_artificial_operands() {
2106        for arch in [Arch::RISCV32, Arch::RISCV64] {
2107            let mut buf = CodeBuffer::new(Environment::new(arch));
2108            {
2109                let mut asm = Assembler::new(&mut buf);
2110                asm.fence_i();
2111                asm.fence_tso();
2112                assert_eq!(asm.last_error(), None);
2113            }
2114            assert_eq!(
2115                data(&mut buf),
2116                [0x0000_100fu32.to_le_bytes(), 0x8330_000fu32.to_le_bytes()].concat()
2117            );
2118        }
2119    }
2120
2121    #[test]
2122    fn baseline_rejects_optional_extensions_before_writing() {
2123        let mut buf = CodeBuffer::new(Environment::baseline(Arch::RISCV64));
2124        let mut asm = Assembler::new(&mut buf);
2125        let vm = imm(1);
2126
2127        let error = asm
2128            .try_emit_n(
2129                Opcode::VADDVV as i64,
2130                &[
2131                    V0.as_operand(),
2132                    V1.as_operand(),
2133                    V2.as_operand(),
2134                    vm.as_operand(),
2135                ],
2136            )
2137            .unwrap_err();
2138
2139        assert!(
2140            matches!(error, AsmError::MissingCpuFeature { feature } if feature.contains("vadd.vv") && feature.contains("rv_v"))
2141        );
2142        assert!(asm.buffer.data().is_empty());
2143    }
2144
2145    #[test]
2146    fn optional_extensions_are_enabled_by_default() {
2147        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2148        let mut asm = Assembler::new(&mut buf);
2149        let vm = imm(1);
2150
2151        asm.try_emit_n(
2152            Opcode::VADDVV as i64,
2153            &[
2154                V0.as_operand(),
2155                V1.as_operand(),
2156                V2.as_operand(),
2157                vm.as_operand(),
2158            ],
2159        )
2160        .unwrap();
2161
2162        assert_eq!(asm.buffer.data().len(), 4);
2163    }
2164
2165    #[test]
2166    fn invalid_raw_opcode_is_rejected_without_writing() {
2167        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2168        let mut asm = Assembler::new(&mut buf);
2169
2170        asm.emit_n(-1, &[]);
2171        assert_eq!(asm.last_error(), Some(AsmError::InvalidInstruction));
2172        assert!(asm.buffer.data().is_empty());
2173
2174        asm.buffer.clear();
2175        asm.emit_n(i64::MAX, &[]);
2176        assert_eq!(asm.last_error(), Some(AsmError::InvalidInstruction));
2177        assert!(asm.buffer.data().is_empty());
2178    }
2179
2180    #[test]
2181    fn raw_emission_rejects_malformed_or_extra_operands_without_mutation() {
2182        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2183        let mut asm = Assembler::new(&mut buf);
2184        let malformed = Operand {
2185            signature: OperandSignature::from(7),
2186            base_id: 0,
2187            data: [0; 2],
2188        };
2189        let invalid_register = Operand {
2190            signature: OperandSignature::from(
2191                OperandType::Reg as u32 | (31 << OperandSignature::REG_TYPE_SHIFT),
2192            ),
2193            base_id: 0,
2194            data: [0; 2],
2195        };
2196        let none = Operand::new();
2197
2198        assert_eq!(
2199            asm.try_emit_n(Opcode::ADD as i64, &[&malformed]),
2200            Err(AsmError::InvalidOperand)
2201        );
2202        assert_eq!(
2203            asm.try_emit_n(Opcode::ADD as i64, &[&invalid_register]),
2204            Err(AsmError::InvalidOperand)
2205        );
2206        assert_eq!(
2207            asm.try_emit_n(Opcode::ADD as i64, &[RA.as_operand()]),
2208            Err(AsmError::InvalidOperand)
2209        );
2210        assert_eq!(
2211            asm.try_emit_n(
2212                Opcode::ADD as i64,
2213                &[&none, &none, &none, &none, &none, &none],
2214            ),
2215            Err(AsmError::InvalidOperand)
2216        );
2217        assert!(asm.buffer.error().is_none());
2218        assert!(asm.buffer.data().is_empty());
2219    }
2220
2221    #[test]
2222    fn macro_helpers_reject_wrong_operand_kinds_without_panicking() {
2223        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2224        let mut asm = Assembler::new(&mut buf);
2225
2226        asm.la(RA, imm(0));
2227        assert_eq!(asm.buffer.error(), Some(&AsmError::InvalidOperand));
2228        assert!(asm.buffer.data().is_empty());
2229
2230        asm.buffer.clear();
2231        asm.call(Operand::new());
2232        assert_eq!(asm.buffer.error(), Some(&AsmError::InvalidOperand));
2233        assert!(asm.buffer.data().is_empty());
2234    }
2235
2236    #[test]
2237    fn raw_label_registration_error_rolls_back_emission() {
2238        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2239        let mut asm = Assembler::new(&mut buf);
2240        let invalid_label = Label::from_id(0);
2241
2242        assert_eq!(
2243            asm.try_emit_n(
2244                Opcode::JAL as i64,
2245                &[RA.as_operand(), invalid_label.as_operand()]
2246            ),
2247            Err(AsmError::InvalidArgument)
2248        );
2249        assert_eq!(asm.buffer.error(), Some(&AsmError::InvalidArgument));
2250        assert!(asm.buffer.data().is_empty());
2251    }
2252
2253    #[test]
2254    fn failed_call_rolls_back_the_whole_sequence() {
2255        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2256        let mut asm = Assembler::new(&mut buf);
2257
2258        asm.call(Label::from_id(0));
2259
2260        assert_eq!(asm.buffer.error(), Some(&AsmError::InvalidArgument));
2261        assert!(asm.buffer.data().is_empty());
2262    }
2263
2264    #[test]
2265    fn unimplemented_encodings_return_typed_errors() {
2266        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2267        let mut asm = Assembler::new(&mut buf);
2268        let one = imm(1);
2269        let sixteen = imm(16);
2270        let zero = imm(0);
2271
2272        asm.emit_n(
2273            Opcode::CMPOP as i64,
2274            &[one.as_operand(), sixteen.as_operand()],
2275        );
2276        assert!(matches!(
2277            asm.last_error(),
2278            Some(AsmError::UnsupportedInstruction { .. })
2279        ));
2280        assert!(asm.buffer.data().is_empty());
2281
2282        asm.buffer.clear();
2283        asm.emit_n(Opcode::CMMVA01S as i64, &[A0.as_operand(), A1.as_operand()]);
2284        assert!(matches!(
2285            asm.last_error(),
2286            Some(AsmError::UnsupportedInstruction { .. })
2287        ));
2288        assert!(asm.buffer.data().is_empty());
2289
2290        asm.buffer.clear();
2291        asm.emit_n(
2292            Opcode::MOPRN as i64,
2293            &[
2294                zero.as_operand(),
2295                zero.as_operand(),
2296                zero.as_operand(),
2297                A0.as_operand(),
2298                A1.as_operand(),
2299            ],
2300        );
2301        assert!(matches!(
2302            asm.last_error(),
2303            Some(AsmError::UnsupportedInstruction { .. })
2304        ));
2305        assert!(asm.buffer.data().is_empty());
2306
2307        asm.buffer.clear();
2308        asm.emit_n(
2309            Opcode::MOPRRN as i64,
2310            &[
2311                zero.as_operand(),
2312                zero.as_operand(),
2313                A0.as_operand(),
2314                A1.as_operand(),
2315                A2.as_operand(),
2316            ],
2317        );
2318        assert!(matches!(
2319            asm.last_error(),
2320            Some(AsmError::UnsupportedInstruction { .. })
2321        ));
2322        assert!(asm.buffer.data().is_empty());
2323
2324        asm.buffer.clear();
2325        asm.emit_n(Opcode::CSEXTW as i64, &[A0.as_operand()]);
2326        assert!(matches!(
2327            asm.last_error(),
2328            Some(AsmError::UnsupportedInstruction { .. })
2329        ));
2330        assert!(asm.buffer.data().is_empty());
2331    }
2332
2333    #[test]
2334    fn rv32_encodes_base_and_rv32_only_instructions() {
2335        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV32));
2336        {
2337            let mut asm = rv32(&mut buf);
2338            asm.add(A0, A1, A2);
2339            // rv32-only variant of slli (shamt restricted to 5 bits).
2340            asm.slli_rv32(A0, A1, 5);
2341            assert_eq!(asm.last_error(), None);
2342        }
2343        let expected = [0x00C58533u32.to_le_bytes(), 0x00559513u32.to_le_bytes()].concat();
2344        assert_eq!(data(&mut buf), expected);
2345    }
2346
2347    #[test]
2348    fn rv32_rejects_rv64_only_instructions() {
2349        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV32));
2350        {
2351            let mut asm = rv32(&mut buf);
2352            asm.ld(A0, A1, imm(0));
2353            asm.addw(A0, A1, A2);
2354            assert_eq!(asm.last_error(), Some(AsmError::InvalidInstruction));
2355        }
2356        assert_eq!(buf.finish().err(), Some(AsmError::InvalidInstruction));
2357    }
2358
2359    #[test]
2360    fn rv64_rejects_rv32_only_instructions() {
2361        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2362        {
2363            let mut asm = Assembler::new(&mut buf);
2364            asm.slli_rv32(A0, A1, imm(5));
2365            assert_eq!(asm.last_error(), Some(AsmError::InvalidInstruction));
2366        }
2367        assert_eq!(buf.finish().err(), Some(AsmError::InvalidInstruction));
2368    }
2369
2370    #[test]
2371    fn rv64_encodes_rv64_only_instructions() {
2372        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2373        {
2374            let mut asm = Assembler::new(&mut buf);
2375            asm.ld(A0, A1, imm(0));
2376            asm.addw(A0, A1, A2);
2377            assert_eq!(asm.last_error(), None);
2378        }
2379        let expected = [0x0005B503u32.to_le_bytes(), 0x00C5853Bu32.to_le_bytes()].concat();
2380        assert_eq!(data(&mut buf), expected);
2381    }
2382
2383    #[test]
2384    fn lpad_encodes_on_both_xlen() {
2385        // lpad is auipc with rd=x0; U-type immediates are passed pre-shifted
2386        // (bits [31:12] of the operand, as for lui/auipc).
2387        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV32));
2388        {
2389            let mut asm = rv32(&mut buf);
2390            asm.lpad(imm(0x12345 << 12));
2391            assert_eq!(asm.last_error(), None);
2392        }
2393        assert_eq!(data(&mut buf), 0x12345017u32.to_le_bytes());
2394
2395        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2396        {
2397            let mut asm = Assembler::new(&mut buf);
2398            asm.lpad(imm(0x12345 << 12));
2399            assert_eq!(asm.last_error(), None);
2400        }
2401        assert_eq!(data(&mut buf), 0x12345017u32.to_le_bytes());
2402    }
2403
2404    #[test]
2405    fn zicfiss_shadow_stack_instructions_encode() {
2406        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV32));
2407        {
2408            let mut asm = rv32(&mut buf);
2409            asm.sspush_x1();
2410            asm.ssrdp(A0);
2411            asm.ssamoswap_w(A0, A1, A2, imm(0), imm(0));
2412            assert_eq!(asm.last_error(), None);
2413        }
2414        let sspush_x1 = 0xCE104073u32; // architecturally fixed encoding
2415        let ssrdp_a0 = MATCH_SSRDP | (10 << 7);
2416        let ssamoswap_w = MATCH_SSAMOSWAP_W | (10 << 7) | (11 << 15) | (12 << 20);
2417        let expected = [
2418            sspush_x1.to_le_bytes(),
2419            ssrdp_a0.to_le_bytes(),
2420            ssamoswap_w.to_le_bytes(),
2421        ]
2422        .concat();
2423        assert_eq!(data(&mut buf), expected);
2424    }
2425
2426    #[test]
2427    fn rv32_accepts_zclsd_compressed_load_store_pair() {
2428        // Zclsd makes the c.ld/c.sd encodings available on rv32 (they reuse the
2429        // rv32 Zcf encoding space).
2430        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV32));
2431        {
2432            let mut asm = rv32(&mut buf);
2433            asm.c_ld(A0, A1, imm(0));
2434            // Stores take (base, source, imm), like sw.
2435            asm.c_sd(A1, A0, imm(0));
2436            assert_eq!(asm.last_error(), None);
2437        }
2438        // c.ld a0', 0(a1') = 0x6188, c.sd a0', 0(a1') = 0xE188.
2439        assert_eq!(data(&mut buf), [0x88, 0x61, 0x88, 0xE1]);
2440    }
2441
2442    #[test]
2443    fn compressed_loads_use_prime_register_fields() {
2444        // c.lw encodes rd'/rs1' as 3-bit fields (registers x8..x15).
2445        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2446        {
2447            let mut asm = Assembler::new(&mut buf);
2448            asm.c_lw(S0, A5, imm(0));
2449            assert_eq!(asm.last_error(), None);
2450        }
2451        assert_eq!(
2452            data(&mut buf),
2453            ((MATCH_C_LW | (7 << 7)) as u16).to_le_bytes()
2454        );
2455    }
2456
2457    #[test]
2458    fn typed_vector_fields_encode_and_validate() {
2459        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2460        {
2461            let mut asm = Assembler::new(&mut buf);
2462            asm.vle8_v(V1, A0, imm(1), imm(3));
2463            assert_eq!(asm.last_error(), None);
2464        }
2465        let expected = MATCH_VLE8_V | (1 << 7) | (10 << 15) | (1 << 25) | (3 << 29);
2466        assert_eq!(data(&mut buf), expected.to_le_bytes());
2467
2468        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2469        let mut asm = Assembler::new(&mut buf);
2470        asm.vle8_v(V1, A0, imm(2), imm(0));
2471        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2472        asm.buffer.clear();
2473        asm.vle8_v(V1, A0, imm(1), imm(8));
2474        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2475
2476        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2477        let mut asm = Assembler::new(&mut buf);
2478        asm.vaeskf1_vi(V1, V2, 3);
2479        assert_eq!(asm.last_error(), None);
2480        let expected = MATCH_VAESKF1_VI | (1 << 7) | (3 << 15) | (2 << 20);
2481        assert_eq!(asm.buffer.data(), expected.to_le_bytes());
2482
2483        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2484        let mut asm = Assembler::new(&mut buf);
2485        asm.vfadd_vf(V1, V2, F0, 1);
2486        asm.fmv_w_x(F1, A0);
2487        asm.fmv_x_w(A1, F2);
2488        assert_eq!(asm.last_error(), None);
2489        let expected = [
2490            (MATCH_VFADD_VF | (1 << 7) | (2 << 20) | (1 << 25)).to_le_bytes(),
2491            (MATCH_FMV_W_X | (1 << 7) | (10 << 15)).to_le_bytes(),
2492            (MATCH_FMV_X_W | (11 << 7) | (2 << 15)).to_le_bytes(),
2493        ]
2494        .concat();
2495        assert_eq!(asm.buffer.data(), expected);
2496    }
2497
2498    #[test]
2499    fn pc_relative_label_register_coupling_is_transactional() {
2500        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2501        let mut asm = Assembler::new(&mut buf);
2502        let label = asm.get_label();
2503        asm.emit_n(
2504            Opcode::JALR as i64,
2505            &[A0.as_operand(), A1.as_operand(), label.as_operand()],
2506        );
2507        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2508        assert!(asm.buffer.data().is_empty());
2509
2510        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2511        let mut asm = Assembler::new(&mut buf);
2512        let label = asm.get_label();
2513        asm.emit_n(
2514            Opcode::LW as i64,
2515            &[A0.as_operand(), A1.as_operand(), label.as_operand()],
2516        );
2517        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2518        assert!(asm.buffer.data().is_empty());
2519
2520        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2521        let mut asm = Assembler::new(&mut buf);
2522        let label = asm.get_label();
2523        asm.emit_n(
2524            Opcode::JALR as i64,
2525            &[A0.as_operand(), A0.as_operand(), label.as_operand()],
2526        );
2527        assert_eq!(asm.last_error(), None);
2528        assert_eq!(asm.buffer.data().len(), 8);
2529
2530        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2531        let mut asm = Assembler::new(&mut buf);
2532        let label = asm.get_label();
2533        asm.emit_n(
2534            Opcode::LW as i64,
2535            &[A0.as_operand(), A0.as_operand(), label.as_operand()],
2536        );
2537        assert_eq!(asm.last_error(), None);
2538        assert_eq!(asm.buffer.data().len(), 8);
2539    }
2540
2541    #[test]
2542    fn typed_atomic_and_rounding_fields_validate() {
2543        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2544        {
2545            let mut asm = Assembler::new(&mut buf);
2546            asm.lr_w(A0, A1, 0, 0);
2547            asm.lr_w(A0, A1, 0, 1);
2548            asm.lr_w(A0, A1, 1, 0);
2549            asm.lr_w(A0, A1, 1, 1);
2550            assert_eq!(asm.last_error(), None);
2551        }
2552        let base = MATCH_LR_W | (10 << 7) | (11 << 15);
2553        let expected = [
2554            base.to_le_bytes(),
2555            (base | (1 << 25)).to_le_bytes(),
2556            (base | (1 << 26)).to_le_bytes(),
2557            (base | (3 << 25)).to_le_bytes(),
2558        ]
2559        .concat();
2560        assert_eq!(data(&mut buf), expected);
2561
2562        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2563        let mut asm = Assembler::new(&mut buf);
2564        asm.fadd_s(F0, F1, F2, imm(5));
2565        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2566        asm.buffer.clear();
2567        asm.fadd_s(F0, F1, F2, imm(7));
2568        assert_eq!(asm.last_error(), None);
2569        let expected = MATCH_FADD_S | (1 << 15) | (2 << 20) | (7 << 12);
2570        assert_eq!(asm.buffer.data(), expected.to_le_bytes());
2571
2572        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2573        let mut asm = Assembler::new(&mut buf);
2574        asm.fmadd_s(F0, F1, F2, F3, 0);
2575        assert_eq!(asm.last_error(), None);
2576        let expected = MATCH_FMADD_S | (1 << 15) | (2 << 20) | (3 << 27);
2577        assert_eq!(asm.buffer.data(), expected.to_le_bytes());
2578    }
2579
2580    #[test]
2581    fn compressed_prime_registers_reject_non_prime_ids() {
2582        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2583        let mut asm = Assembler::new(&mut buf);
2584        asm.c_lw(T2, A1, imm(0));
2585        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2586        assert!(asm.buffer.data().is_empty());
2587
2588        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2589        {
2590            let mut asm = Assembler::new(&mut buf);
2591            asm.c_not(S0);
2592            assert_eq!(asm.last_error(), None);
2593        }
2594        assert_eq!(
2595            data(&mut buf),
2596            ((MATCH_C_NOT & !(0x7 << 7)) as u16).to_le_bytes()
2597        );
2598
2599        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2600        let mut asm = Assembler::new(&mut buf);
2601        asm.c_not(T2);
2602        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2603        assert!(asm.buffer.data().is_empty());
2604
2605        asm.buffer.clear();
2606        asm.c_lw(A0, T2, imm(0));
2607        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2608        assert!(asm.buffer.data().is_empty());
2609
2610        asm.buffer.clear();
2611        asm.c_lw(A6, A1, imm(0));
2612        assert_eq!(asm.last_error(), Some(AsmError::InvalidOperand));
2613        assert!(asm.buffer.data().is_empty());
2614    }
2615
2616    #[test]
2617    fn register_label_encodings_use_the_label_operand() {
2618        for arch in [Arch::RISCV32, Arch::RISCV64] {
2619            let mut buf = CodeBuffer::new(Environment::new(arch));
2620            let target = buf.get_label();
2621            {
2622                let mut asm = Assembler::new(&mut buf);
2623                asm.auipc(A0, target);
2624                asm.jal(A1, target);
2625                asm.bind_label(target);
2626                assert_eq!(asm.last_error(), None);
2627            }
2628            assert_eq!(buf.label_offset(target), 8);
2629            assert_eq!(
2630                data(&mut buf),
2631                [0x0000_0517u32.to_le_bytes(), 0x0040_05EFu32.to_le_bytes()].concat()
2632            );
2633
2634            let mut buf = CodeBuffer::new(Environment::new(arch));
2635            let target = buf.get_label();
2636            {
2637                let mut asm = Assembler::new(&mut buf);
2638                asm.bind_label(target);
2639                asm.auipc(A0, target);
2640                asm.jal(A1, target);
2641                assert_eq!(asm.last_error(), None);
2642            }
2643            assert_eq!(buf.label_offset(target), 0);
2644            assert_eq!(
2645                data(&mut buf),
2646                [0x0000_0517u32.to_le_bytes(), 0xFFDFF5EFu32.to_le_bytes()].concat()
2647            );
2648        }
2649    }
2650
2651    #[test]
2652    fn patchable_li_literal_can_be_rewritten() {
2653        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2654        let block = {
2655            let mut asm = Assembler::new(&mut buf);
2656            asm.patchable_li(A0, 0x1122_3344_5566_7788u64 as i64)
2657        };
2658        let code = buf.finish_patched().unwrap();
2659        assert_eq!(block.size(), 8);
2660        assert_eq!(
2661            &code.data()[block.offset() as usize..][..8],
2662            &0x1122_3344_5566_7788u64.to_le_bytes()
2663        );
2664        // The `ld` must read the literal at `auipc + 12`, not the `jal`
2665        // that skips over it.
2666        let ld = u32::from_le_bytes([
2667            code.data()[4],
2668            code.data()[5],
2669            code.data()[6],
2670            code.data()[7],
2671        ]);
2672        assert_eq!(((ld as i32) >> 20) & 0xFFF, 12);
2673
2674        let mut bytes = code.data().to_vec();
2675        unsafe {
2676            block
2677                .repatch_u64(&mut bytes, 0xAABB_CCDD_EEFF_0011)
2678                .unwrap();
2679        }
2680        assert_eq!(
2681            &bytes[block.offset() as usize..][..8],
2682            &0xAABB_CCDD_EEFF_0011u64.to_le_bytes()
2683        );
2684    }
2685
2686    #[test]
2687    fn patchable_j_can_be_retargeted_offline() {
2688        let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
2689        let (site, alt) = {
2690            let mut asm = Assembler::new(&mut buf);
2691            let target = asm.get_label();
2692            let alt = asm.get_label();
2693            let site = asm.patchable_j(target);
2694            asm.bind_label(target);
2695            asm.addi(A0, A0, imm(1));
2696            asm.bind_label(alt);
2697            asm.addi(A0, A0, imm(2));
2698            (site, asm.label_offset(alt))
2699        };
2700        let code = buf.finish_patched().unwrap();
2701        let mut bytes = code.data().to_vec();
2702        unsafe {
2703            site.retarget(&mut bytes, alt).unwrap();
2704        }
2705        // Catalog still describes the original target; the handle rewrote the bytes.
2706        assert_ne!(bytes, code.data());
2707    }
2708}