use super::ppc_instr_info::PpcInstrInfo;
#[cfg(test)]
use super::ppc_instr_info::PpcOpcode;
#[cfg(test)]
use super::ppc_register_info::*;
use super::ppc_register_info::{PPC_FPR_BASE, PPC_GPR_BASE};
use crate::codegen::*;
use crate::module::Module;
pub struct PpcAsmPrinter {
pub output: String,
pub is_64bit: bool,
pub instr_info: PpcInstrInfo,
}
impl PpcAsmPrinter {
pub fn new(is_64bit: bool) -> Self {
PpcAsmPrinter {
output: String::new(),
is_64bit,
instr_info: PpcInstrInfo::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(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(),
"lwz"
| "stw"
| "lbz"
| "stb"
| "lhz"
| "sth"
| "lha"
| "ld"
| "std"
| "lwzu"
| "stwu"
| "ldu"
| "stdu"
| "lmw"
| "stmw"
);
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(_)
);
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));
}
}
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 {
match opcode {
0 => "add".into(),
1 => "addi".into(),
2 => "subf".into(),
3 => "mullw".into(),
4 => "mulld".into(),
5 => "divw".into(),
6 => "divd".into(),
7 => "neg".into(),
8 => "and".into(),
9 => "andc".into(),
10 => "or".into(),
11 => "orc".into(),
12 => "xor".into(),
13 => "nand".into(),
14 => "nor".into(),
15 => "eqv".into(),
16 => "extsb".into(),
17 => "extsh".into(),
18 => "extsw".into(),
19 => "cntlzw".into(),
20 => "cntlzd".into(),
21 => "slw".into(),
22 => "srw".into(),
23 => "sraw".into(),
24 => "sld".into(),
25 => "srd".into(),
26 => "srad".into(),
27 => "rldicl".into(),
28 => "rldicr".into(),
29 => "rldimi".into(),
30 => "lwz".into(),
31 => "lwzu".into(),
32 => "stw".into(),
33 => "stwu".into(),
34 => "ld".into(),
35 => "ldu".into(),
36 => "std".into(),
37 => "stdu".into(),
38 => "lhz".into(),
39 => "lha".into(),
40 => "lbz".into(),
41 => "sth".into(),
42 => "stb".into(),
43 => "lmw".into(),
44 => "stmw".into(),
45 => "b".into(),
46 => "bl".into(),
47 => "ba".into(),
48 => "bla".into(),
49 => "bc".into(),
50 => "bcl".into(),
51 => "bctr".into(),
52 => "bclr".into(),
53 => "bcctr".into(),
54 => "cmpw".into(),
55 => "cmpd".into(),
56 => "cmpwi".into(),
57 => "cmpdi".into(),
69 => "fadd".into(),
70 => "fsub".into(),
71 => "fmul".into(),
72 => "fdiv".into(),
73 => "fmadd".into(),
80 => "ld".into(),
81 => "std".into(),
86 => "lvx".into(),
87 => "stvx".into(),
88 => "vaddfp".into(),
89 => "vmaddfp".into(),
93 => "nop".into(),
94 => "li".into(),
95 => "lis".into(),
96 => "mr".into(),
97 => "mtlr".into(),
98 => "mflr".into(),
99 => "mtctr".into(),
100 => "mfctr".into(),
101 => "cmpi".into(),
102 => "cmpli".into(),
103 => "bdnz".into(),
104 => "bdz".into(),
_ => "INVALID".into(),
}
}
pub fn format_reg(&self, reg_id: u16) -> String {
if reg_id >= PPC_GPR_BASE && reg_id < PPC_GPR_BASE + 32 {
format!("r{}", reg_id - PPC_GPR_BASE)
} else if reg_id >= PPC_FPR_BASE && reg_id < PPC_FPR_BASE + 32 {
format!("f{}", reg_id - PPC_FPR_BASE)
} else if reg_id >= 5150 && reg_id < 5182 {
format!("v{}", reg_id - 5150)
} else if reg_id == 5100 {
"lr".into()
} else if reg_id == 5101 {
"ctr".into()
} else if reg_id == 5102 {
"xer".into()
} else if reg_id >= 5110 && reg_id <= 5117 {
format!("cr{}", reg_id - 5110)
} else {
format!("r{}", reg_id)
}
}
pub fn format_imm(&self, imm: i64) -> String {
if imm >= -32768 && imm <= 32767 {
format!("{}", imm)
} else {
format!("{}", imm)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_printer() {
let printer = PpcAsmPrinter::new(false);
assert!(!printer.is_64bit);
assert!(printer.output.is_empty());
}
#[test]
fn test_format_gpr() {
let printer = PpcAsmPrinter::new(false);
assert_eq!(printer.format_reg(R0), "r0");
assert_eq!(printer.format_reg(SP), "r1");
assert_eq!(printer.format_reg(R3), "r3");
assert_eq!(printer.format_reg(R31), "r31");
}
#[test]
fn test_format_fpr() {
let printer = PpcAsmPrinter::new(false);
assert_eq!(printer.format_reg(F0), "f0");
assert_eq!(printer.format_reg(F31), "f31");
}
#[test]
fn test_format_special() {
let printer = PpcAsmPrinter::new(false);
assert_eq!(printer.format_reg(LR), "lr");
assert_eq!(printer.format_reg(CTR), "ctr");
}
#[test]
fn test_get_mnemonic() {
let printer = PpcAsmPrinter::new(false);
assert_eq!(printer.get_mnemonic(PpcOpcode::ADD as u32), "add");
assert_eq!(printer.get_mnemonic(PpcOpcode::LWZ as u32), "lwz");
assert_eq!(printer.get_mnemonic(PpcOpcode::STW as u32), "stw");
assert_eq!(printer.get_mnemonic(PpcOpcode::B as u32), "b");
assert_eq!(printer.get_mnemonic(PpcOpcode::BL as u32), "bl");
assert_eq!(printer.get_mnemonic(PpcOpcode::BCLR as u32), "bclr");
assert_eq!(printer.get_mnemonic(PpcOpcode::NOP as u32), "nop");
assert_eq!(printer.get_mnemonic(PpcOpcode::LI as u32), "li");
assert_eq!(printer.get_mnemonic(PpcOpcode::MR as u32), "mr");
}
#[test]
fn test_print_prologue() {
let mut printer = PpcAsmPrinter::new(false);
let mf = MachineFunction::new("main");
printer.print_prologue(&mf);
assert!(printer.output.contains(".globl\tmain"));
assert!(printer.output.contains("main:\n"));
}
#[test]
fn test_print_add_instruction() {
let mut printer = PpcAsmPrinter::new(false);
let mut mi = MachineInstr::new(PpcOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(R3 as u32));
mi.operands.push(MachineOperand::PhysReg(R4 as u32));
mi.operands.push(MachineOperand::PhysReg(R5 as u32));
printer.print_instruction(&mi);
assert!(printer.output.contains("add\tr3, r4, r5"));
}
#[test]
fn test_print_lwz_instruction() {
let mut printer = PpcAsmPrinter::new(false);
let mut mi = MachineInstr::new(PpcOpcode::LWZ as u32);
mi.operands.push(MachineOperand::PhysReg(R3 as u32));
mi.operands.push(MachineOperand::PhysReg(SP as u32));
mi.push_imm(0);
printer.print_instruction(&mi);
assert!(printer.output.contains("lwz\tr3, 0(r1)"));
}
#[test]
fn test_print_stw_instruction() {
let mut printer = PpcAsmPrinter::new(false);
let mut mi = MachineInstr::new(PpcOpcode::STW as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(SP as u32));
mi.push_imm(8);
printer.print_instruction(&mi);
assert!(printer.output.contains("stw\tr0, 8(r1)"));
}
#[test]
fn test_print_blr() {
let mut printer = PpcAsmPrinter::new(false);
let mut mi = MachineInstr::new(PpcOpcode::BCLR as u32);
mi.operands.push(MachineOperand::PhysReg(CR0 as u32));
printer.print_instruction(&mi);
assert!(printer.output.contains("bclr\tcr0"));
}
#[test]
fn test_print_function() {
let mut printer = PpcAsmPrinter::new(false);
let mut mf = MachineFunction::new("test_func");
let mut bb = MachineBasicBlock {
name: "entry".into(),
instructions: Vec::new(),
successors: Vec::new(),
};
let mut mi1 = MachineInstr::new(PpcOpcode::MFLR as u32);
mi1.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi1);
let mut mi2 = MachineInstr::new(PpcOpcode::STW as u32);
mi2.operands.push(MachineOperand::PhysReg(R0 as u32));
mi2.operands.push(MachineOperand::PhysReg(SP as u32));
mi2.push_imm(8);
bb.instructions.push(mi2);
let mut mi3 = MachineInstr::new(PpcOpcode::LI as u32);
mi3.operands.push(MachineOperand::PhysReg(R3 as u32));
mi3.push_imm(42);
bb.instructions.push(mi3);
let mut mi4 = MachineInstr::new(PpcOpcode::BCLR as u32);
mi4.operands.push(MachineOperand::PhysReg(CR0 as u32));
bb.instructions.push(mi4);
mf.push_block(bb);
printer.print_function(&mf);
let output = &printer.output;
assert!(output.contains("test_func:"));
assert!(output.contains("mflr\tr0"));
assert!(output.contains("stw\tr0, 8(r1)"));
assert!(output.contains("li\tr3, 42"));
assert!(output.contains("bclr\tcr0"));
}
}