use crate::arm::arm_instr_info::ArmInstrInfo;
use crate::arm::arm_instr_info::ArmOpcode;
use crate::arm::arm_register_info::*;
use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand};
use crate::module::Module;
pub struct ArmAsmPrinter {
pub output: String,
pub is_64bit: bool,
pub instr_info: ArmInstrInfo,
}
impl ArmAsmPrinter {
pub fn new(is_64bit: bool) -> Self {
Self {
output: String::new(),
is_64bit,
instr_info: ArmInstrInfo::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.arch ");
if self.is_64bit {
self.output.push_str("armv8-a\n");
} else {
self.output.push_str("armv7-a\n");
}
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));
if self.is_64bit {
self.output
.push_str(&format!("\t.type\t{}, @function\n", name));
} else {
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();
for op in &mi.operands {
let s = self.print_operand(op);
if !s.is_empty() {
op_strs.push(s);
}
}
let instr_line = if op_strs.is_empty() {
format!("\t{}\n", mnemonic)
} else {
format!("\t{}\t{}\n", mnemonic, op_strs.join(", "))
};
self.output.push_str(&instr_line);
}
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 => "add".to_string(),
1 => "sub".to_string(),
2 => "mul".to_string(),
3 => "sdiv".to_string(),
4 => "udiv".to_string(),
5 => "and".to_string(),
6 => "orr".to_string(),
7 => "eor".to_string(),
8 => "orn".to_string(),
9 => "bic".to_string(),
10 => "lsl".to_string(),
11 => "lsr".to_string(),
12 => "asr".to_string(),
13 => "ror".to_string(),
14 => "adds".to_string(),
15 => "subs".to_string(),
16 => "madd".to_string(),
17 => "msub".to_string(),
18 => "smull".to_string(),
19 => "umull".to_string(),
20 => "smaddl".to_string(),
21 => "umaddl".to_string(),
22 => "smsubl".to_string(),
23 => "umsubl".to_string(),
24 => "mov".to_string(),
25 => "movz".to_string(),
26 => "movk".to_string(),
27 => "movn".to_string(),
28 => "movi".to_string(),
29 => "adr".to_string(),
30 => "adrp".to_string(),
31 => "ldr".to_string(),
32 => "str".to_string(),
33 => "ldp".to_string(),
34 => "stp".to_string(),
35 => "ldrb".to_string(),
36 => "strb".to_string(),
37 => "ldrh".to_string(),
38 => "strh".to_string(),
39 => "ldrsb".to_string(),
40 => "ldrsh".to_string(),
41 => "ldrsw".to_string(),
42 => "ldur".to_string(),
43 => "stur".to_string(),
44 => "ldurb".to_string(),
45 => "sturb".to_string(),
46 => "ldurh".to_string(),
47 => "sturh".to_string(),
48 => "ldursw".to_string(),
49 => "ldr".to_string(), 50 => "ldnp".to_string(),
51 => "stnp".to_string(),
52 => "ldtr".to_string(),
53 => "sttr".to_string(),
54 => "prfm".to_string(),
55 => "b".to_string(),
56 => "bl".to_string(),
57 => "br".to_string(),
58 => "blr".to_string(),
59 => "ret".to_string(),
60 => "cbz".to_string(),
61 => "cbnz".to_string(),
62 => "tbz".to_string(),
63 => "tbnz".to_string(),
64 => "b.cond".to_string(),
65 => "cmp".to_string(),
66 => "cmn".to_string(),
67 => "tst".to_string(),
68 => "csel".to_string(),
69 => "csinc".to_string(),
70 => "csinv".to_string(),
71 => "csneg".to_string(),
72 => "cset".to_string(),
73 => "csetm".to_string(),
74 => "ccmp".to_string(),
75 => "ccmn".to_string(),
76 => "sbfm".to_string(),
77 => "ubfm".to_string(),
78 => "sbfx".to_string(),
79 => "ubfx".to_string(),
80 => "bfm".to_string(),
81 => "bfi".to_string(),
82 => "bfxil".to_string(),
83 => "sxtb".to_string(),
84 => "sxth".to_string(),
85 => "sxtw".to_string(),
86 => "uxtb".to_string(),
87 => "uxth".to_string(),
88 => "uxtw".to_string(),
89 => "extr".to_string(),
90 => "clz".to_string(),
91 => "cls".to_string(),
92 => "rbit".to_string(),
93 => "rev".to_string(),
94 => "rev16".to_string(),
95 => "rev32".to_string(),
96 => "svc".to_string(),
97 => "hvc".to_string(),
98 => "smc".to_string(),
99 => "brk".to_string(),
100 => "nop".to_string(),
101 => "hint".to_string(),
102 => "isb".to_string(),
103 => "dsb".to_string(),
104 => "dmb".to_string(),
105 => "msr".to_string(),
106 => "mrs".to_string(),
107 => "sys".to_string(),
108 => "sysl".to_string(),
109 => "add".to_string(), 110 => "sub".to_string(), 111 => "mul".to_string(), 112 => "mla".to_string(),
113 => "mls".to_string(),
114 => "fadd".to_string(),
115 => "fsub".to_string(),
116 => "fmul".to_string(),
117 => "fdiv".to_string(),
118 => "fabs".to_string(),
119 => "fneg".to_string(),
120 => "fsqrt".to_string(),
121 => "fcsel".to_string(),
122 => "fcmp".to_string(),
123 => "fccmp".to_string(),
124 => "fcvt".to_string(),
125 => "scvtf".to_string(),
126 => "ucvtf".to_string(),
127 => "fcvtzs".to_string(),
128 => "fcvtzu".to_string(),
129..=200 => "simd".to_string(),
307 => "add".to_string(),
308 => "sub".to_string(),
309 => "mul".to_string(),
310 => "and".to_string(),
311 => "orr".to_string(),
312 => "eor".to_string(),
313 => "mov".to_string(),
314 => "movw".to_string(),
315 => "movt".to_string(),
316 => "mvn".to_string(),
317 => "cmp".to_string(),
318 => "cmn".to_string(),
319 => "tst".to_string(),
320 => "teq".to_string(),
321 => "b".to_string(),
322 => "bl".to_string(),
323 => "bx".to_string(),
324 => "blx".to_string(),
325 => "blx".to_string(), 326 => "ldr".to_string(),
327 => "str".to_string(),
328 => "ldm".to_string(),
329 => "stm".to_string(),
330 => "ldmia".to_string(),
331 => "stmia".to_string(),
332 => "ldmib".to_string(),
333 => "stmib".to_string(),
334 => "ldmda".to_string(),
335 => "stmda".to_string(),
336 => "ldmdb".to_string(),
337 => "stmdb".to_string(),
338 => "push".to_string(),
339 => "pop".to_string(),
340..=380 => "arm32".to_string(),
381 => "vadd.f32".to_string(), 382 => "vsub.f32".to_string(), 383 => "vmul.f32".to_string(), 384 => "vdiv.f32".to_string(), 385..=410 => "vfp".to_string(),
411 => "vcmp".to_string(), 412..=469 => "neon".to_string(),
470..=509 => "thumb".to_string(),
1000 => "ldxr".to_string(),
1001 => "ldxrb".to_string(),
1002 => "ldxrh".to_string(),
1003 => "stxr".to_string(),
1004 => "stxrb".to_string(),
1005 => "stxrh".to_string(),
1006 => "ldaxr".to_string(),
1007 => "stlxr".to_string(),
1010 => "mvn".to_string(),
1011 => "neg".to_string(),
1012 => "negs".to_string(),
1013 => "ngc".to_string(),
1014 => "ngcs".to_string(),
1015 => "adc".to_string(),
1016 => "adcs".to_string(),
1017 => "sbc".to_string(),
1018 => "sbcs".to_string(),
1019 => "bics".to_string(),
1020 => "eon".to_string(),
1021 => "ands".to_string(),
1022 => "ubfiz".to_string(),
1023 => "sbfiz".to_string(),
1024 => "lslv".to_string(),
1025 => "lsrv".to_string(),
1026 => "asrv".to_string(),
1027 => "rorv".to_string(),
1028 => "crc32b".to_string(),
1029 => "crc32h".to_string(),
1030 => "crc32w".to_string(),
1031 => "crc32x".to_string(),
1032 => "crc32cb".to_string(),
1033 => "crc32ch".to_string(),
1034 => "crc32cw".to_string(),
1035 => "crc32cx".to_string(),
1040 => "fcmpe".to_string(),
1041 => "fccmpe".to_string(),
1042 => "fmadd".to_string(),
1043 => "fmsub".to_string(),
1044 => "fnmadd".to_string(),
1045 => "fnmsub".to_string(),
1050 => "addv".to_string(),
1051 => "mvni".to_string(),
1052 => "shl".to_string(),
1053 => "fadd".to_string(),
1054 => "fsub".to_string(),
1055 => "fmul".to_string(),
1056 => "fdiv".to_string(),
1057 => "fcmla".to_string(),
1058 => "fcadd".to_string(),
1060 => "fcvtn".to_string(),
1061 => "fcvtl".to_string(),
1070 => "ld1".to_string(),
1071 => "st1".to_string(),
1072 => "ldr".to_string(),
1073 => "str".to_string(),
_ => {
"INVALID".to_string()
}
}
}
pub fn format_reg(&self, reg: u16) -> String {
if self.is_64bit {
self.format_reg_aarch64(reg)
} else {
self.format_reg_arm32(reg)
}
}
fn format_reg_aarch64(&self, reg: u16) -> String {
match reg {
r @ 2000..=2030 => {
let idx = r - 2000;
format!("x{}", idx)
}
2031 => "sp".to_string(),
2032 => "xzr".to_string(),
2033 => "pc".to_string(),
r @ 2040..=2070 => {
let idx = r - 2040;
format!("w{}", idx)
}
2071 => "wsp".to_string(),
2072 => "wzr".to_string(),
r @ 2200..=2231 => {
let idx = r - 2200;
format!("v{}", idx)
}
r @ 2250..=2281 => {
let idx = r - 2250;
format!("q{}", idx)
}
r @ 2300..=2331 => {
let idx = r - 2300;
format!("d{}", idx)
}
r @ 2350..=2381 => {
let idx = r - 2350;
format!("s{}", idx)
}
r @ 2400..=2431 => {
let idx = r - 2400;
format!("h{}", idx)
}
r @ 2450..=2481 => {
let idx = r - 2450;
format!("b{}", idx)
}
2117 => "nzcv".to_string(),
_ => format!("reg<{}>", reg),
}
}
fn format_reg_arm32(&self, reg: u16) -> String {
match reg {
r @ 2100..=2115 => {
let idx = r - 2100;
match idx {
13 => "sp".to_string(),
14 => "lr".to_string(),
15 => "pc".to_string(),
_ => format!("r{}", idx),
}
}
2116 => "cpsr".to_string(),
2117 => "nzcv".to_string(),
r @ 2500..=2531 => {
let idx = r - 2500;
format!("d{}", idx)
}
r @ 2550..=2581 => {
let idx = r - 2550;
format!("s{}", idx)
}
_ => format!("reg<{}>", reg),
}
}
pub fn format_imm(&self, imm: i64) -> String {
format!("#{}", imm)
}
pub fn format_mem_offset(&self, base: u16, offset: i64) -> String {
let base_str = self.format_reg(base);
if offset == 0 {
format!("[{}]", base_str)
} else {
format!("[{}, #{}]", base_str, offset)
}
}
pub fn format_mem_pre_index(&self, base: u16, offset: i64) -> String {
let base_str = self.format_reg(base);
format!("[{}, #{}]!", base_str, offset)
}
pub fn format_mem_post_index(&self, base: u16, offset: i64) -> String {
let base_str = self.format_reg(base);
format!("[{}], #{}", base_str, offset)
}
pub fn format_cond(&self, cond: u8) -> &str {
match cond & 0xF {
0b0000 => "eq",
0b0001 => "ne",
0b0010 => "cs",
0b0011 => "cc",
0b0100 => "mi",
0b0101 => "pl",
0b0110 => "vs",
0b0111 => "vc",
0b1000 => "hi",
0b1001 => "ls",
0b1010 => "ge",
0b1011 => "lt",
0b1100 => "gt",
0b1101 => "le",
0b1110 => "al",
0b1111 => "nv",
_ => "al",
}
}
pub fn format_cond_extended(&self, cond: u8) -> &str {
match cond & 0xF {
0b0000 => "eq",
0b0001 => "ne",
0b0010 => "hs",
0b0011 => "lo",
0b0100 => "mi",
0b0101 => "pl",
0b0110 => "vs",
0b0111 => "vc",
0b1000 => "hi",
0b1001 => "ls",
0b1010 => "ge",
0b1011 => "lt",
0b1100 => "gt",
0b1101 => "le",
0b1110 => "al",
0b1111 => "nv",
_ => "al",
}
}
pub fn format_shift(&self, shift_type: u8, amount: u8) -> String {
let shift = match shift_type {
0b00 => "lsl",
0b01 => "lsr",
0b10 => "asr",
0b11 => "ror",
_ => "lsl",
};
if amount == 0 {
shift.to_string()
} else {
format!("{} #{}", shift, amount)
}
}
pub fn format_msl(&self, amount: u8) -> String {
if amount == 0 {
"msl".to_string()
} else {
format!("msl #{}", amount)
}
}
pub fn format_extend(&self, ext_type: u8, shift: u8) -> String {
let ext = match ext_type {
0b000 => "uxtb",
0b001 => "uxth",
0b010 => "uxtw",
0b011 => "uxtx",
0b100 => "sxtb",
0b101 => "sxth",
0b110 => "sxtw",
0b111 => "sxtx",
_ => "uxtb",
};
if shift == 0 {
ext.to_string()
} else {
format!("{} #{}", ext, shift)
}
}
pub fn format_lsl_extend(&self, shift: u8) -> String {
if shift == 0 {
"uxtw".to_string()
} else {
format!("uxtw #{}", shift)
}
}
pub fn format_sys_reg(&self, op0: u8, op1: u8, crn: u8, crm: u8, op2: u8) -> String {
let key = ((op0 as u64) << 20)
| ((op1 as u64) << 16)
| ((crn as u64) << 12)
| ((crm as u64) << 4)
| (op2 as u64);
match key {
0x30E001 => "cntpct_el0".to_string(),
0x30E002 => "cntvct_el0".to_string(),
0x30E020 => "cntp_ctl_el0".to_string(),
0x30E021 => "cntp_cval_el0".to_string(),
0x30E022 => "cntv_ctl_el0".to_string(),
0x30E023 => "cntv_cval_el0".to_string(),
0x30E010 => "cntfrq_el0".to_string(),
0x33D002 => "tpidr_el0".to_string(),
0x30D003 => "tpidrro_el0".to_string(),
0x30D004 => "tpidr_el1".to_string(),
0x30D005 => "tpidr_el2".to_string(),
0x30D006 => "tpidr_el3".to_string(),
0x301000 => "sctlr_el1".to_string(),
0x301001 => "actlr_el1".to_string(),
0x303000 => "ttbr0_el1".to_string(),
0x303001 => "ttbr1_el1".to_string(),
0x302000 => "tcr_el1".to_string(),
0x30C000 => "vbar_el1".to_string(),
0x30C001 => "esr_el1".to_string(),
0x30C002 => "far_el1".to_string(),
0x30C003 => "par_el1".to_string(),
0x200000 => "mdccsr_el0".to_string(),
0x200001 => "dbgdtr_el0".to_string(),
0x200002 => "dbgdtrrx_el0".to_string(),
0x3090C0 => "pmcr_el0".to_string(),
0x3090C1 => "pmcntenset_el0".to_string(),
0x3090C2 => "pmcntenclr_el0".to_string(),
0x3090D0 => "pmccntr_el0".to_string(),
0x300000 => "midr_el1".to_string(),
0x300001 => "mpidr_el1".to_string(),
0x300002 => "revidr_el1".to_string(),
0x300005 => "id_pfr0_el1".to_string(),
0x300006 => "id_pfr1_el1".to_string(),
0x300007 => "id_dfr0_el1".to_string(),
0x300010 => "id_afr0_el1".to_string(),
0x300012 => "id_mmfr0_el1".to_string(),
0x300013 => "id_mmfr1_el1".to_string(),
0x300014 => "id_mmfr2_el1".to_string(),
0x300015 => "id_mmfr3_el1".to_string(),
0x300016 => "id_mmfr4_el1".to_string(),
0x300020 => "id_isar0_el1".to_string(),
0x300021 => "id_isar1_el1".to_string(),
0x300022 => "id_isar2_el1".to_string(),
0x300023 => "id_isar3_el1".to_string(),
0x300024 => "id_isar4_el1".to_string(),
0x300025 => "id_isar5_el1".to_string(),
0x300026 => "id_isar6_el1".to_string(),
0x300030 => "id_mmfr5_el1".to_string(),
0x302021 => "apiakeylo_el1".to_string(),
0x302022 => "apiakeyhi_el1".to_string(),
0x302023 => "apibkeylo_el1".to_string(),
0x302024 => "apibkeyhi_el1".to_string(),
0x302025 => "apdakeylo_el1".to_string(),
0x302026 => "apdakeyhi_el1".to_string(),
0x302027 => "apdbkeylo_el1".to_string(),
0x302028 => "apdbkeyhi_el1".to_string(),
0x302029 => "apgakeylo_el1".to_string(),
_ => format!("s{}_{}_c{}_c{}_{}", op0, op1, crn, crm, op2),
}
}
pub fn format_simd_arrangement(&self, q: bool, size: u8) -> String {
let lanes = if q { 16 } else { 8 };
let (lanes, suffix) = match size {
0b00 => (lanes, "b"), 0b01 => (lanes / 2, "h"), 0b10 => (lanes / 4, "s"), 0b11 => (lanes / 8, "d"), _ => (0, "?"),
};
format!(".{}{}", lanes, suffix)
}
pub fn format_simd_reg(&self, reg: u16, q: bool, size: u8) -> String {
let base = self.format_reg(reg);
let arr = self.format_simd_arrangement(q, size);
format!("{}{}", base, arr)
}
pub fn format_simd_element(&self, reg: u16, size: u8, index: u8) -> String {
let base = self.format_reg(reg);
let suffix = match size {
0b00 => "b",
0b01 => "h",
0b10 => "s",
0b11 => "d",
_ => "?",
};
format!("{}.{}[{}]", base, suffix, index)
}
pub fn format_simd_struct(&self, reg: u16, count: u8, size: u8) -> String {
let base = self.format_reg(reg);
let suffix = match size {
0b00 => "b",
0b01 => "h",
0b10 => "s",
0b11 => "d",
_ => "?",
};
if count == 1 {
format!("{{{}.{}}}", base, suffix)
} else if count <= 4 {
let regs: Vec<String> = (0..count).map(|i| format!(".{}", suffix)).collect();
format!("{{{}{}}}", base, regs.join(", "))
} else {
format!("{{{}.{}}}", base, suffix)
}
}
pub fn emit_cpu_directive(&mut self, cpu_name: &str) {
self.output.push_str(&format!(
" .cpu {}
",
cpu_name
));
}
pub fn emit_arch_directive(&mut self, arch_name: &str) {
self.output.push_str(&format!(
" .arch {}
",
arch_name
));
}
pub fn emit_arch_extension_directive(&mut self, ext_name: &str) {
self.output.push_str(&format!(
" .arch_extension {}
",
ext_name
));
}
pub fn emit_fpu_directive(&mut self, fpu_name: &str) {
self.output.push_str(&format!(
" .fpu {}
",
fpu_name
));
}
pub fn emit_section_directive(&mut self, section: &str) {
self.output.push_str(&format!(
" .section {}
",
section
));
}
pub fn emit_text_section(&mut self) {
self.emit_section_directive(".text");
}
pub fn emit_data_section(&mut self) {
self.emit_section_directive(".data");
}
pub fn emit_bss_section(&mut self) {
self.emit_section_directive(".bss");
}
pub fn emit_rodata_section(&mut self) {
self.emit_section_directive(".rodata");
}
pub fn emit_align_directive(&mut self, alignment: u32) {
self.output.push_str(&format!(
" .align {}
",
alignment
));
}
pub fn emit_p2align_directive(
&mut self,
p2align: u32,
max_skip: Option<u32>,
fill: Option<u32>,
) {
if let (Some(ms), Some(f)) = (max_skip, fill) {
self.output.push_str(&format!(
" .p2align {}, {}, {}
",
p2align, ms, f
));
} else if let Some(ms) = max_skip {
self.output.push_str(&format!(
" .p2align {}, {}
",
p2align, ms
));
} else {
self.output.push_str(&format!(
" .p2align {}
",
p2align
));
}
}
pub fn emit_module_directives(&mut self, is_64bit: bool, cpu: &str, arch: &str) {
if is_64bit {
self.output.push_str(
" .arch armv8-a
",
);
} else {
self.output.push_str(
" .arch armv7-a
",
);
}
if !cpu.is_empty() && cpu != "generic" {
self.emit_cpu_directive(cpu);
}
if !arch.is_empty() {
self.emit_arch_directive(arch);
}
}
pub fn emit_function_label(&mut self, name: &str, align: Option<u32>) {
if let Some(a) = align {
self.emit_align_directive(a);
}
self.output.push_str(&format!(
"{}:
",
name
));
}
pub fn emit_local_label(&mut self, label: &str) {
self.output.push_str(&format!(
"{}:
",
label
));
}
pub fn emit_comment(&mut self, comment: &str) {
self.output.push_str(&format!(
" // {}
",
comment
));
}
pub fn emit_file_directive(&mut self, file: &str) {
self.output.push_str(&format!(
" .file \"{}\"
",
file
));
}
pub fn emit_loc_directive(&mut self, file_id: u32, line: u32, col: u32) {
self.output.push_str(&format!(
" .loc {} {} {}
",
file_id, line, col
));
}
pub fn emit_cfi_startproc(&mut self) {
self.output.push_str(
" .cfi_startproc
",
);
}
pub fn emit_cfi_endproc(&mut self) {
self.output.push_str(
" .cfi_endproc
",
);
}
pub fn emit_cfi_def_cfa_offset(&mut self, offset: i32) {
self.output.push_str(&format!(
" .cfi_def_cfa_offset {}
",
offset
));
}
pub fn emit_cfi_offset(&mut self, reg: u16, offset: i32) {
let reg_name = self.format_reg(reg);
self.output.push_str(&format!(
" .cfi_offset {}, {}
",
reg_name, offset
));
}
pub fn emit_cfi_def_cfa_register(&mut self, reg: u16) {
let reg_name = self.format_reg(reg);
self.output.push_str(&format!(
" .cfi_def_cfa_register {}
",
reg_name
));
}
pub fn emit_size_directive(&mut self, name: &str) {
self.output.push_str(&format!(
" .size {}, .-{}
",
name, name
));
}
pub fn emit_weak_directive(&mut self, name: &str) {
self.output.push_str(&format!(
" .weak {}
",
name
));
}
pub fn emit_hidden_directive(&mut self, name: &str) {
self.output.push_str(&format!(
" .hidden {}
",
name
));
}
}
impl Default for ArmAsmPrinter {
fn default() -> Self {
ArmAsmPrinter::new(true)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arm::arm_instr_info::ArmOpcode;
use crate::codegen::{MachineBasicBlock, MachineInstr, MachineOperand};
fn make_test_mf(name: &str, is_64bit: bool) -> MachineFunction {
let mut mf = MachineFunction::new(name);
let mut mbb = MachineBasicBlock {
name: "entry".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
if is_64bit {
let mut stp = MachineInstr::new(ArmOpcode::STP as u32);
stp.operands.push(MachineOperand::PhysReg(X29 as u32));
stp.operands.push(MachineOperand::PhysReg(X30 as u32));
stp.operands.push(MachineOperand::PhysReg(SP as u32));
stp.push_imm(-16);
mbb.instructions.push(stp);
let mut mov = MachineInstr::new(ArmOpcode::MOV as u32);
mov.operands.push(MachineOperand::PhysReg(X29 as u32));
mov.operands.push(MachineOperand::PhysReg(SP as u32));
mbb.instructions.push(mov);
let mut mov_imm = MachineInstr::new(ArmOpcode::MOV as u32);
mov_imm.operands.push(MachineOperand::PhysReg(W0 as u32));
mov_imm.push_imm(42);
mbb.instructions.push(mov_imm);
mbb.instructions
.push(MachineInstr::new(ArmOpcode::RET as u32));
} else {
let mut push = MachineInstr::new(ArmOpcode::ARM_PUSH as u32);
push.operands.push(MachineOperand::PhysReg(R11 as u32)); push.operands.push(MachineOperand::PhysReg(R14 as u32)); mbb.instructions.push(push);
let mut mov_imm = MachineInstr::new(ArmOpcode::ARM_MOV as u32);
mov_imm.operands.push(MachineOperand::PhysReg(R0 as u32));
mov_imm.push_imm(42);
mbb.instructions.push(mov_imm);
let mut pop = MachineInstr::new(ArmOpcode::ARM_POP as u32);
pop.operands.push(MachineOperand::PhysReg(R11 as u32)); pop.operands.push(MachineOperand::PhysReg(R15 as u32)); mbb.instructions.push(pop);
}
mf.push_block(mbb);
mf
}
#[test]
fn test_new_aarch64() {
let printer = ArmAsmPrinter::new(true);
assert!(printer.is_64bit);
assert!(printer.output.is_empty());
}
#[test]
fn test_new_arm32() {
let printer = ArmAsmPrinter::new(false);
assert!(!printer.is_64bit);
}
#[test]
fn test_default_is_aarch64() {
let printer = ArmAsmPrinter::default();
assert!(printer.is_64bit);
}
#[test]
fn test_print_function_aarch64() {
let mf = make_test_mf("test_func", true);
let mut printer = ArmAsmPrinter::new(true);
printer.print_function(&mf);
let output = &printer.output;
assert!(output.contains(".globl\ttest_func"));
assert!(output.contains(".type\ttest_func, @function"));
assert!(output.contains("test_func:"));
assert!(output.contains("stp"));
assert!(output.contains("mov"));
assert!(output.contains("ret"));
assert!(output.contains(".size\ttest_func, .-test_func"));
}
#[test]
fn test_print_function_arm32() {
let mf = make_test_mf("test_arm32", false);
let mut printer = ArmAsmPrinter::new(false);
printer.print_function(&mf);
let output = &printer.output;
assert!(output.contains(".globl\ttest_arm32"));
assert!(output.contains(".type\ttest_arm32, %function"));
assert!(output.contains("test_arm32:"));
assert!(output.contains("push"));
assert!(output.contains("pop"));
}
#[test]
fn test_print_basic_block_with_label() {
let mut mbb = MachineBasicBlock {
name: "loop".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
mbb.instructions
.push(MachineInstr::new(ArmOpcode::NOP as u32));
let mut printer = ArmAsmPrinter::new(true);
printer.print_basic_block(&mbb);
assert!(printer.output.contains(".LBB_loop:"));
assert!(printer.output.contains("nop"));
}
#[test]
fn test_print_instruction_add() {
let mut mi = MachineInstr::new(ArmOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(X0 as u32));
mi.operands.push(MachineOperand::PhysReg(X1 as u32));
mi.push_imm(5);
let mut printer = ArmAsmPrinter::new(true);
printer.print_instruction(&mi);
let output = &printer.output;
assert!(output.contains("add"));
assert!(output.contains("x0"));
assert!(output.contains("#5"));
}
#[test]
fn test_print_instruction_ret() {
let mi = MachineInstr::new(ArmOpcode::RET as u32);
let mut printer = ArmAsmPrinter::new(true);
printer.print_instruction(&mi);
assert!(printer.output.contains("ret"));
}
#[test]
fn test_print_instruction_ldr() {
let mut mi = MachineInstr::new(ArmOpcode::LDR as u32);
mi.operands.push(MachineOperand::PhysReg(X0 as u32));
mi.operands.push(MachineOperand::PhysReg(X1 as u32));
mi.push_imm(0);
let mut printer = ArmAsmPrinter::new(true);
printer.print_instruction(&mi);
assert!(printer.output.contains("ldr"));
}
#[test]
fn test_print_instruction_str() {
let mut mi = MachineInstr::new(ArmOpcode::STR as u32);
mi.operands.push(MachineOperand::PhysReg(X0 as u32));
mi.operands.push(MachineOperand::PhysReg(SP as u32));
mi.push_imm(8);
let mut printer = ArmAsmPrinter::new(true);
printer.print_instruction(&mi);
assert!(printer.output.contains("str"));
}
#[test]
fn test_print_operand_phys_reg() {
let printer = ArmAsmPrinter::new(true);
let op = MachineOperand::PhysReg(X0 as u32);
let result = printer.print_operand(&op);
assert_eq!(result, "x0");
}
#[test]
fn test_print_operand_imm() {
let printer = ArmAsmPrinter::new(true);
let op = MachineOperand::Imm(42);
let result = printer.print_operand(&op);
assert_eq!(result, "#42");
}
#[test]
fn test_print_operand_label() {
let printer = ArmAsmPrinter::new(true);
let op = MachineOperand::Label("loop".to_string());
let result = printer.print_operand(&op);
assert_eq!(result, ".LBB_loop");
}
#[test]
fn test_print_operand_vreg() {
let printer = ArmAsmPrinter::new(true);
let op = MachineOperand::Reg(3);
let result = printer.print_operand(&op);
assert_eq!(result, "vreg3");
}
#[test]
fn test_print_operand_global() {
let printer = ArmAsmPrinter::new(true);
let op = MachineOperand::Global("printf".to_string());
let result = printer.print_operand(&op);
assert_eq!(result, "printf");
}
#[test]
fn test_format_reg_x0() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_reg(X0), "x0");
}
#[test]
fn test_format_reg_x30() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_reg(X30), "x30");
}
#[test]
fn test_format_reg_sp() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_reg(SP), "sp");
}
#[test]
fn test_format_reg_xzr() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_reg(XZR), "xzr");
}
#[test]
fn test_format_reg_w0() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_reg(W0), "w0");
}
#[test]
fn test_format_reg_r0_arm32() {
let printer = ArmAsmPrinter::new(false);
assert_eq!(printer.format_reg(R0), "r0");
}
#[test]
fn test_format_reg_sp_arm32() {
let printer = ArmAsmPrinter::new(false);
assert_eq!(printer.format_reg(SP_ARM32), "sp");
}
#[test]
fn test_format_reg_lr_arm32() {
let printer = ArmAsmPrinter::new(false);
assert_eq!(printer.format_reg(LR_ARM32), "lr");
}
#[test]
fn test_format_reg_pc_arm32() {
let printer = ArmAsmPrinter::new(false);
assert_eq!(printer.format_reg(PC_ARM32), "pc");
}
#[test]
fn test_format_imm_positive() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_imm(42), "#42");
}
#[test]
fn test_format_imm_negative() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_imm(-16), "#-16");
}
#[test]
fn test_format_imm_zero() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_imm(0), "#0");
}
#[test]
fn test_format_mem_offset_zero() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_mem_offset(X0, 0), "[x0]");
}
#[test]
fn test_format_mem_offset_nonzero() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_mem_offset(SP, 8), "[sp, #8]");
}
#[test]
fn test_format_mem_pre_index() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_mem_pre_index(SP, -16), "[sp, #-16]!");
}
#[test]
fn test_format_mem_post_index() {
let printer = ArmAsmPrinter::new(true);
assert_eq!(printer.format_mem_post_index(SP, 16), "[sp], #16");
}
#[test]
fn test_get_mnemonic_add() {
assert_eq!(ArmOpcode::ADD as u32, 0);
assert_eq!(ArmOpcode::ARM_ADD as u32, 307);
assert_eq!(ArmOpcode::ARM_PUSH as u32, 338);
assert_eq!(ArmOpcode::ARM_POP as u32, 339);
assert_eq!(ArmOpcode::ARM_MOV as u32, 313);
assert_eq!(ArmOpcode::ARM_BX as u32, 323);
assert_eq!(ArmOpcode::ARM_BL as u32, 322);
assert_eq!(ArmOpcode::ARM_CMP as u32, 317);
let printer = ArmAsmPrinter::new(true);
let mnemonic = printer.get_mnemonic(ArmOpcode::ADD as u32);
assert_eq!(mnemonic, "add");
}
#[test]
fn test_get_mnemonic_ldr() {
let printer = ArmAsmPrinter::new(true);
let mnemonic = printer.get_mnemonic(ArmOpcode::LDR as u32);
assert_eq!(mnemonic, "ldr");
}
#[test]
fn test_get_mnemonic_ret() {
let printer = ArmAsmPrinter::new(true);
let mnemonic = printer.get_mnemonic(ArmOpcode::RET as u32);
assert_eq!(mnemonic, "ret");
}
#[test]
fn test_get_mnemonic_bl() {
let printer = ArmAsmPrinter::new(true);
let mnemonic = printer.get_mnemonic(ArmOpcode::BL as u32);
assert_eq!(mnemonic, "bl");
}
}