1use std::fmt::Write as _;
43
44use rucc_base::Interner;
45use rucc_target::{PhysReg, RegClass, RegFile};
46
47use crate::func::{Func, defs};
48use crate::inst::{Amode, Block, BlockCall, Constraint, Inst, Operand, Param, Reg, Role};
49
50#[must_use]
52pub fn print(funcs: &[Func], names: &Interner, regs: &RegFile) -> String {
53 let mut printer = Printer::new(names, regs);
54 for (index, func) in funcs.iter().enumerate() {
55 if index > 0 {
56 printer.gap();
57 }
58 printer.func(func);
59 }
60 printer.finish()
61}
62
63#[must_use]
65pub fn print_func(func: &Func, names: &Interner, regs: &RegFile) -> String {
66 let mut printer = Printer::new(names, regs);
67 printer.func(func);
68 printer.finish()
69}
70
71#[derive(Debug)]
73pub struct Printer<'a> {
74 names: &'a Interner,
75 regs: &'a RegFile,
76 out: String,
77 numbers: Vec<u32>,
81 labels: Vec<u32>,
83}
84
85impl<'a> Printer<'a> {
86 #[must_use]
88 pub fn new(names: &'a Interner, regs: &'a RegFile) -> Printer<'a> {
89 Printer { names, regs, out: String::new(), numbers: Vec::new(), labels: Vec::new() }
90 }
91
92 #[must_use]
94 pub fn finish(self) -> String {
95 self.out
96 }
97
98 pub fn gap(&mut self) {
100 self.out.push('\n');
101 }
102
103 pub fn func(&mut self, func: &Func) {
105 self.number(func);
106 let _ = writeln!(self.out, "mfunc @{} {{", self.names.resolve(func.name));
107 for (index, block) in func.blocks().enumerate() {
108 if index > 0 {
109 self.out.push('\n');
110 }
111 self.block(func, block, index);
112 }
113 self.out.push_str("}\n");
114 }
115
116 fn number(&mut self, func: &Func) {
118 self.numbers.clear();
119 self.numbers.resize(func.vregs(), u32::MAX);
120 self.labels.clear();
121 self.labels.resize(func.block_count(), u32::MAX);
122 let mut next = 0;
123 for (index, block) in func.blocks().enumerate() {
124 self.labels[block.index()] = index as u32;
125 for param in &func[block].params {
126 self.give(param.reg, &mut next);
127 }
128 for inst in func.insts(block) {
129 let operands = &func[func[inst].operands];
130 for operand in &operands[..defs(operands)] {
131 self.give(operand.reg, &mut next);
132 }
133 }
134 }
135 }
136
137 fn give(&mut self, reg: Reg, next: &mut u32) {
139 let Some(number) = reg.number() else { return };
140 let Some(slot) = self.numbers.get_mut(number as usize) else { return };
141 if *slot == u32::MAX {
142 *slot = *next;
143 *next += 1;
144 }
145 }
146
147 fn block(&mut self, func: &Func, block: Block, index: usize) {
149 let _ = write!(self.out, "block{index}");
150 let params = &func[block].params;
151 if !params.is_empty() {
152 self.out.push('(');
153 for (at, param) in params.iter().enumerate() {
154 if at > 0 {
155 self.out.push_str(", ");
156 }
157 self.param(*param);
158 }
159 self.out.push(')');
160 }
161 self.out.push_str(":\n");
162 let last = func.terminator(block);
163 for inst in func.insts(block) {
164 self.inst(func, block, inst, Some(inst) == last);
165 }
166 }
167
168 fn param(&mut self, param: Param) {
170 self.reg(param.reg, param.class, true);
171 }
172
173 fn inst(&mut self, func: &Func, block: Block, inst: Inst, terminator: bool) {
178 let data = func[inst];
179 let operands = &func[data.operands];
180 let written = defs(operands);
181 self.out.push_str(" ");
182 for (at, operand) in operands[..written].iter().enumerate() {
183 if at > 0 {
184 self.out.push_str(", ");
185 }
186 self.operand(*operand);
187 }
188 if written > 0 {
189 self.out.push_str(" = ");
190 }
191 self.out.push_str(self.names.resolve(data.opcode.name()));
192
193 let mut rest: Vec<String> = Vec::new();
197 let addressed = data.mem.map(|mem| func[mem]);
198 for (at, operand) in operands.iter().enumerate().skip(written) {
199 if names_operand(addressed.as_ref(), at) {
200 continue;
201 }
202 rest.push(self.text(|printer| printer.operand(*operand)));
203 }
204 if let Some(symbol) = data.symbol {
205 rest.push(format!("@{}", self.names.resolve(symbol)));
206 }
207 if let Some(amode) = addressed {
208 rest.push(self.text(|printer| printer.amode(operands, &amode)));
209 }
210 if let Some(imm) = data.imm {
211 rest.push(func[imm].0.to_string());
212 }
213 if terminator {
214 for succ in &func[block].succs {
215 rest.push(self.text(|printer| printer.block_call(func, succ)));
216 }
217 }
218 for (at, text) in rest.iter().enumerate() {
219 self.out.push_str(if at > 0 { ", " } else { " " });
220 self.out.push_str(text);
221 }
222 self.out.push('\n');
223 }
224
225 fn operand(&mut self, operand: Operand) {
227 if operand.role == Role::EarlyDef {
228 self.out.push_str("early ");
229 }
230 self.reg(operand.reg, operand.class, operand.role.is_def());
231 match operand.constraint {
232 Constraint::Reg => {}
233 Constraint::Any => self.out.push_str("(any)"),
234 Constraint::Stack => self.out.push_str("(stack)"),
235 Constraint::Fixed(phys) => {
236 self.out.push('(');
237 self.phys(operand.class, phys);
238 self.out.push(')');
239 }
240 Constraint::Reuse(at) => {
241 let _ = write!(self.out, "(reuse {at})");
242 }
243 }
244 }
245
246 fn reg(&mut self, reg: Reg, class: RegClass, declared: bool) {
249 if let Some(phys) = reg.phys() {
250 self.phys(class, phys);
251 return;
252 }
253 match self.printed(reg) {
254 Some(number) => {
255 let _ = write!(self.out, "%{number}");
256 }
257 None => self.out.push_str("%?"),
258 }
259 if declared {
260 let name = self.regs.class(class).map_or("?", |info| info.name);
261 let _ = write!(self.out, ":{name}");
262 }
263 }
264
265 fn phys(&mut self, class: RegClass, reg: PhysReg) {
267 let _ = write!(self.out, "${}", self.regs.name(class, reg).unwrap_or("?"));
268 }
269
270 fn printed(&self, reg: Reg) -> Option<u32> {
272 let number = reg.number()?;
273 match self.numbers.get(number as usize).copied() {
274 Some(u32::MAX) | None => None,
275 Some(number) => Some(number),
276 }
277 }
278
279 fn amode(&mut self, operands: &[Operand], amode: &Amode) {
281 self.out.push('[');
282 let mut written = false;
283 if let Some(symbol) = amode.symbol {
284 let _ = write!(self.out, "@{}", self.names.resolve(symbol));
285 written = true;
286 }
287 if let Some(operand) = amode.base.and_then(|at| operands.get(usize::from(at))) {
288 if written {
289 self.out.push_str(" + ");
290 }
291 self.reg(operand.reg, operand.class, false);
292 written = true;
293 }
294 if let Some(operand) = amode.index.and_then(|at| operands.get(usize::from(at))) {
295 if written {
296 self.out.push_str(" + ");
297 }
298 self.reg(operand.reg, operand.class, false);
299 if amode.scale != 1 {
300 let _ = write!(self.out, "*{}", amode.scale);
301 }
302 written = true;
303 }
304 if amode.disp != 0 || !written {
307 if written {
308 let sign = if amode.disp < 0 { '-' } else { '+' };
309 let _ = write!(self.out, " {sign} {}", i64::from(amode.disp).abs());
310 } else {
311 let _ = write!(self.out, "{}", amode.disp);
312 }
313 }
314 self.out.push(']');
315 }
316
317 fn block_call(&mut self, func: &Func, call: &BlockCall) {
322 match self.labels.get(call.block.index()).copied() {
323 Some(u32::MAX) | None => self.out.push_str("block?"),
324 Some(number) => {
325 let _ = write!(self.out, "block{number}");
326 }
327 }
328 if call.args.is_empty() {
329 return;
330 }
331 self.out.push('(');
332 for (at, &arg) in call.args.iter().enumerate() {
333 if at > 0 {
334 self.out.push_str(", ");
335 }
336 let class = func[call.block]
340 .params
341 .get(at)
342 .map_or_else(|| RegClass::new(0), |param| param.class);
343 self.reg(arg, class, false);
344 }
345 self.out.push(')');
346 }
347
348 fn text(&mut self, write: impl FnOnce(&mut Self)) -> String {
350 let held = std::mem::take(&mut self.out);
351 write(self);
352 std::mem::replace(&mut self.out, held)
353 }
354}
355
356fn names_operand(amode: Option<&Amode>, at: usize) -> bool {
359 let Some(amode) = amode else { return false };
360 let at = u8::try_from(at).ok();
361 amode.base == at || amode.index == at
362}
363
364#[cfg(test)]
365mod tests {
366 use rucc_target::PhysReg;
367
368 use super::*;
369 use crate::fixtures::{BEFORE, REGS};
370 use crate::inst::{BlockCall, Mem, Opcode};
371
372 fn scale() -> (Interner, Func) {
377 let mut names = Interner::new();
378 let gpr = REGS.class_named("gpr").expect("the fixture file has a gpr class");
379 let xmm = REGS.class_named("xmm").expect("the fixture file has an xmm class");
380 let rax = named("rax");
381 let rdx = named("rdx");
382 let mut func = Func::new(names.intern("scale"));
383 let op = |names: &mut Interner, text: &str| Opcode::new(names.intern(text));
384
385 let entry = func.create_block();
386 let body = func.create_block();
387 let exit = func.create_block();
388
389 let n = func.append_param(entry, gpr);
390 let stride = func.append_param(entry, gpr);
391 let four = func.new_vreg(gpr);
392 let scaled = func.new_vreg(gpr);
393 let opcode = op(&mut names, "x64.mov_ri");
394 func.build(entry, opcode).def(four, gpr).imm(4).finish();
395 let opcode = op(&mut names, "x64.imul_rr");
396 func.build(entry, opcode)
397 .operand(Operand::write(scaled, gpr).with(Constraint::Reuse(1)))
398 .uses(stride, gpr)
399 .uses(four, gpr)
400 .finish();
401 let opcode = op(&mut names, "x64.cmp_ri");
402 func.build(entry, opcode).uses(n, gpr).imm(0).finish();
403 let opcode = op(&mut names, "x64.jle");
404 func.build(entry, opcode).finish();
405 *func.succs_mut(entry) =
406 vec![BlockCall::with(exit, vec![n]), BlockCall::with(body, vec![scaled, stride])];
407
408 let base = func.append_param(body, gpr);
409 let index = func.append_param(body, gpr);
410 let addr = func.new_vreg(gpr);
411 let loaded = func.new_vreg(gpr);
412 let quotient = func.new_vreg(gpr);
413 let remainder = func.new_vreg(gpr);
414 let opcode = op(&mut names, "x64.lea");
415 func.build(body, opcode)
416 .def(addr, gpr)
417 .mem(Mem::at(Operand::read(base, gpr)).indexed(Operand::read(index, gpr), 4).plus(16))
418 .finish();
419 let counter = names.intern("counter");
420 let opcode = op(&mut names, "x64.mov_rm");
421 func.build(body, opcode).def(loaded, gpr).mem(Mem::of(counter).plus(8)).finish();
422 let opcode = op(&mut names, "x64.mov_mi");
423 func.build(body, opcode).mem(Mem::at(Operand::read(addr, gpr)).plus(-4)).imm(1).finish();
424 let opcode = op(&mut names, "x64.idiv_rr");
425 func.build(body, opcode)
426 .operand(Operand::write(quotient, gpr).with(Constraint::Fixed(rax)))
427 .operand(Operand::write_early(remainder, gpr).with(Constraint::Fixed(rdx)))
428 .operand(Operand::read(loaded, gpr).with(Constraint::Fixed(rax)))
429 .operand(Operand::read(addr, gpr).with(Constraint::Any))
430 .finish();
431 let opcode = op(&mut names, "x64.cmp_rr");
432 func.build(body, opcode)
433 .uses(quotient, gpr)
434 .operand(Operand::read(remainder, gpr).with(Constraint::Stack))
435 .finish();
436 let opcode = op(&mut names, "x64.jmp");
437 func.build(body, opcode).finish();
438 *func.succs_mut(body) = vec![BlockCall::with(exit, vec![quotient])];
439
440 let result = func.append_param(exit, gpr);
441 let moved = func.new_vreg(xmm);
442 let opcode = op(&mut names, "x64.movd_xr");
443 func.build(exit, opcode).def(moved, xmm).uses(result, gpr).finish();
444 let opcode = op(&mut names, "x64.ret");
445 func.build(exit, opcode).uses(Reg::physical(rax), gpr).finish();
446
447 (names, func)
448 }
449
450 fn named(name: &str) -> PhysReg {
452 REGS.reg_named(name).expect("the fixture file has that register").1
453 }
454
455 #[test]
456 fn a_function_prints_as_the_fixture_says() {
457 let (names, func) = scale();
458 assert_eq!(print_func(&func, &names, ®S), BEFORE);
459 }
460
461 #[test]
462 fn two_functions_are_printed_with_a_blank_line_between_them() {
463 let (names, func) = scale();
464 let empty = Func::new(func.name);
465 let text = print(&[empty, func], &names, ®S);
466 assert_eq!(text, format!("mfunc @scale {{\n}}\n\n{BEFORE}"));
467 }
468
469 #[test]
470 fn a_register_nothing_writes_prints_as_one_nothing_writes() {
471 let mut names = Interner::new();
472 let gpr = REGS.class_named("gpr").expect("the fixture file has a gpr class");
473 let mut func = Func::new(names.intern("f"));
474 let block = func.create_block();
475 let missing = func.new_vreg(gpr);
476 let opcode = Opcode::new(names.intern("x64.ret"));
477 func.build(block, opcode).uses(missing, gpr).finish();
478 assert_eq!(print_func(&func, &names, ®S), "mfunc @f {\nblock0:\n x64.ret %?\n}\n");
479 }
480}