use super::riscv_instr_info::RiscVInstrInfo;
#[cfg(test)]
use super::riscv_instr_info::RiscVOpcode;
use super::riscv_register_info::*;
use crate::codegen::*;
use crate::module::Module;
pub struct RiscVAsmPrinter {
pub output: String,
pub is_64bit: bool,
pub instr_info: RiscVInstrInfo,
}
impl RiscVAsmPrinter {
pub fn new(is_64bit: bool) -> Self {
RiscVAsmPrinter {
output: String::new(),
is_64bit,
instr_info: RiscVInstrInfo::new(),
}
}
pub fn print_function(&mut self, mf: &MachineFunction) {
self.print_prologue(mf);
for bb in &mf.blocks {
self.print_basic_block(bb);
}
self.print_epilogue(mf);
}
pub fn print_module(&mut self, module: &Module) {
if self.is_64bit {
self.output.push_str("\t.attribute\t4, 5\n");
self.output.push_str("\t.attribute\t5, \"rv64i2p1\"\n");
} else {
self.output.push_str("\t.attribute\t4, 4\n");
self.output.push_str("\t.attribute\t5, \"rv32i2p1\"\n");
}
if !module.source_filename.is_empty() {
self.output
.push_str(&format!("\t.file\t\"{}\"\n", module.source_filename));
}
self.output.push_str("\t.text\n");
}
pub fn print_prologue(&mut self, mf: &MachineFunction) {
let name = &mf.name;
self.output.push_str(&format!("\t.globl\t{}\n", name));
self.output
.push_str(&format!("\t.type\t{}, @function\n", name));
self.output.push_str(&format!("{}:\n", name));
}
pub fn print_epilogue(&mut self, mf: &MachineFunction) {
let name = &mf.name;
self.output
.push_str(&format!("\t.size\t{}, .-{}\n", name, name));
}
pub fn print_basic_block(&mut self, bb: &MachineBasicBlock) {
if !bb.name.is_empty() {
self.output.push_str(&format!(".LBB_{}:\n", bb.name));
}
for mi in &bb.instructions {
self.print_instruction(mi);
}
}
pub fn print_instruction(&mut self, mi: &MachineInstr) {
let mnemonic = self.get_mnemonic(mi.opcode);
if mnemonic.is_empty() || mnemonic == "INVALID" {
return;
}
let mut op_strs: Vec<String> = Vec::new();
let len = mi.operands.len();
let mut skip_last_two = false;
if len >= 2 {
let second_last = &mi.operands[len - 2];
let last = &mi.operands[len - 1];
let base_is_reg = matches!(
second_last,
MachineOperand::PhysReg(_) | MachineOperand::Reg(_)
);
let last_is_imm = matches!(last, MachineOperand::Imm(_));
if base_is_reg && last_is_imm {
skip_last_two = true;
for j in 0..len - 2 {
op_strs.push(self.print_operand(&mi.operands[j]));
}
let base_name = match second_last {
MachineOperand::PhysReg(r) => self.format_reg(*r as u16),
MachineOperand::Reg(vr) => format!("%vreg{}", vr),
_ => unreachable!(),
};
let offset = match last {
MachineOperand::Imm(imm) => *imm,
_ => unreachable!(),
};
op_strs.push(format!("{}({})", offset, base_name));
} else if matches!(second_last, MachineOperand::Imm(_)) && base_is_reg == false {
let imm_is_second_last = matches!(second_last, MachineOperand::Imm(_));
let last_is_reg =
matches!(last, MachineOperand::PhysReg(_) | MachineOperand::Reg(_));
if imm_is_second_last && last_is_reg {
skip_last_two = true;
for j in 0..len - 2 {
op_strs.push(self.print_operand(&mi.operands[j]));
}
let base_name = match last {
MachineOperand::PhysReg(r) => self.format_reg(*r as u16),
MachineOperand::Reg(vr) => format!("%vreg{}", vr),
_ => unreachable!(),
};
let offset = match second_last {
MachineOperand::Imm(imm) => *imm,
_ => unreachable!(),
};
op_strs.push(format!("{}({})", offset, base_name));
}
}
}
if !skip_last_two {
for op in &mi.operands {
let s = self.print_operand(op);
if !s.is_empty() {
op_strs.push(s);
}
}
}
if op_strs.is_empty() {
self.output.push_str(&format!("\t{}\n", mnemonic));
} else {
self.output
.push_str(&format!("\t{}\t{}\n", mnemonic, op_strs.join(", ")));
}
}
pub fn print_operand(&self, op: &MachineOperand) -> String {
match op {
MachineOperand::Reg(vr) => {
format!("%vreg{}", vr)
}
MachineOperand::PhysReg(reg) => self.format_reg(*reg as u16),
MachineOperand::Imm(imm) => self.format_imm(*imm),
MachineOperand::Label(label) => {
format!(".LBB_{}", label)
}
MachineOperand::Global(name) => name.clone(),
}
}
pub fn get_mnemonic(&self, _opcode: u32) -> String {
self.get_mnemonic_fallback(_opcode)
}
fn get_mnemonic_fallback(&self, opcode: u32) -> String {
match opcode {
0 => "lui".to_string(),
1 => "auipc".to_string(),
2 => "jal".to_string(),
3 => "jalr".to_string(),
4 => "beq".to_string(),
5 => "bne".to_string(),
6 => "blt".to_string(),
7 => "bge".to_string(),
8 => "bltu".to_string(),
9 => "bgeu".to_string(),
10 => "lb".to_string(),
11 => "lh".to_string(),
12 => "lw".to_string(),
13 => "lbu".to_string(),
14 => "lhu".to_string(),
15 => "ld".to_string(),
16 => "lwu".to_string(),
17 => "sb".to_string(),
18 => "sh".to_string(),
19 => "sw".to_string(),
20 => "sd".to_string(),
21 => "addi".to_string(),
22 => "slti".to_string(),
23 => "sltiu".to_string(),
24 => "xori".to_string(),
25 => "ori".to_string(),
26 => "andi".to_string(),
27 => "slli".to_string(),
28 => "srli".to_string(),
29 => "srai".to_string(),
30 => "add".to_string(),
31 => "sub".to_string(),
32 => "sll".to_string(),
33 => "slt".to_string(),
34 => "sltu".to_string(),
35 => "xor".to_string(),
36 => "srl".to_string(),
37 => "sra".to_string(),
38 => "or".to_string(),
39 => "and".to_string(),
40 => "addiw".to_string(),
41 => "slliw".to_string(),
42 => "srliw".to_string(),
43 => "sraiw".to_string(),
44 => "addw".to_string(),
45 => "subw".to_string(),
46 => "sllw".to_string(),
47 => "srlw".to_string(),
48 => "sraw".to_string(),
49 => "fence".to_string(),
50 => "fence.i".to_string(),
51 => "fence.tso".to_string(),
52 => "pause".to_string(),
53 => "ecall".to_string(),
54 => "ebreak".to_string(),
55 => "csrrw".to_string(),
56 => "csrrs".to_string(),
57 => "csrrc".to_string(),
58 => "csrrwi".to_string(),
59 => "csrrsi".to_string(),
60 => "csrrci".to_string(),
61 => "mul".to_string(),
62 => "mulh".to_string(),
63 => "mulhsu".to_string(),
64 => "mulhu".to_string(),
65 => "div".to_string(),
66 => "divu".to_string(),
67 => "rem".to_string(),
68 => "remu".to_string(),
69 => "mulw".to_string(),
70 => "divw".to_string(),
71 => "divuw".to_string(),
72 => "remw".to_string(),
73 => "remuw".to_string(),
74 => "lr.w".to_string(),
75 => "sc.w".to_string(),
76 => "lr.d".to_string(),
77 => "sc.d".to_string(),
78 => "amoswap.w".to_string(),
79 => "amoadd.w".to_string(),
80 => "amoxor.w".to_string(),
81 => "amoand.w".to_string(),
82 => "amoor.w".to_string(),
83 => "amomin.w".to_string(),
84 => "amomax.w".to_string(),
85 => "amominu.w".to_string(),
86 => "amomaxu.w".to_string(),
87 => "amoswap.d".to_string(),
88 => "amoadd.d".to_string(),
89 => "amoxor.d".to_string(),
90 => "amoand.d".to_string(),
91 => "amoor.d".to_string(),
92 => "amomin.d".to_string(),
93 => "amomax.d".to_string(),
94 => "amominu.d".to_string(),
95 => "amomaxu.d".to_string(),
96 => "flw".to_string(),
97 => "fsw".to_string(),
98 => "fmadd.s".to_string(),
99 => "fmsub.s".to_string(),
100 => "fnmsub.s".to_string(),
101 => "fnmadd.s".to_string(),
102 => "fadd.s".to_string(),
103 => "fsub.s".to_string(),
104 => "fmul.s".to_string(),
105 => "fdiv.s".to_string(),
106 => "fsqrt.s".to_string(),
107 => "fsgnj.s".to_string(),
108 => "fsgnjn.s".to_string(),
109 => "fsgnjx.s".to_string(),
110 => "fmin.s".to_string(),
111 => "fmax.s".to_string(),
112 => "fcvt.w.s".to_string(),
113 => "fcvt.wu.s".to_string(),
114 => "fmv.x.w".to_string(),
115 => "feq.s".to_string(),
116 => "flt.s".to_string(),
117 => "fle.s".to_string(),
118 => "fclass.s".to_string(),
119 => "fcvt.s.w".to_string(),
120 => "fcvt.s.wu".to_string(),
121 => "fmv.w.x".to_string(),
122 => "fcvt.l.s".to_string(),
123 => "fcvt.lu.s".to_string(),
124 => "fcvt.s.l".to_string(),
125 => "fcvt.s.lu".to_string(),
126 => "fld".to_string(),
127 => "fsd".to_string(),
128 => "fmadd.d".to_string(),
129 => "fmsub.d".to_string(),
130 => "fnmsub.d".to_string(),
131 => "fnmadd.d".to_string(),
132 => "fadd.d".to_string(),
133 => "fsub.d".to_string(),
134 => "fmul.d".to_string(),
135 => "fdiv.d".to_string(),
136 => "fsqrt.d".to_string(),
137 => "fsgnj.d".to_string(),
138 => "fsgnjn.d".to_string(),
139 => "fsgnjx.d".to_string(),
140 => "fmin.d".to_string(),
141 => "fmax.d".to_string(),
142 => "fcvt.w.d".to_string(),
143 => "fcvt.wu.d".to_string(),
144 => "fmv.x.d".to_string(),
145 => "feq.d".to_string(),
146 => "flt.d".to_string(),
147 => "fle.d".to_string(),
148 => "fclass.d".to_string(),
149 => "fcvt.d.w".to_string(),
150 => "fcvt.d.wu".to_string(),
151 => "fmv.d.x".to_string(),
152 => "fcvt.l.d".to_string(),
153 => "fcvt.lu.d".to_string(),
154 => "fcvt.d.l".to_string(),
155 => "fcvt.d.lu".to_string(),
156 => "fcvt.s.d".to_string(),
157 => "fcvt.d.s".to_string(),
158 => "c.addi4spn".to_string(),
159 => "c.lw".to_string(),
160 => "c.ld".to_string(),
161 => "c.flw".to_string(),
162 => "c.fld".to_string(),
163 => "c.sw".to_string(),
164 => "c.sd".to_string(),
165 => "c.fsw".to_string(),
166 => "c.fsd".to_string(),
167 => "c.nop".to_string(),
168 => "c.addi".to_string(),
169 => "c.addiw".to_string(),
170 => "c.addi16sp".to_string(),
171 => "c.li".to_string(),
172 => "c.lui".to_string(),
173 => "c.slli".to_string(),
174 => "c.srai".to_string(),
175 => "c.srli".to_string(),
176 => "c.misc_alu".to_string(),
177 => "c.j".to_string(),
178 => "c.jal".to_string(),
179 => "c.beqz".to_string(),
180 => "c.bnez".to_string(),
181 => "c.mv".to_string(),
182 => "c.add".to_string(),
183 => "c.jr".to_string(),
184 => "c.jalr".to_string(),
185 => "c.ebreak".to_string(),
186 => "c.swsp".to_string(),
187 => "c.sdsp".to_string(),
188 => "c.fswsp".to_string(),
189 => "c.fsdsp".to_string(),
190 => "c.lwsp".to_string(),
191 => "c.ldsp".to_string(),
192 => "c.flwsp".to_string(),
193 => "c.fldsp".to_string(),
194 => "c.sub".to_string(),
195 => "c.xor".to_string(),
196 => "c.or".to_string(),
197 => "c.and".to_string(),
198 => "c.andi".to_string(),
199 => "nop".to_string(),
200 => "mv".to_string(),
201 => "not".to_string(),
202 => "neg".to_string(),
203 => "negw".to_string(),
204 => "seqz".to_string(),
205 => "snez".to_string(),
206 => "sltz".to_string(),
207 => "sgtz".to_string(),
208 => "beqz".to_string(),
209 => "bnez".to_string(),
210 => "blez".to_string(),
211 => "bgez".to_string(),
212 => "bltz".to_string(),
213 => "bgtz".to_string(),
214 => "j".to_string(),
215 => "jr".to_string(),
216 => "ret".to_string(),
217 => "call".to_string(),
218 => "tail".to_string(),
219 => "li".to_string(),
220 => "la".to_string(),
_ => {
"INVALID".to_string()
}
}
}
pub fn format_reg(&self, reg_id: u16) -> String {
if reg_id >= GPR_BASE && reg_id < GPR_BASE + 32 {
return self.format_gpr(reg_id);
}
if reg_id >= FPR_BASE && reg_id < FPR_BASE + 32 {
return self.format_fp_reg(reg_id);
}
format!("%r{}", reg_id)
}
pub fn format_gpr(&self, reg: u16) -> String {
let idx = reg - GPR_BASE;
match idx {
0 => "zero".to_string(),
1 => "ra".to_string(),
2 => "sp".to_string(),
3 => "gp".to_string(),
4 => "tp".to_string(),
5 => "t0".to_string(),
6 => "t1".to_string(),
7 => "t2".to_string(),
8 => "s0".to_string(),
9 => "s1".to_string(),
10 => "a0".to_string(),
11 => "a1".to_string(),
12 => "a2".to_string(),
13 => "a3".to_string(),
14 => "a4".to_string(),
15 => "a5".to_string(),
16 => "a6".to_string(),
17 => "a7".to_string(),
18 => "s2".to_string(),
19 => "s3".to_string(),
20 => "s4".to_string(),
21 => "s5".to_string(),
22 => "s6".to_string(),
23 => "s7".to_string(),
24 => "s8".to_string(),
25 => "s9".to_string(),
26 => "s10".to_string(),
27 => "s11".to_string(),
28 => "t3".to_string(),
29 => "t4".to_string(),
30 => "t5".to_string(),
31 => "t6".to_string(),
_ => format!("x{}", idx),
}
}
pub fn format_fp_reg(&self, reg_id: u16) -> String {
let f_idx = reg_id - FPR_BASE;
if f_idx > 31 {
return format!("%f{}", f_idx);
}
match f_idx {
0 => "ft0".to_string(),
1 => "ft1".to_string(),
2 => "ft2".to_string(),
3 => "ft3".to_string(),
4 => "ft4".to_string(),
5 => "ft5".to_string(),
6 => "ft6".to_string(),
7 => "ft7".to_string(),
8 => "fs0".to_string(),
9 => "fs1".to_string(),
10 => "fa0".to_string(),
11 => "fa1".to_string(),
12 => "fa2".to_string(),
13 => "fa3".to_string(),
14 => "fa4".to_string(),
15 => "fa5".to_string(),
16 => "fa6".to_string(),
17 => "fa7".to_string(),
18 => "fs2".to_string(),
19 => "fs3".to_string(),
20 => "fs4".to_string(),
21 => "fs5".to_string(),
22 => "fs6".to_string(),
23 => "fs7".to_string(),
24 => "fs8".to_string(),
25 => "fs9".to_string(),
26 => "fs10".to_string(),
27 => "fs11".to_string(),
28 => "ft8".to_string(),
29 => "ft9".to_string(),
30 => "ft10".to_string(),
31 => "ft11".to_string(),
_ => format!("f{}", f_idx),
}
}
pub fn format_imm(&self, imm: i64) -> String {
format!("{}", imm)
}
pub fn format_mem_offset(&self, offset: i64, base_reg: u16) -> String {
format!("{}({})", self.format_imm(offset), self.format_reg(base_reg))
}
pub const VR_BASE: u16 = 3100;
pub fn format_vreg_name(&self, v_idx: u16) -> String {
if v_idx > 31 {
return format!("%v{}", v_idx);
}
match v_idx {
0 => "v0".to_string(),
1 => "v1".to_string(),
2 => "v2".to_string(),
3 => "v3".to_string(),
4 => "v4".to_string(),
5 => "v5".to_string(),
6 => "v6".to_string(),
7 => "v7".to_string(),
8 => "v8".to_string(),
9 => "v9".to_string(),
10 => "v10".to_string(),
11 => "v11".to_string(),
12 => "v12".to_string(),
13 => "v13".to_string(),
14 => "v14".to_string(),
15 => "v15".to_string(),
16 => "v16".to_string(),
17 => "v17".to_string(),
18 => "v18".to_string(),
19 => "v19".to_string(),
20 => "v20".to_string(),
21 => "v21".to_string(),
22 => "v22".to_string(),
23 => "v23".to_string(),
24 => "v24".to_string(),
25 => "v25".to_string(),
26 => "v26".to_string(),
27 => "v27".to_string(),
28 => "v28".to_string(),
29 => "v29".to_string(),
30 => "v30".to_string(),
31 => "v31".to_string(),
_ => format!("v{}", v_idx),
}
}
pub fn print_li(&mut self, rd: u16, imm: i64) {
if imm >= -2048 && imm <= 2047 {
self.output
.push_str(&format!("\tli\t{}, {}\n", self.format_reg(rd), imm));
} else {
let upper = ((imm as u64 + 0x800) >> 12) as i64;
let lower = imm - (upper << 12);
if upper != 0 {
self.output
.push_str(&format!("\tlui\t{}, {}\n", self.format_reg(rd), upper));
}
if lower != 0 || upper == 0 {
if upper != 0 {
self.output.push_str(&format!(
"\taddi\t{}, {}, {}\n",
self.format_reg(rd),
self.format_reg(rd),
lower
));
} else {
self.output.push_str(&format!(
"\taddi\t{}, zero, {}\n",
self.format_reg(rd),
lower
));
}
}
}
}
pub fn print_la(&mut self, rd: u16, symbol: &str) {
self.output
.push_str(&format!("\tla\t{}, {}\n", self.format_reg(rd), symbol));
}
pub fn print_call(&mut self, symbol: &str) {
self.output.push_str(&format!("\tcall\t{}\n", symbol));
}
pub fn print_tail(&mut self, symbol: &str) {
self.output.push_str(&format!("\ttail\t{}\n", symbol));
}
pub fn print_nop(&mut self) {
self.output.push_str("\tnop\n");
}
pub fn print_mv(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tmv\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_not(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tnot\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_neg(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tneg\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_seqz(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tseqz\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_snez(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tsnez\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_sltz(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tsltz\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn print_sgtz(&mut self, rd: u16, rs: u16) {
self.output.push_str(&format!(
"\tsgtz\t{}, {}\n",
self.format_reg(rd),
self.format_reg(rs)
));
}
pub fn emit_directive_text(&mut self) {
self.output.push_str("\t.text\n");
}
pub fn emit_directive_data(&mut self) {
self.output.push_str("\t.data\n");
}
pub fn emit_directive_bss(&mut self) {
self.output.push_str("\t.bss\n");
}
pub fn emit_directive_section(&mut self, name: &str, flags: &str) {
self.output
.push_str(&format!("\t.section\t{}, \"{}\"\n", name, flags));
}
pub fn emit_directive_globl(&mut self, symbol: &str) {
self.output.push_str(&format!("\t.globl\t{}\n", symbol));
}
pub fn emit_directive_local(&mut self, symbol: &str) {
self.output.push_str(&format!("\t.local\t{}\n", symbol));
}
pub fn emit_directive_weak(&mut self, symbol: &str) {
self.output.push_str(&format!("\t.weak\t{}\n", symbol));
}
pub fn emit_directive_align(&mut self, alignment: u32) {
self.output.push_str(&format!("\t.align\t{}\n", alignment));
}
pub fn emit_directive_size(&mut self, symbol: &str, expr: &str) {
self.output
.push_str(&format!("\t.size\t{}, {}\n", symbol, expr));
}
pub fn emit_directive_type(&mut self, symbol: &str, sym_type: &str) {
self.output
.push_str(&format!("\t.type\t{}, @{}\n", symbol, sym_type));
}
pub fn emit_directive_file(&mut self, filename: &str) {
self.output
.push_str(&format!("\t.file\t\"{}\"\n", filename));
}
pub fn emit_directive_ident(&mut self, ident: &str) {
self.output.push_str(&format!("\t.ident\t\"{}\"\n", ident));
}
pub fn emit_directive_option(&mut self, option: &str) {
self.output.push_str(&format!("\t.option\t{}\n", option));
}
pub fn emit_directive_attribute(&mut self, tag: u32, value: &str) {
self.output
.push_str(&format!("\t.attribute\t{}, {}\n", tag, value));
}
pub fn emit_cfi_startproc(&mut self) {
self.output.push_str("\t.cfi_startproc\n");
}
pub fn emit_cfi_endproc(&mut self) {
self.output.push_str("\t.cfi_endproc\n");
}
pub fn emit_cfi_def_cfa(&mut self, reg: u16, offset: i64) {
self.output.push_str(&format!(
"\t.cfi_def_cfa\t{}, {}\n",
self.format_reg(reg),
offset
));
}
pub fn emit_cfi_def_cfa_offset(&mut self, offset: i64) {
self.output
.push_str(&format!("\t.cfi_def_cfa_offset\t{}\n", offset));
}
pub fn emit_cfi_offset(&mut self, reg: u16, offset: i64) {
self.output.push_str(&format!(
"\t.cfi_offset\t{}, {}\n",
self.format_reg(reg),
offset
));
}
pub fn emit_cfi_restore(&mut self, reg: u16) {
self.output
.push_str(&format!("\t.cfi_restore\t{}\n", self.format_reg(reg)));
}
pub fn emit_cfi_remember_state(&mut self) {
self.output.push_str("\t.cfi_remember_state\n");
}
pub fn emit_cfi_restore_state(&mut self) {
self.output.push_str("\t.cfi_restore_state\n");
}
pub fn emit_cfi_return_column(&mut self, reg: u16) {
self.output
.push_str(&format!("\t.cfi_return_column\t{}\n", self.format_reg(reg)));
}
pub fn emit_cfi_prologue(&mut self, frame_size: i64, has_fp: bool) {
self.emit_cfi_startproc();
if frame_size > 0 {
self.emit_cfi_def_cfa_offset(frame_size);
} else {
self.emit_cfi_def_cfa(SP, 0);
}
self.emit_cfi_return_column(RA);
if frame_size > 0 {
let reg_width: i64 = if self.is_64bit { 8 } else { 4 };
self.emit_cfi_offset(RA, -(reg_width));
}
if has_fp {
let reg_width: i64 = if self.is_64bit { 8 } else { 4 };
self.emit_cfi_offset(S0, -(2 * reg_width));
self.emit_cfi_def_cfa(S0, 0);
}
}
pub fn emit_cfi_epilogue(&mut self, has_fp: bool) {
if has_fp {
self.emit_cfi_restore(S0);
}
self.emit_cfi_restore(RA);
self.emit_cfi_def_cfa(SP, 0);
self.emit_cfi_endproc();
}
pub fn get_mnemonic_zba(&self, opcode: u32) -> String {
match opcode {
221 => "sh1add".to_string(),
222 => "sh2add".to_string(),
223 => "sh3add".to_string(),
_ => "INVALID".to_string(),
}
}
pub fn get_mnemonic_zbb(&self, opcode: u32) -> String {
match opcode {
224 => "andn".to_string(),
225 => "orn".to_string(),
226 => "xnor".to_string(),
227 => "clz".to_string(),
228 => "ctz".to_string(),
229 => "cpop".to_string(),
230 => "max".to_string(),
231 => "maxu".to_string(),
232 => "min".to_string(),
233 => "minu".to_string(),
234 => "sext.b".to_string(),
235 => "sext.h".to_string(),
236 => "zext.h".to_string(),
237 => "rol".to_string(),
238 => "ror".to_string(),
239 => "rori".to_string(),
240 => "rev8".to_string(),
241 => "orc.b".to_string(),
_ => "INVALID".to_string(),
}
}
pub fn get_mnemonic_zbs(&self, opcode: u32) -> String {
match opcode {
242 => "bclr".to_string(),
243 => "bset".to_string(),
244 => "binv".to_string(),
245 => "bext".to_string(),
_ => "INVALID".to_string(),
}
}
pub fn get_mnemonic_vector(&self, opcode: u32) -> String {
match opcode {
250 => "vadd.vv".to_string(),
251 => "vsub.vv".to_string(),
252 => "vmul.vv".to_string(),
253 => "vdiv.vv".to_string(),
254 => "vand.vv".to_string(),
255 => "vor.vv".to_string(),
256 => "vxor.vv".to_string(),
257 => "vsll.vv".to_string(),
258 => "vsrl.vv".to_string(),
259 => "vsra.vv".to_string(),
260 => "vmseq.vv".to_string(),
261 => "vmsne.vv".to_string(),
262 => "vmslt.vv".to_string(),
263 => "vmsle.vv".to_string(),
264 => "vmsgt.vv".to_string(),
265 => "vmsge.vv".to_string(),
266 => "vmin.vv".to_string(),
267 => "vmax.vv".to_string(),
268 => "vmerge.vvm".to_string(),
269 => "vmv.v.v".to_string(),
270 => "vmv.v.x".to_string(),
271 => "vmv.v.i".to_string(),
272 => "vfmv.f.s".to_string(),
273 => "vfmv.s.f".to_string(),
274 => "vfmacc.vv".to_string(),
275 => "vfnmacc.vv".to_string(),
276 => "vfmsac.vv".to_string(),
277 => "vfnmsac.vv".to_string(),
278 => "vfmadd.vv".to_string(),
279 => "vfnmadd.vv".to_string(),
280 => "vfmsub.vv".to_string(),
281 => "vfnmsub.vv".to_string(),
282 => "vfadd.vv".to_string(),
283 => "vfsub.vv".to_string(),
284 => "vfmul.vv".to_string(),
285 => "vfdiv.vv".to_string(),
286 => "vfsqrt.v".to_string(),
287 => "vle8.v".to_string(),
288 => "vle16.v".to_string(),
289 => "vle32.v".to_string(),
290 => "vle64.v".to_string(),
291 => "vse8.v".to_string(),
292 => "vse16.v".to_string(),
293 => "vse32.v".to_string(),
294 => "vse64.v".to_string(),
295 => "vlse.v".to_string(),
296 => "vsse.v".to_string(),
297 => "vluxei.v".to_string(),
298 => "vsuxei.v".to_string(),
299 => "vloxei.v".to_string(),
300 => "vsoxei.v".to_string(),
_ => "INVALID".to_string(),
}
}
pub fn print_vector_instruction(&mut self, opcode: u32, vd: u16, vs1: u16, vs2: u16) {
let mnemonic = self.get_mnemonic_vector(opcode);
if mnemonic == "INVALID" {
return;
}
self.output.push_str(&format!(
"\t{}\t{}, {}, {}\n",
mnemonic,
self.format_vreg_name(vd),
self.format_vreg_name(vs1),
self.format_vreg_name(vs2)
));
}
pub fn emit_directive_zero(&mut self, count: u32) {
self.output.push_str(&format!("\t.zero\t{}\n", count));
}
pub fn emit_directive_byte(&mut self, values: &[u8]) {
let vals: Vec<String> = values.iter().map(|b| b.to_string()).collect();
self.output
.push_str(&format!("\t.byte\t{}\n", vals.join(", ")));
}
pub fn emit_directive_short(&mut self, values: &[u16]) {
let vals: Vec<String> = values.iter().map(|v| v.to_string()).collect();
self.output
.push_str(&format!("\t.short\t{}\n", vals.join(", ")));
}
pub fn emit_directive_long(&mut self, values: &[u32]) {
let vals: Vec<String> = values.iter().map(|v| v.to_string()).collect();
self.output
.push_str(&format!("\t.long\t{}\n", vals.join(", ")));
}
pub fn emit_directive_quad(&mut self, values: &[u64]) {
let vals: Vec<String> = values.iter().map(|v| v.to_string()).collect();
self.output
.push_str(&format!("\t.quad\t{}\n", vals.join(", ")));
}
pub fn emit_directive_string(&mut self, text: &str) {
self.output.push_str(&format!("\t.string\t\"{}\"\n", text));
}
pub fn emit_directive_asciz(&mut self, text: &str) {
self.output.push_str(&format!("\t.asciz\t\"{}\"\n", text));
}
pub fn emit_cfi_sections(&mut self) {
self.output.push_str("\t.cfi_sections\t.eh_frame\n");
}
pub fn emit_cfi_lsda(&mut self, encoding: u8, value: &str) {
self.output
.push_str(&format!("\t.cfi_lsda\t{}, {}\n", encoding, value));
}
pub fn emit_cfi_personality(&mut self, encoding: u8, symbol: &str) {
self.output
.push_str(&format!("\t.cfi_personality\t{}, {}\n", encoding, symbol));
}
pub fn emit_cfi_escape(&mut self, bytes: &[u8]) {
let hex: Vec<String> = bytes.iter().map(|b| format!("0x{:02x}", b)).collect();
self.output
.push_str(&format!("\t.cfi_escape\t{}\n", hex.join(", ")));
}
pub fn emit_cfi_val_offset(&mut self, reg: u16, offset: i64) {
self.output.push_str(&format!(
"\t.cfi_val_offset\t{}, {}\n",
self.format_reg(reg),
offset
));
}
}
impl Default for RiscVAsmPrinter {
fn default() -> Self {
Self::new(true)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_test_mf(name: &str) -> MachineFunction {
let mut mf = MachineFunction::new(name);
let bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
mf.blocks.push(bb);
mf
}
#[test]
fn test_new_rv64() {
let printer = RiscVAsmPrinter::new(true);
assert!(printer.is_64bit);
assert!(printer.output.is_empty());
}
#[test]
fn test_new_rv32() {
let printer = RiscVAsmPrinter::new(false);
assert!(!printer.is_64bit);
}
#[test]
fn test_default_is_rv64() {
let printer = RiscVAsmPrinter::default();
assert!(printer.is_64bit);
}
#[test]
fn test_print_function_rv64() {
let mut printer = RiscVAsmPrinter::new(true);
let mf = make_test_mf("my_func");
printer.print_function(&mf);
let out = printer.output;
assert!(out.contains(".globl\tmy_func"));
assert!(out.contains(".type\tmy_func, @function"));
assert!(out.contains("my_func:"));
assert!(out.contains(".size\tmy_func, .-my_func"));
}
#[test]
fn test_print_prologue() {
let mut printer = RiscVAsmPrinter::new(true);
let mf = MachineFunction::new("test_fn");
printer.print_prologue(&mf);
let out = printer.output;
assert!(out.contains(".globl\ttest_fn"));
assert!(out.contains(".type\ttest_fn, @function"));
assert!(out.contains("test_fn:"));
}
#[test]
fn test_print_epilogue() {
let mut printer = RiscVAsmPrinter::new(true);
let mf = MachineFunction::new("test_fn");
printer.print_epilogue(&mf);
let out = printer.output;
assert!(out.contains(".size\ttest_fn, .-test_fn"));
}
#[test]
fn test_print_basic_block_with_label() {
let mut printer = RiscVAsmPrinter::new(true);
let bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
printer.print_basic_block(&bb);
let out = printer.output;
assert!(out.contains(".LBB_entry:"));
}
#[test]
fn test_print_basic_block_empty_name() {
let mut printer = RiscVAsmPrinter::new(true);
let bb = MachineBasicBlock {
name: String::new(),
instructions: Vec::new(),
successors: Vec::new(),
};
printer.print_basic_block(&bb);
let out = printer.output;
assert!(!out.contains(".LBB_"));
}
#[test]
fn test_print_instruction_add() {
let mut printer = RiscVAsmPrinter::new(true);
let mut mi = MachineInstr::new(RiscVOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(A0 as u32));
mi.operands.push(MachineOperand::PhysReg(A1 as u32));
mi.operands.push(MachineOperand::PhysReg(A2 as u32));
printer.print_instruction(&mi);
let out = printer.output;
assert!(out.contains("add"));
assert!(out.contains("a0"));
assert!(out.contains("a1"));
assert!(out.contains("a2"));
}
#[test]
fn test_print_instruction_ret() {
let mut printer = RiscVAsmPrinter::new(true);
let mi = MachineInstr::new(RiscVOpcode::RET as u32);
printer.print_instruction(&mi);
let out = printer.output;
assert!(out.contains("ret"));
}
#[test]
fn test_print_instruction_ld() {
let mut printer = RiscVAsmPrinter::new(true);
let mut mi = MachineInstr::new(RiscVOpcode::LD as u32);
mi.operands.push(MachineOperand::PhysReg(RA as u32));
mi.operands.push(MachineOperand::PhysReg(SP as u32));
mi.push_imm(24);
printer.print_instruction(&mi);
let out = printer.output;
assert!(out.contains("ld"));
assert!(out.contains("ra"));
assert!(out.contains("sp"));
assert!(out.contains("24"));
}
#[test]
fn test_print_instruction_sd() {
let mut printer = RiscVAsmPrinter::new(true);
let mut mi = MachineInstr::new(RiscVOpcode::SD as u32);
mi.operands.push(MachineOperand::PhysReg(S0 as u32));
mi.operands.push(MachineOperand::PhysReg(SP as u32));
mi.push_imm(16);
printer.print_instruction(&mi);
let out = printer.output;
assert!(out.contains("sd"));
assert!(out.contains("s0"));
assert!(out.contains("16(sp)"));
}
#[test]
fn test_print_operand_phys_reg() {
let printer = RiscVAsmPrinter::new(true);
let op = MachineOperand::PhysReg(A0 as u32);
let s = printer.print_operand(&op);
assert_eq!(s, "a0");
}
#[test]
fn test_print_operand_imm() {
let printer = RiscVAsmPrinter::new(true);
let op = MachineOperand::Imm(-32);
let s = printer.print_operand(&op);
assert_eq!(s, "-32");
}
#[test]
fn test_print_operand_label() {
let printer = RiscVAsmPrinter::new(true);
let op = MachineOperand::Label("else".to_string());
let s = printer.print_operand(&op);
assert_eq!(s, ".LBB_else");
}
#[test]
fn test_print_operand_vreg() {
let printer = RiscVAsmPrinter::new(true);
let op = MachineOperand::Reg(42);
let s = printer.print_operand(&op);
assert!(s.contains("vreg42") || s.contains("%vreg42"));
}
#[test]
fn test_print_operand_global() {
let printer = RiscVAsmPrinter::new(true);
let op = MachineOperand::Global("printf".to_string());
let s = printer.print_operand(&op);
assert_eq!(s, "printf");
}
#[test]
fn test_format_reg_x0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE), "zero");
}
#[test]
fn test_format_reg_ra() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 1), "ra");
}
#[test]
fn test_format_reg_sp() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 2), "sp");
}
#[test]
fn test_format_reg_a0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 10), "a0");
}
#[test]
fn test_format_reg_s0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 8), "s0");
}
#[test]
fn test_format_reg_t0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 5), "t0");
}
#[test]
fn test_format_reg_t6() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(GPR_BASE + 31), "t6");
}
#[test]
fn test_format_fp_reg_ft0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(FPR_BASE), "ft0");
}
#[test]
fn test_format_fp_reg_fa0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(FPR_BASE + 10), "fa0");
}
#[test]
fn test_format_fp_reg_fs0() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(FPR_BASE + 8), "fs0");
}
#[test]
fn test_format_fp_reg_ft11() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_reg(FPR_BASE + 31), "ft11");
}
#[test]
fn test_format_imm_positive() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_imm(42), "42");
}
#[test]
fn test_format_imm_negative() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_imm(-32), "-32");
}
#[test]
fn test_format_imm_zero() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_imm(0), "0");
}
#[test]
fn test_format_mem_offset_zero() {
let printer = RiscVAsmPrinter::new(true);
let s = printer.format_mem_offset(0, GPR_BASE + 2); assert_eq!(s, "0(sp)");
}
#[test]
fn test_format_mem_offset_nonzero() {
let printer = RiscVAsmPrinter::new(true);
let s = printer.format_mem_offset(24, GPR_BASE + 2); assert_eq!(s, "24(sp)");
}
#[test]
fn test_get_mnemonic_add() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::ADD as u32), "add");
}
#[test]
fn test_get_mnemonic_sub() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::SUB as u32), "sub");
}
#[test]
fn test_get_mnemonic_mul() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::MUL as u32), "mul");
}
#[test]
fn test_get_mnemonic_ld() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::LD as u32), "ld");
}
#[test]
fn test_get_mnemonic_sd() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::SD as u32), "sd");
}
#[test]
fn test_get_mnemonic_ret() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::RET as u32), "ret");
}
#[test]
fn test_get_mnemonic_jal() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::JAL as u32), "jal");
}
#[test]
fn test_get_mnemonic_addi() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(RiscVOpcode::ADDI as u32), "addi");
}
#[test]
fn test_get_mnemonic_invalid() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic(0xFFFF), "INVALID");
}
#[test]
fn test_print_module_rv64() {
let mut printer = RiscVAsmPrinter::new(true);
let mut module = Module::new("test_mod");
module.source_filename = "test.S".to_string();
printer.print_module(&module);
let out = printer.output;
assert!(out.contains(".attribute\t4, 5"));
assert!(out.contains("rv64i2p1"));
assert!(out.contains(".file\t\"test.S\""));
assert!(out.contains(".text"));
}
#[test]
fn test_print_module_rv32() {
let mut printer = RiscVAsmPrinter::new(false);
let module = Module::new("test_mod");
printer.print_module(&module);
let out = printer.output;
assert!(out.contains(".attribute\t4, 4"));
assert!(out.contains("rv32i2p1"));
}
#[test]
fn test_print_function_full_pipeline() {
let mut printer = RiscVAsmPrinter::new(true);
let mut mf = MachineFunction::new("full_func");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32);
addi.operands.push(MachineOperand::PhysReg(SP as u32));
addi.operands.push(MachineOperand::PhysReg(SP as u32));
addi.push_imm(-32);
bb.instructions.push(addi);
let mut sd = MachineInstr::new(RiscVOpcode::SD as u32);
sd.operands.push(MachineOperand::PhysReg(RA as u32));
sd.operands.push(MachineOperand::PhysReg(SP as u32));
sd.push_imm(24);
bb.instructions.push(sd);
let ret = MachineInstr::new(RiscVOpcode::RET as u32);
bb.instructions.push(ret);
mf.blocks.push(bb);
printer.print_function(&mf);
let out = printer.output;
assert!(out.contains(".globl\tfull_func"));
assert!(out.contains("full_func:"));
assert!(out.contains("addi"));
assert!(out.contains("-32"));
assert!(out.contains("sd"));
assert!(out.contains("24(sp)"));
assert!(out.contains("ret"));
assert!(out.contains(".size\tfull_func, .-full_func"));
}
#[test]
fn test_print_instruction_unknown_opcode_skipped() {
let mut printer = RiscVAsmPrinter::new(true);
let mi = MachineInstr::new(0xFFFF);
printer.print_instruction(&mi);
let out = printer.output;
assert!(out.is_empty());
}
#[test]
fn test_print_li_small() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_li(A0, 42);
let out = printer.output;
assert!(out.contains("li"));
assert!(out.contains("42"));
}
#[test]
fn test_print_li_large() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_li(T0, 0x12345);
let out = printer.output;
assert!(out.contains("lui"));
}
#[test]
fn test_print_mv() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_mv(A0, A1);
let out = printer.output;
assert!(out.contains("mv"));
assert!(out.contains("a0"));
assert!(out.contains("a1"));
}
#[test]
fn test_print_nop() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_nop();
assert!(printer.output.contains("nop"));
}
#[test]
fn test_print_not() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_not(T0, T1);
assert!(printer.output.contains("not"));
}
#[test]
fn test_print_neg() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_neg(T0, T1);
assert!(printer.output.contains("neg"));
}
#[test]
fn test_print_seqz() {
let mut printer = RiscVAsmPrinter::new(true);
printer.print_seqz(A0, A1);
assert!(printer.output.contains("seqz"));
}
#[test]
fn test_emit_directive_text() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_directive_text();
assert!(printer.output.contains(".text"));
}
#[test]
fn test_emit_directive_globl() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_directive_globl("main");
assert!(printer.output.contains(".globl"));
assert!(printer.output.contains("main"));
}
#[test]
fn test_emit_directive_align() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_directive_align(4);
assert!(printer.output.contains(".align"));
assert!(printer.output.contains("4"));
}
#[test]
fn test_emit_directive_size() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_directive_size("main", ".-main");
assert!(printer.output.contains(".size"));
}
#[test]
fn test_emit_cfi_prologue() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_cfi_prologue(32, true);
let out = printer.output;
assert!(out.contains(".cfi_startproc"));
assert!(out.contains(".cfi_def_cfa_offset"));
assert!(out.contains(".cfi_return_column"));
}
#[test]
fn test_emit_cfi_epilogue() {
let mut printer = RiscVAsmPrinter::new(true);
printer.emit_cfi_epilogue(true);
let out = printer.output;
assert!(out.contains(".cfi_restore"));
assert!(out.contains(".cfi_endproc"));
}
#[test]
fn test_format_vreg_name() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.format_vreg_name(0), "v0");
assert_eq!(printer.format_vreg_name(15), "v15");
assert_eq!(printer.format_vreg_name(31), "v31");
}
#[test]
fn test_get_mnemonic_vector() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic_vector(250), "vadd.vv");
assert_eq!(printer.get_mnemonic_vector(282), "vfadd.vv");
assert_eq!(printer.get_mnemonic_vector(999), "INVALID");
}
#[test]
fn test_get_mnemonic_zba() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic_zba(221), "sh1add");
assert_eq!(printer.get_mnemonic_zba(999), "INVALID");
}
#[test]
fn test_get_mnemonic_zbb() {
let printer = RiscVAsmPrinter::new(true);
assert_eq!(printer.get_mnemonic_zbb(224), "andn");
assert_eq!(printer.get_mnemonic_zbb(227), "clz");
assert_eq!(printer.get_mnemonic_zbb(229), "cpop");
}
}