1use std::fmt::Write as _;
43
44use rucc_base::Interner;
45use rucc_target::{Constraint, PhysReg, RegClass, RegFile, Role};
46
47use crate::func::{Func, defs};
48use crate::inst::{Amode, Block, BlockCall, Inst, Operand, Param, Reg};
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 if last.is_none() && !func[block].succs.is_empty() {
171 let arms: Vec<String> = func[block]
172 .succs
173 .iter()
174 .map(|succ| self.text(|printer| printer.block_call(func, succ)))
175 .collect();
176 let _ = writeln!(self.out, " {}", arms.join(", "));
177 }
178 }
179
180 fn param(&mut self, param: Param) {
182 self.reg(param.reg, param.class, true);
183 }
184
185 fn inst(&mut self, func: &Func, block: Block, inst: Inst, terminator: bool) {
190 let data = func[inst];
191 let operands = &func[data.operands];
192 let written = defs(operands);
193 self.out.push_str(" ");
194 for (at, operand) in operands[..written].iter().enumerate() {
195 if at > 0 {
196 self.out.push_str(", ");
197 }
198 self.operand(*operand);
199 }
200 if written > 0 {
201 self.out.push_str(" = ");
202 }
203 self.out.push_str(self.names.resolve(data.opcode.name()));
204
205 let mut rest: Vec<String> = Vec::new();
209 let addressed = data.mem.map(|mem| func[mem]);
210 for (at, operand) in operands.iter().enumerate().skip(written) {
211 if names_operand(addressed.as_ref(), at) {
212 continue;
213 }
214 rest.push(self.text(|printer| printer.operand(*operand)));
215 }
216 if let Some(symbol) = data.symbol {
217 rest.push(format!("@{}", self.names.resolve(symbol)));
218 }
219 if let Some(amode) = addressed {
220 rest.push(self.text(|printer| printer.amode(operands, &amode)));
221 }
222 if let Some(imm) = data.imm {
223 rest.push(func[imm].0.to_string());
224 }
225 if terminator {
226 for succ in &func[block].succs {
227 rest.push(self.text(|printer| printer.block_call(func, succ)));
228 }
229 }
230 for (at, text) in rest.iter().enumerate() {
231 self.out.push_str(if at > 0 { ", " } else { " " });
232 self.out.push_str(text);
233 }
234 self.out.push('\n');
235 }
236
237 fn operand(&mut self, operand: Operand) {
239 if operand.role == Role::EarlyDef {
240 self.out.push_str("early ");
241 }
242 self.reg(operand.reg, operand.class, operand.role.is_def());
243 match operand.constraint {
244 Constraint::Reg => {}
245 Constraint::Any => self.out.push_str("(any)"),
246 Constraint::Stack => self.out.push_str("(stack)"),
247 Constraint::Fixed(phys) => {
248 self.out.push('(');
249 self.phys(operand.class, phys);
250 self.out.push(')');
251 }
252 Constraint::Reuse(at) => {
253 let _ = write!(self.out, "(reuse {at})");
254 }
255 }
256 }
257
258 fn reg(&mut self, reg: Reg, class: RegClass, declared: bool) {
261 if let Some(phys) = reg.phys() {
262 self.phys(class, phys);
263 return;
264 }
265 match self.printed(reg) {
266 Some(number) => {
267 let _ = write!(self.out, "%{number}");
268 }
269 None => self.out.push_str("%?"),
270 }
271 if declared {
272 let name = self.regs.class(class).map_or("?", |info| info.name);
273 let _ = write!(self.out, ":{name}");
274 }
275 }
276
277 fn phys(&mut self, class: RegClass, reg: PhysReg) {
279 let _ = write!(self.out, "${}", self.regs.name(class, reg).unwrap_or("?"));
280 }
281
282 fn printed(&self, reg: Reg) -> Option<u32> {
284 let number = reg.number()?;
285 match self.numbers.get(number as usize).copied() {
286 Some(u32::MAX) | None => None,
287 Some(number) => Some(number),
288 }
289 }
290
291 fn amode(&mut self, operands: &[Operand], amode: &Amode) {
293 self.out.push('[');
294 let mut written = false;
295 if let Some(symbol) = amode.symbol {
296 let _ = write!(self.out, "@{}", self.names.resolve(symbol));
297 written = true;
298 }
299 if let Some(operand) = amode.base.and_then(|at| operands.get(usize::from(at))) {
300 if written {
301 self.out.push_str(" + ");
302 }
303 self.reg(operand.reg, operand.class, false);
304 written = true;
305 }
306 if let Some(operand) = amode.index.and_then(|at| operands.get(usize::from(at))) {
307 if written {
308 self.out.push_str(" + ");
309 }
310 self.reg(operand.reg, operand.class, false);
311 if amode.scale != 1 {
312 let _ = write!(self.out, "*{}", amode.scale);
313 }
314 written = true;
315 }
316 if amode.disp != 0 || !written {
319 if written {
320 let sign = if amode.disp < 0 { '-' } else { '+' };
321 let _ = write!(self.out, " {sign} {}", i64::from(amode.disp).abs());
322 } else {
323 let _ = write!(self.out, "{}", amode.disp);
324 }
325 }
326 self.out.push(']');
327 }
328
329 fn block_call(&mut self, func: &Func, call: &BlockCall) {
334 match self.labels.get(call.block.index()).copied() {
335 Some(u32::MAX) | None => self.out.push_str("block?"),
336 Some(number) => {
337 let _ = write!(self.out, "block{number}");
338 }
339 }
340 if call.args.is_empty() {
341 return;
342 }
343 self.out.push('(');
344 for (at, &arg) in call.args.iter().enumerate() {
345 if at > 0 {
346 self.out.push_str(", ");
347 }
348 let class = func[call.block]
352 .params
353 .get(at)
354 .map_or_else(|| RegClass::new(0), |param| param.class);
355 self.reg(arg, class, false);
356 }
357 self.out.push(')');
358 }
359
360 fn text(&mut self, write: impl FnOnce(&mut Self)) -> String {
362 let held = std::mem::take(&mut self.out);
363 write(self);
364 std::mem::replace(&mut self.out, held)
365 }
366}
367
368fn names_operand(amode: Option<&Amode>, at: usize) -> bool {
371 let Some(amode) = amode else { return false };
372 let at = u8::try_from(at).ok();
373 amode.base == at || amode.index == at
374}
375
376#[cfg(test)]
377mod tests {
378 use rucc_target::PhysReg;
379
380 use super::*;
381 use crate::fixtures::{BEFORE, REGS};
382 use crate::inst::{BlockCall, Mem, Opcode};
383
384 fn scale() -> (Interner, Func) {
389 let mut names = Interner::new();
390 let gpr = REGS.class_named("gpr").expect("the fixture file has a gpr class");
391 let xmm = REGS.class_named("xmm").expect("the fixture file has an xmm class");
392 let rax = named("rax");
393 let rdx = named("rdx");
394 let mut func = Func::new(names.intern("scale"));
395 let op = |names: &mut Interner, text: &str| Opcode::new(names.intern(text));
396
397 let entry = func.create_block();
398 let body = func.create_block();
399 let exit = func.create_block();
400
401 let n = func.append_param(entry, gpr);
402 let stride = func.append_param(entry, gpr);
403 let four = func.new_vreg(gpr);
404 let scaled = func.new_vreg(gpr);
405 let opcode = op(&mut names, "x64.mov_ri");
406 func.build(entry, opcode).def(four, gpr).imm(4).finish();
407 let opcode = op(&mut names, "x64.imul_rr");
408 func.build(entry, opcode)
409 .operand(Operand::write(scaled, gpr).with(Constraint::Reuse(1)))
410 .uses(stride, gpr)
411 .uses(four, gpr)
412 .finish();
413 let opcode = op(&mut names, "x64.cmp_ri");
414 func.build(entry, opcode).uses(n, gpr).imm(0).finish();
415 let opcode = op(&mut names, "x64.jle");
416 func.build(entry, opcode).finish();
417 *func.succs_mut(entry) =
418 vec![BlockCall::with(exit, vec![n]), BlockCall::with(body, vec![scaled, stride])];
419
420 let base = func.append_param(body, gpr);
421 let index = func.append_param(body, gpr);
422 let addr = func.new_vreg(gpr);
423 let loaded = func.new_vreg(gpr);
424 let quotient = func.new_vreg(gpr);
425 let remainder = func.new_vreg(gpr);
426 let opcode = op(&mut names, "x64.lea");
427 func.build(body, opcode)
428 .def(addr, gpr)
429 .mem(Mem::at(Operand::read(base, gpr)).indexed(Operand::read(index, gpr), 4).plus(16))
430 .finish();
431 let counter = names.intern("counter");
432 let opcode = op(&mut names, "x64.mov_rm");
433 func.build(body, opcode).def(loaded, gpr).mem(Mem::of(counter).plus(8)).finish();
434 let opcode = op(&mut names, "x64.mov_mi");
435 func.build(body, opcode).mem(Mem::at(Operand::read(addr, gpr)).plus(-4)).imm(1).finish();
436 let opcode = op(&mut names, "x64.idiv_rr");
437 func.build(body, opcode)
438 .operand(Operand::write(quotient, gpr).with(Constraint::Fixed(rax)))
439 .operand(Operand::write_early(remainder, gpr).with(Constraint::Fixed(rdx)))
440 .operand(Operand::read(loaded, gpr).with(Constraint::Fixed(rax)))
441 .operand(Operand::read(addr, gpr).with(Constraint::Any))
442 .finish();
443 let opcode = op(&mut names, "x64.cmp_rr");
444 func.build(body, opcode)
445 .uses(quotient, gpr)
446 .operand(Operand::read(remainder, gpr).with(Constraint::Stack))
447 .finish();
448 let opcode = op(&mut names, "x64.jmp");
449 func.build(body, opcode).finish();
450 *func.succs_mut(body) = vec![BlockCall::with(exit, vec![quotient])];
451
452 let result = func.append_param(exit, gpr);
453 let moved = func.new_vreg(xmm);
454 let opcode = op(&mut names, "x64.movd_xr");
455 func.build(exit, opcode).def(moved, xmm).uses(result, gpr).finish();
456 let opcode = op(&mut names, "x64.ret");
457 func.build(exit, opcode).uses(Reg::physical(rax), gpr).finish();
458
459 (names, func)
460 }
461
462 fn named(name: &str) -> PhysReg {
464 REGS.reg_named(name).expect("the fixture file has that register").1
465 }
466
467 #[test]
468 fn a_function_prints_as_the_fixture_says() {
469 let (names, func) = scale();
470 assert_eq!(print_func(&func, &names, ®S), BEFORE);
471 }
472
473 #[test]
474 fn two_functions_are_printed_with_a_blank_line_between_them() {
475 let (names, func) = scale();
476 let empty = Func::new(func.name);
477 let text = print(&[empty, func], &names, ®S);
478 assert_eq!(text, format!("mfunc @scale {{\n}}\n\n{BEFORE}"));
479 }
480
481 #[test]
482 fn a_register_nothing_writes_prints_as_one_nothing_writes() {
483 let mut names = Interner::new();
484 let gpr = REGS.class_named("gpr").expect("the fixture file has a gpr class");
485 let mut func = Func::new(names.intern("f"));
486 let block = func.create_block();
487 let missing = func.new_vreg(gpr);
488 let opcode = Opcode::new(names.intern("x64.ret"));
489 func.build(block, opcode).uses(missing, gpr).finish();
490 assert_eq!(print_func(&func, &names, ®S), "mfunc @f {\nblock0:\n x64.ret %?\n}\n");
491 }
492
493 #[test]
494 fn a_block_with_nothing_in_it_still_prints_where_it_goes() {
495 let mut names = Interner::new();
496 let gpr = REGS.class_named("gpr").expect("the fixture file has a gpr class");
497 let mut func = Func::new(names.intern("f"));
498 let entry = func.create_block();
499 let exit = func.create_block();
500 let value = func.append_param(entry, gpr);
501 func.append_param(exit, gpr);
502 *func.succs_mut(entry) = vec![BlockCall::with(exit, vec![value])];
503
504 assert_eq!(
508 print_func(&func, &names, ®S),
509 "mfunc @f {\nblock0(%0:gpr):\n block1(%0)\n\nblock1(%1:gpr):\n}\n"
510 );
511 }
512}