use super::hexagon_instr_info::HexagonInstrInfo;
use crate::codegen::*;
use crate::module::Module;
pub struct HexagonAsmPrinter {
pub output: String,
pub is_64bit: bool,
pub instr_info: HexagonInstrInfo,
}
impl HexagonAsmPrinter {
pub fn new(_is_64bit: bool) -> Self {
HexagonAsmPrinter {
output: String::new(),
is_64bit: false,
instr_info: HexagonInstrInfo::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) {
self.output.push_str("\t.text\n");
if !module.source_filename.is_empty() {
self.output
.push_str(&format!("\t.file\t\"{}\"\n", module.source_filename));
}
}
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));
self.output.push_str("\t{\n");
self.output
.push_str(&format!("\t\tallocframe(r29, #0):raw\n"));
}
pub fn print_epilogue(&mut self, mf: &MachineFunction) {
let name = &mf.name;
self.output.push_str("\t\tdeallocframe\n");
self.output.push_str("\t}\n");
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));
}
self.output.push_str("\t{\n");
for mi in &bb.instructions {
self.print_instruction(mi);
}
self.output.push_str("\t}\n");
}
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;
let is_mem_op = matches!(
mnemonic.as_str(),
"ldw" | "ldb" | "ldh" | "ldd" | "stw" | "stb" | "sth" | "std"
);
if len >= 3 && is_mem_op {
let second_last = &mi.operands[len - 2];
let last = &mi.operands[len - 1];
let base_is_reg = matches!(
second_last,
MachineOperand::PhysReg(_) | MachineOperand::Reg(_)
);
if base_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 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) => format!("#{}", imm),
_ => String::new(),
};
op_strs.push(format!("{}({})", base_name, offset));
}
}
if !skip_last_two {
for op in &mi.operands {
let s = self.print_operand(op);
if !s.is_empty() {
op_strs.push(s);
}
}
}
let prefix = if mnemonic.starts_with("if") {
format!("{} ", mnemonic)
} else {
String::new()
};
if op_strs.is_empty() {
self.output
.push_str(&format!("\t\t{} = {}\n", prefix.trim(), mnemonic));
} else {
let ops = op_strs.join(", ");
self.output
.push_str(&format!("\t\t{} = {}({})\n", prefix.trim(), mnemonic, ops));
}
}
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) => format!("#{}", imm),
MachineOperand::Label(label) => format!(".LBB_{}", label),
MachineOperand::Global(name) => name.clone(),
}
}
fn format_reg(&self, reg_id: u16) -> String {
if reg_id < 32 {
format!("r{}", reg_id)
} else if reg_id >= 32 && reg_id < 36 {
format!("p{}", reg_id - 32)
} else if reg_id >= 40 && reg_id < 72 {
format!("v{}", reg_id - 40)
} else {
format!("r{}", reg_id)
}
}
pub fn get_mnemonic(&self, opcode: u32) -> String {
match opcode {
0 => "add".to_string(),
1 => "sub".to_string(),
2 => "and".to_string(),
3 => "or".to_string(),
4 => "xor".to_string(),
5 => "neg".to_string(),
6 => "not".to_string(),
7 => "combine".to_string(),
8 => "extract".to_string(),
9 => "insert".to_string(),
10 => "sxt".to_string(),
11 => "zxt".to_string(),
12 => "abs".to_string(),
20 => "lsl".to_string(),
21 => "lsr".to_string(),
22 => "asr".to_string(),
23 => "ror".to_string(),
24 => "rol".to_string(),
30 => "mpy".to_string(),
31 => "mpyu".to_string(),
32 => "mpyi".to_string(),
33 => "mpyih".to_string(),
34 => "mpyh".to_string(),
50 => "ldw".to_string(),
51 => "ldb".to_string(),
52 => "ldh".to_string(),
53 => "ldd".to_string(),
60 => "stw".to_string(),
61 => "stb".to_string(),
62 => "sth".to_string(),
63 => "std".to_string(),
64 => "memw".to_string(),
65 => "memd".to_string(),
66 => "allocframe".to_string(),
67 => "deallocframe".to_string(),
80 => "jump".to_string(),
81 => "jumpr".to_string(),
82 => "jumpr".to_string(),
83 => "call".to_string(),
84 => "callr".to_string(),
85 => "jmp".to_string(),
86 => "jmpr".to_string(),
100 => "cmpeq".to_string(),
101 => "cmpgt".to_string(),
102 => "cmpgtu".to_string(),
103 => "cmplt".to_string(),
104 => "cmpltu".to_string(),
110 => "mux".to_string(),
120 => "vadd".to_string(),
121 => "vsub".to_string(),
122 => "vmpy".to_string(),
123 => "vmpa".to_string(),
124 => "vshuff".to_string(),
125 => "vshuffe".to_string(),
126 => "vshuffo".to_string(),
140 => "nop".to_string(),
141 => "li".to_string(),
142 => "mov".to_string(),
_ => "INVALID".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mnemonic_lookup() {
let printer = HexagonAsmPrinter::new(false);
assert_eq!(printer.get_mnemonic(0), "add");
assert_eq!(printer.get_mnemonic(1), "sub");
assert_eq!(printer.get_mnemonic(30), "mpy");
assert_eq!(printer.get_mnemonic(80), "jump");
assert_eq!(printer.get_mnemonic(83), "call");
assert_eq!(printer.get_mnemonic(100), "cmpeq");
assert_eq!(printer.get_mnemonic(120), "vadd");
assert_eq!(printer.get_mnemonic(140), "nop");
assert_eq!(printer.get_mnemonic(142), "mov");
}
#[test]
fn test_format_reg() {
let printer = HexagonAsmPrinter::new(false);
assert_eq!(printer.format_reg(0), "r0");
assert_eq!(printer.format_reg(29), "r29");
assert_eq!(printer.format_reg(31), "r31");
assert_eq!(printer.format_reg(32), "p0");
assert_eq!(printer.format_reg(40), "v0");
}
#[test]
fn test_print_nop() {
let mut printer = HexagonAsmPrinter::new(false);
let mi = MachineInstr::new(140);
printer.print_instruction(&mi);
assert!(printer.output.contains("nop"));
}
}