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