use core::fmt::{self, Display, Write};
use crate::csr::csr_name;
use crate::instruction::Instruction;
use crate::opcodes::zfa::FLI_CONSTANTS;
use crate::reg::{FP_NAMES, INT_NAMES};
impl Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = self.op.name;
let name = match (self.aq || self.rl)
.then(|| name.strip_suffix(".aq").or(name.strip_suffix(".rl")))
{
Some(Some(base)) => base,
_ => name,
};
f.write_str(name)?;
match (self.aq, self.rl) {
(true, true) => f.write_str(".aqrl")?,
(true, false) => f.write_str(".aq")?,
(false, true) => f.write_str(".rl")?,
(false, false) => {}
}
if !self.op.format.is_empty() {
write!(f, " {}", self.operands())?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Operands<'a>(pub(crate) &'a Instruction);
impl Display for Operands<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ins = self.0;
for c in ins.op.format.chars() {
write_part(f, ins, c)?;
}
Ok(())
}
}
fn write_part(out: &mut fmt::Formatter<'_>, ins: &Instruction, c: char) -> fmt::Result {
let fp_names = if ins.fp_in_x { &INT_NAMES } else { &FP_NAMES };
match c {
'(' | ',' | ' ' | ')' | '-' => out.write_char(c),
'L' => match ins.rs1 {
4 => out.write_str("{ra}"),
5 => out.write_str("{ra,s0}"),
15 => out.write_str("{ra,s0-s11}"),
rlist => write!(out, "{{ra,s0-s{}}}", rlist - 5),
},
'0' => out.write_str(reg(&INT_NAMES, ins.rd)),
'1' => out.write_str(reg(&INT_NAMES, ins.rs1)),
'2' => out.write_str(reg(&INT_NAMES, ins.rs2)),
'3' => out.write_str(reg(fp_names, ins.rd)),
'4' => out.write_str(reg(fp_names, ins.rs1)),
'5' => out.write_str(reg(fp_names, ins.rs2)),
'6' => out.write_str(reg(fp_names, ins.rs3)),
'7' => write!(out, "{}", ins.rs1),
'i' => write!(out, "{}", ins.imm),
'j' => write!(out, "{}", ins.imm2),
'>' => write!(out, "{:#x}", ins.imm),
'u' => write!(out, "{:#x}", (ins.imm as u32 >> 12) & 0xfffff),
'o' => write!(out, "{:#x}", ins.target()),
'c' => match csr_name(ins.imm as u16 & 0xfff) {
Some(name) => out.write_str(name),
None => write!(out, "{:#x}", ins.imm & 0xfff),
},
'r' if ins.rm == 7 => Ok(()),
'R' if ins.rm == 0 => Ok(()),
'r' | 'R' => {
let names = [
"rne", "rtz", "rdn", "rup", "rmm", "unknown", "unknown", "dyn",
];
write!(out, ",{}", names[ins.rm as usize & 7])
}
'P' => write!(out, "{}", ins.imm & !0x1f),
'D' => write!(out, "v{}", ins.rd),
'A' => write!(out, "v{}", ins.rs1),
'B' => write!(out, "v{}", ins.rs2),
'M' if ins.masked => out.write_str(",v0.t"),
'M' => Ok(()),
'Z' => out.write_str("v0"),
'T' => write_vtype(out, ins.imm as u32),
'F' => out.write_str(FLI_CONSTANTS[ins.rs1 as usize & 31]),
'l' => write!(out, "{}", ins.len),
'x' => {
let parcels = (64 - ins.inst.leading_zeros()).div_ceil(16).max(1) as usize;
write!(out, "0x{:01$x}", ins.inst, parcels * 4)
}
'p' => write_fence_set(out, ins.pred),
's' => write_fence_set(out, ins.succ),
_ => {
debug_assert!(false, "unknown format character {c:?}");
Ok(())
}
}
}
fn reg(names: &'static [&'static str; 32], r: u8) -> &'static str {
names[r as usize & 31]
}
fn write_vtype(out: &mut fmt::Formatter<'_>, vtype: u32) -> fmt::Result {
let (sew, lmul) = ((vtype >> 3) & 7, vtype & 7);
let lmul = ["m1", "m2", "m4", "m8", "", "mf8", "mf4", "mf2"][lmul as usize];
if vtype >> 8 != 0 || sew > 3 || lmul.is_empty() {
return write!(out, "{vtype}");
}
let tail = if vtype & 0x40 != 0 { "ta" } else { "tu" };
let mask = if vtype & 0x80 != 0 { "ma" } else { "mu" };
write!(out, "e{},{lmul},{tail},{mask}", 8 << sew)
}
fn write_fence_set(out: &mut fmt::Formatter<'_>, set: u8) -> fmt::Result {
if set == 0 {
return out.write_str("unknown");
}
for (bit, name) in [(8, 'i'), (4, 'o'), (2, 'r'), (1, 'w')] {
if set & bit != 0 {
out.write_char(name)?;
}
}
Ok(())
}