use super::sparc_instr_info::SparcInstrInfo;
#[cfg(test)]
use super::sparc_instr_info::SparcOpcode;
#[cfg(test)]
use super::sparc_register_info::*;
use super::sparc_register_info::{SPARC_FPR_BASE, SPARC_GPR_BASE};
use crate::codegen::*;
use crate::module::Module;
pub struct SparcAsmPrinter {
pub output: String,
pub is_64bit: bool,
pub instr_info: SparcInstrInfo,
}
impl SparcAsmPrinter {
pub fn new(is_64bit: bool) -> Self {
SparcAsmPrinter {
output: String::new(),
is_64bit,
instr_info: SparcInstrInfo::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.section\t\".text\",#alloc,#execinstr\n");
} else {
self.output
.push_str("\t.section\t\".text\",#alloc,#execinstr\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.global\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(),
"ld" | "ldub"
| "lduh"
| "ldsb"
| "ldsh"
| "ldd"
| "ldstub"
| "ldsw"
| "ldx"
| "st"
| "stb"
| "sth"
| "std"
| "stx"
);
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_or_reg = matches!(
last,
MachineOperand::Imm(_) | MachineOperand::PhysReg(_) | MachineOperand::Reg(_)
);
if base_is_reg && last_is_imm_or_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),
MachineOperand::PhysReg(r) => self.format_reg(*r as u16),
MachineOperand::Reg(vr) => format!("%vreg{}", vr),
_ => unreachable!(),
};
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);
}
}
}
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(),
}
}
fn format_reg(&self, reg_id: u16) -> String {
match reg_id {
0 => "%g0".into(),
1 => "%g1".into(),
2 => "%g2".into(),
3 => "%g3".into(),
4 => "%g4".into(),
5 => "%g5".into(),
6 => "%g6".into(),
7 => "%g7".into(),
8 => "%o0".into(),
9 => "%o1".into(),
10 => "%o2".into(),
11 => "%o3".into(),
12 => "%o4".into(),
13 => "%o5".into(),
14 => "%sp".into(),
15 => "%o7".into(),
16 => "%l0".into(),
17 => "%l1".into(),
18 => "%l2".into(),
19 => "%l3".into(),
20 => "%l4".into(),
21 => "%l5".into(),
22 => "%l6".into(),
23 => "%l7".into(),
24 => "%i0".into(),
25 => "%i1".into(),
26 => "%i2".into(),
27 => "%i3".into(),
28 => "%i4".into(),
29 => "%i5".into(),
30 => "%fp".into(),
31 => "%i7".into(),
32..=63 => format!("%f{}", reg_id - 32),
64 => "%fsr".into(),
65 => "%y".into(),
66 => "%ccr".into(),
_ => format!("%r{}", reg_id),
}
}
fn format_imm(&self, imm: i64) -> String {
if imm >= -4096 && imm <= 4095 {
format!("{}", imm)
} else {
format!("{}", imm)
}
}
pub fn get_mnemonic(&self, opcode: u32) -> String {
self.get_mnemonic_fallback(opcode)
}
fn get_mnemonic_fallback(&self, opcode: u32) -> String {
match opcode {
0 => "add".to_string(),
1 => "addcc".to_string(),
2 => "addc".to_string(),
3 => "addccc".to_string(),
4 => "sub".to_string(),
5 => "subcc".to_string(),
6 => "subc".to_string(),
7 => "subccc".to_string(),
8 => "mulx".to_string(),
9 => "sdivx".to_string(),
10 => "udivx".to_string(),
11 => "smul".to_string(),
12 => "umul".to_string(),
13 => "sdiv".to_string(),
14 => "udiv".to_string(),
15 => "taddcc".to_string(),
16 => "tsubcc".to_string(),
17 => "taddcctv".to_string(),
18 => "tsubcctv".to_string(),
20 => "and".to_string(),
21 => "andcc".to_string(),
22 => "andn".to_string(),
23 => "andncc".to_string(),
24 => "or".to_string(),
25 => "orcc".to_string(),
26 => "orn".to_string(),
27 => "orncc".to_string(),
28 => "xor".to_string(),
29 => "xorcc".to_string(),
30 => "xnor".to_string(),
31 => "xnorcc".to_string(),
40 => "sll".to_string(),
41 => "srl".to_string(),
42 => "sra".to_string(),
43 => "sllx".to_string(),
44 => "srlx".to_string(),
45 => "srax".to_string(),
50 => "ld".to_string(),
51 => "ldub".to_string(),
52 => "lduh".to_string(),
53 => "ldsb".to_string(),
54 => "ldsh".to_string(),
55 => "ldd".to_string(),
56 => "ldstub".to_string(),
57 => "ldsw".to_string(),
58 => "ldx".to_string(),
60 => "st".to_string(),
61 => "stb".to_string(),
62 => "sth".to_string(),
63 => "std".to_string(),
64 => "stx".to_string(),
70 => "ba".to_string(),
71 => "bn".to_string(),
72 => "bne".to_string(),
73 => "be".to_string(),
74 => "bg".to_string(),
75 => "ble".to_string(),
76 => "bge".to_string(),
77 => "bl".to_string(),
78 => "bgu".to_string(),
79 => "bleu".to_string(),
80 => "bcc".to_string(),
81 => "bcs".to_string(),
82 => "bpos".to_string(),
83 => "bneg".to_string(),
84 => "bvc".to_string(),
85 => "bvs".to_string(),
90 => "fba".to_string(),
91 => "fbn".to_string(),
92 => "fbu".to_string(),
93 => "fbg".to_string(),
94 => "fbl".to_string(),
95 => "fbge".to_string(),
96 => "fble".to_string(),
97 => "fbe".to_string(),
98 => "fbne".to_string(),
99 => "fbo".to_string(),
110 => "brz".to_string(),
111 => "brnz".to_string(),
112 => "brlez".to_string(),
113 => "brlz".to_string(),
114 => "brgez".to_string(),
115 => "brgz".to_string(),
120 => "call".to_string(),
121 => "jmpl".to_string(),
122 => "ret".to_string(),
123 => "retl".to_string(),
124 => "save".to_string(),
125 => "restore".to_string(),
130 => "sethi".to_string(),
131 => "nop".to_string(),
132 => "rd".to_string(),
133 => "wr".to_string(),
134 => "rdpsr".to_string(),
135 => "wrpsr".to_string(),
136 => "rdwim".to_string(),
137 => "wrwim".to_string(),
138 => "rdtbr".to_string(),
139 => "wrtbr".to_string(),
140 => "flush".to_string(),
141 => "stbar".to_string(),
142 => "membar".to_string(),
143 => "iflush".to_string(),
150 => "fadds".to_string(),
151 => "faddd".to_string(),
152 => "fsubs".to_string(),
153 => "fsubd".to_string(),
154 => "fmuls".to_string(),
155 => "fmuld".to_string(),
156 => "fdivs".to_string(),
157 => "fdivd".to_string(),
158 => "fsqrts".to_string(),
159 => "fsqrtd".to_string(),
160 => "fitos".to_string(),
161 => "fitod".to_string(),
162 => "fstoi".to_string(),
163 => "fdtoi".to_string(),
164 => "fmovs".to_string(),
165 => "fmovd".to_string(),
166 => "fnegs".to_string(),
167 => "fnegd".to_string(),
168 => "fabss".to_string(),
169 => "fabsd".to_string(),
170 => "fcmps".to_string(),
171 => "fcmpd".to_string(),
172 => "fcmpes".to_string(),
173 => "fcmped".to_string(),
178 => "fstod".to_string(),
179 => "fdtos".to_string(),
180 => "fstox".to_string(),
181 => "fdtox".to_string(),
182 => "fxtos".to_string(),
183 => "fxtod".to_string(),
_ => "INVALID".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mnemonic_lookup() {
let printer = SparcAsmPrinter::new(false);
assert_eq!(printer.get_mnemonic(0), "add");
assert_eq!(printer.get_mnemonic(4), "sub");
assert_eq!(printer.get_mnemonic(24), "or");
assert_eq!(printer.get_mnemonic(28), "xor");
assert_eq!(printer.get_mnemonic(70), "ba");
assert_eq!(printer.get_mnemonic(120), "call");
assert_eq!(printer.get_mnemonic(122), "ret");
assert_eq!(printer.get_mnemonic(124), "save");
assert_eq!(printer.get_mnemonic(125), "restore");
assert_eq!(printer.get_mnemonic(150), "fadds");
assert_eq!(printer.get_mnemonic(170), "fcmps");
}
#[test]
fn test_format_reg() {
let printer = SparcAsmPrinter::new(false);
assert_eq!(printer.format_reg(0), "%g0");
assert_eq!(printer.format_reg(8), "%o0");
assert_eq!(printer.format_reg(14), "%sp");
assert_eq!(printer.format_reg(30), "%fp");
assert_eq!(printer.format_reg(32), "%f0");
assert_eq!(printer.format_reg(63), "%f31");
}
#[test]
fn test_print_operand() {
let printer = SparcAsmPrinter::new(false);
assert_eq!(printer.print_operand(&MachineOperand::Imm(42)), "42");
assert_eq!(
printer.print_operand(&MachineOperand::Label("entry".into())),
".LBB_entry"
);
assert_eq!(
printer.print_operand(&MachineOperand::Global("main".into())),
"main"
);
}
#[test]
fn test_print_nop() {
let mut printer = SparcAsmPrinter::new(false);
let mi = MachineInstr::new(131); printer.print_instruction(&mi);
assert!(printer.output.contains("nop"));
}
#[test]
fn test_print_ret() {
let mut printer = SparcAsmPrinter::new(false);
let mi = MachineInstr::new(122); printer.print_instruction(&mi);
assert!(printer.output.contains("ret"));
}
}