use super::lanai_instr_info::LanaiInstrInfo;
use super::lanai_register_info::LanaiRegisterInfo;
use crate::codegen::*;
use crate::module::Module;
pub struct LanaiAsmPrinter {
pub output: String,
pub instr_info: LanaiInstrInfo,
}
impl LanaiAsmPrinter {
pub fn new() -> Self {
LanaiAsmPrinter {
output: String::with_capacity(4096),
instr_info: LanaiInstrInfo::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 !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_by_opcode(mi.opcode);
if mnemonic.is_empty() || mnemonic == "INVALID" {
return;
}
if mi.operands.is_empty() {
let mn = mnemonic.as_str();
match mn {
"nop" => {
self.output.push_str("\tnop\n");
return;
}
"ret" => {
self.output.push_str("\tret\n");
return;
}
_ => {
self.output.push_str(&format!("\t{}\n", mn));
return;
}
}
}
let mut op_strs: Vec<String> = Vec::new();
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!("r{}", vr)
}
MachineOperand::PhysReg(reg) => LanaiRegisterInfo::get_asm_name(*reg as u16),
MachineOperand::Imm(imm) => format!("{}", imm),
MachineOperand::Label(label) => format!(".LBB_{}", label),
MachineOperand::Global(name) => name.clone(),
}
}
fn get_mnemonic_by_opcode(&self, opcode_val: u32) -> String {
match opcode_val {
22000 => "add".into(),
22001 => "addc".into(),
22002 => "sub".into(),
22003 => "subb".into(),
22004 => "and".into(),
22005 => "or".into(),
22006 => "xor".into(),
22007 => "sh".into(),
22008 => "sel".into(),
22010 => "ld".into(),
22011 => "st".into(),
22012 => "ldh".into(),
22013 => "sth".into(),
22014 => "ldb".into(),
22015 => "stb".into(),
22020 => "br".into(),
22021 => "brcc".into(),
22022 => "brc".into(),
22023 => "sa".into(),
22024 => "bt".into(),
22030 => "popc".into(),
22031 => "leadz".into(),
22032 => "trailz".into(),
22040 => "add_i".into(),
22041 => "and_i".into(),
22042 => "or_i".into(),
22043 => "xor_i".into(),
22044 => "sh_i".into(),
22045 => "ld_i".into(),
22046 => "ldh_i".into(),
22047 => "ldb_i".into(),
22048 => "st_i".into(),
22049 => "sth_i".into(),
22050 => "stb_i".into(),
22060 => "nop".into(),
22061 => "mov".into(),
22062 => "li".into(),
22063 => "j".into(),
22064 => "jal".into(),
22065 => "jalr".into(),
22066 => "ret".into(),
_ => "INVALID".into(),
}
}
pub fn clear(&mut self) {
self.output.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_printer_new() {
let printer = LanaiAsmPrinter::new();
assert!(printer.output.is_empty());
}
#[test]
fn test_print_operand_imm() {
let printer = LanaiAsmPrinter::new();
assert_eq!(printer.print_operand(&MachineOperand::Imm(42)), "42");
}
#[test]
fn test_print_operand_label() {
let printer = LanaiAsmPrinter::new();
assert_eq!(
printer.print_operand(&MachineOperand::Label("loop".to_string())),
".LBB_loop"
);
}
#[test]
fn test_get_mnemonic() {
let printer = LanaiAsmPrinter::new();
assert_eq!(printer.get_mnemonic_by_opcode(22000), "add");
assert_eq!(printer.get_mnemonic_by_opcode(22066), "ret");
assert_eq!(printer.get_mnemonic_by_opcode(22063), "j");
assert_eq!(printer.get_mnemonic_by_opcode(99999), "INVALID");
}
#[test]
fn test_print_ret_instruction() {
let mut printer = LanaiAsmPrinter::new();
let mi = MachineInstr::new(22066); printer.print_instruction(&mi);
assert!(printer.output.contains("ret\n"));
}
}