1use std::fmt;
53
54use rucc_mir::{Constraint, Func, Inst, Reg, Role};
55use rucc_target::{PhysReg, RegClass};
56
57use crate::assign::{Assignment, Place};
58use crate::live::{Live, Range};
59use crate::order::{Order, Point};
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Problem {
64 Nowhere {
66 reg: Reg,
68 },
69 Shared {
72 first: Reg,
74 second: Reg,
76 place: Place,
78 },
79 InTheWay {
82 reg: Reg,
84 at: PhysReg,
86 inst: Inst,
88 },
89 NotOnTheStack {
91 reg: Reg,
93 inst: Inst,
95 },
96 NeverWritten {
99 reg: Reg,
101 },
102}
103
104impl fmt::Display for Problem {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 match self {
107 Problem::Nowhere { reg } => write!(f, "{} has nowhere to live", name(*reg)),
108 Problem::Shared { first, second, place } => {
109 let (first, second) = (name(*first), name(*second));
110 write!(f, "{first} and {second} are both live and both in {}", place_name(*place))
111 }
112 Problem::InTheWay { reg, at, inst } => {
113 let reg = name(*reg);
114 let inst = inst.index();
115 write!(f, "{reg} is in register {}, which instruction {inst} wants", at.number())
116 }
117 Problem::NotOnTheStack { reg, inst } => {
118 let reg = name(*reg);
119 write!(f, "{reg} is not on the stack, and instruction {} needs it", inst.index())
120 }
121 Problem::NeverWritten { reg } => {
122 write!(f, "{} is read before anything writes it", name(*reg))
123 }
124 }
125 }
126}
127
128#[must_use]
139pub fn check(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<Problem> {
140 let mut problems = Vec::new();
141 if let Some(entry) = func.entry() {
144 for reg in live.live_in(entry) {
145 problems.push(Problem::NeverWritten { reg });
146 }
147 }
148 let reuses = reuses(func, order);
149 let mut values = Vec::new();
150 for (number, reuse) in reuses.iter().enumerate() {
151 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
152 let (Some(mut range), Some(class)) = (live.range(reg), func.class_of(reg)) else {
153 continue;
154 };
155 let Some(place) = assignment.place(reg) else {
156 problems.push(Problem::Nowhere { reg });
157 continue;
158 };
159 if let Some(reuse) = reuse {
164 range.start = range.start.min(reuse.at);
165 }
166 values.push(Value { reg, class, range, place });
167 }
168 overlaps(&values, &reuses, live, &mut problems);
169 instructions(func, order, live, assignment, &values, &reuses, &mut problems);
170 problems
171}
172
173#[must_use]
175pub fn report(problems: &[Problem]) -> String {
176 let places = if problems.len() == 1 { "place" } else { "places" };
177 let mut report = format!("the allocation is wrong in {} {places}", problems.len());
178 for problem in problems {
179 report.push_str("\n ");
180 report.push_str(&problem.to_string());
181 }
182 report
183}
184
185#[derive(Debug, Clone, Copy)]
187struct Value {
188 reg: Reg,
189 class: RegClass,
190 range: Range,
191 place: Place,
192}
193
194#[derive(Debug, Clone, Copy)]
196struct Reuse {
197 source: Reg,
198 at: Point,
199}
200
201fn overlaps(values: &[Value], reuses: &[Option<Reuse>], live: &Live, problems: &mut Vec<Problem>) {
206 let mut sorted = values.to_vec();
207 sorted.sort_by_key(|value| (value.range.start, value.reg));
208 let mut active: Vec<Value> = Vec::new();
209 for value in sorted {
210 active.retain(|held| held.range.end >= value.range.start);
211 for held in &active {
212 if !together(*held, value) || coalesced(*held, value, reuses, live) {
213 continue;
214 }
215 problems.push(Problem::Shared {
216 first: held.reg,
217 second: value.reg,
218 place: value.place,
219 });
220 }
221 active.push(value);
222 }
223}
224
225fn together(first: Value, second: Value) -> bool {
231 match (first.place, second.place) {
232 (Place::Reg(first_at), Place::Reg(second_at)) => {
233 first_at == second_at && first.class == second.class
234 }
235 (Place::Slot(first_slot), Place::Slot(second_slot)) => first_slot == second_slot,
236 _ => false,
237 }
238}
239
240fn coalesced(first: Value, second: Value, reuses: &[Option<Reuse>], live: &Live) -> bool {
254 let pair = |source: Value, dest: Value| {
255 let Some(reuse) = reuses[index(dest.reg)] else { return false };
256 reuse.source == source.reg
257 && dest.range.start == reuse.at
258 && live.range(source.reg).is_some_and(|r| r.end == reuse.at)
259 };
260 pair(first, second) || pair(second, first)
261}
262
263fn instructions(
266 func: &Func,
267 order: &Order,
268 live: &Live,
269 assignment: &Assignment,
270 values: &[Value],
271 reuses: &[Option<Reuse>],
272 problems: &mut Vec<Problem>,
273) {
274 for block in func.blocks() {
275 for inst in func.insts(block) {
276 for operand in &func[func[inst].operands] {
277 if operand.constraint == Constraint::Stack
278 && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
279 {
280 problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
281 }
282 let at = match operand.constraint {
286 Constraint::Fixed(at) => Some(at),
287 _ => operand.reg.phys(),
288 };
289 let Some(at) = at else { continue };
290 let early = order.early(inst);
291 let point = if operand.role == Role::Def { order.late(inst) } else { early };
292 for value in values {
293 let mine = value.reg == operand.reg
294 || reuses[index(value.reg)].is_some_and(|reuse| {
295 reuse.source == operand.reg
296 && reuse.at == early
297 && value.place == Place::Reg(at)
298 });
299 if mine || value.class != operand.class {
300 continue;
301 }
302 let live_here = live.anywhere_in(value.reg, block);
307 if value.place == Place::Reg(at) && value.range.covers(point) && live_here {
308 problems.push(Problem::InTheWay { reg: value.reg, at, inst });
309 }
310 }
311 }
312 }
313 }
314}
315
316fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
318 let mut reuses = vec![None; func.vregs()];
319 for block in func.blocks() {
320 for inst in func.insts(block) {
321 let operands = &func[func[inst].operands];
322 for operand in operands {
323 let Constraint::Reuse(other) = operand.constraint else { continue };
324 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
325 let Some(number) = number else { continue };
326 let source = operands[usize::from(other)].reg;
327 reuses[number] = Some(Reuse { source, at: order.early(inst) });
328 }
329 }
330 }
331 reuses
332}
333
334fn index(reg: Reg) -> usize {
337 reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
338}
339
340fn name(reg: Reg) -> String {
342 match reg.number() {
343 Some(number) => format!("%{number}"),
344 None => format!("register {}", reg.phys().expect("a physical register").number()),
345 }
346}
347
348fn place_name(place: Place) -> String {
351 match place {
352 Place::Reg(at) => format!("register {}", at.number()),
353 Place::Slot(slot) => format!("slot {slot}"),
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use rucc_base::Interner;
360 use rucc_mir::{BlockCall, Opcode, Operand};
361 use rucc_target::x86_64::{GPR, RAX, RCX, SYSV};
362
363 use super::*;
364 use crate::assign::{Env, assign};
365
366 fn env() -> Env {
368 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
369 Env::new().with(GPR, order, scratch)
370 }
371
372 fn allocated(func: &Func) -> Vec<String> {
375 let order = Order::of(func);
376 let live = Live::of(func, &order);
377 let assignment = assign(func, &order, &live, &env());
378 said(func, &order, &live, &assignment)
379 }
380
381 fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
383 check(func, order, live, assignment).iter().map(ToString::to_string).collect()
384 }
385
386 fn read(func: &Func) -> (Order, Live) {
388 let order = Order::of(func);
389 let live = Live::of(func, &order);
390 (order, live)
391 }
392
393 #[test]
394 fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
395 let mut names = Interner::new();
396 let mut func = Func::new(names.intern("f"));
397 let opcode = Opcode::new(names.intern("x64.nop"));
398 let block = func.create_block();
399 let first = func.new_vreg(GPR);
400 let second = func.new_vreg(GPR);
401 func.build(block, opcode).def(first, GPR).finish();
402 func.build(block, opcode).def(second, GPR).finish();
403 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
404
405 assert_eq!(allocated(&func), Vec::<String>::new());
406 }
407
408 #[test]
409 fn a_value_with_nowhere_to_live_is_found() {
410 let mut names = Interner::new();
411 let mut func = Func::new(names.intern("f"));
412 let opcode = Opcode::new(names.intern("x64.nop"));
413 let block = func.create_block();
414 let only = func.new_vreg(GPR);
415 func.build(block, opcode).def(only, GPR).finish();
416 func.build(block, opcode).uses(only, GPR).finish();
417
418 let (order, live) = read(&func);
419 let assignment = Assignment::empty(func.vregs());
420
421 assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
422 }
423
424 #[test]
425 fn a_value_read_before_anything_writes_it_is_found() {
426 let mut names = Interner::new();
427 let mut func = Func::new(names.intern("f"));
428 let opcode = Opcode::new(names.intern("x64.nop"));
429 let block = func.create_block();
430 let never = func.new_vreg(GPR);
431 func.build(block, opcode).uses(never, GPR).finish();
432
433 let (order, live) = read(&func);
434 let mut assignment = Assignment::empty(func.vregs());
435 assignment.put(never, Place::Reg(RAX));
436
437 assert_eq!(
438 said(&func, &order, &live, &assignment),
439 ["%0 is read before anything writes it"]
440 );
441 }
442
443 #[test]
444 fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
445 let mut names = Interner::new();
446 let mut func = Func::new(names.intern("f"));
447 let opcode = Opcode::new(names.intern("x64.nop"));
448 let block = func.create_block();
449 let first = func.new_vreg(GPR);
450 let second = func.new_vreg(GPR);
451 func.build(block, opcode).def(first, GPR).finish();
452 func.build(block, opcode).def(second, GPR).finish();
453 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
454
455 let (order, live) = read(&func);
456 let mut assignment = Assignment::empty(func.vregs());
457 assignment.put(first, Place::Reg(RAX));
458 assignment.put(second, Place::Reg(RAX));
459
460 let said = said(&func, &order, &live, &assignment);
461 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
462 }
463
464 #[test]
465 fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
466 let mut names = Interner::new();
467 let mut func = Func::new(names.intern("f"));
468 let opcode = Opcode::new(names.intern("x64.nop"));
469 let block = func.create_block();
470 let first = func.new_vreg(GPR);
471 let second = func.new_vreg(GPR);
472 func.build(block, opcode).def(first, GPR).finish();
473 func.build(block, opcode).def(second, GPR).finish();
474 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
475
476 let (order, live) = read(&func);
477 let mut assignment = Assignment::empty(func.vregs());
478 let slot = assignment.take_slot(GPR);
479 assignment.put(first, Place::Slot(slot));
480 assignment.put(second, Place::Slot(slot));
481
482 let said = said(&func, &order, &live, &assignment);
483 assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
484 }
485
486 #[test]
487 fn two_values_that_are_never_both_wanted_may_share_anything() {
488 let mut names = Interner::new();
489 let mut func = Func::new(names.intern("f"));
490 let opcode = Opcode::new(names.intern("x64.nop"));
491 let block = func.create_block();
492 let first = func.new_vreg(GPR);
493 let second = func.new_vreg(GPR);
494 func.build(block, opcode).def(first, GPR).finish();
495 func.build(block, opcode).uses(first, GPR).finish();
496 func.build(block, opcode).def(second, GPR).finish();
497 func.build(block, opcode).uses(second, GPR).finish();
498
499 let (order, live) = read(&func);
500 let mut assignment = Assignment::empty(func.vregs());
501 assignment.put(first, Place::Reg(RAX));
502 assignment.put(second, Place::Reg(RAX));
503
504 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
505 }
506
507 #[test]
508 fn a_value_left_in_a_register_an_instruction_wants_is_found() {
509 let mut names = Interner::new();
510 let mut func = Func::new(names.intern("f"));
511 let nop = Opcode::new(names.intern("x64.nop"));
512 let divide = Opcode::new(names.intern("x64.idiv"));
513 let block = func.create_block();
514 let held = func.new_vreg(GPR);
515 let dividend = func.new_vreg(GPR);
516 func.build(block, nop).def(held, GPR).finish();
517 func.build(block, nop).def(dividend, GPR).finish();
518 func.build(block, divide)
521 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
522 .finish();
523 func.build(block, nop).uses(held, GPR).finish();
524
525 let (order, live) = read(&func);
526 let mut assignment = Assignment::empty(func.vregs());
527 assignment.put(held, Place::Reg(RAX));
528 assignment.put(dividend, Place::Reg(RCX));
529
530 let said = said(&func, &order, &live, &assignment);
531 assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
532 }
533
534 #[test]
535 fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
536 let mut names = Interner::new();
537 let mut func = Func::new(names.intern("f"));
538 let nop = Opcode::new(names.intern("x64.nop"));
539 let divide = Opcode::new(names.intern("x64.idiv"));
540 let block = func.create_block();
541 let dividend = func.new_vreg(GPR);
542 func.build(block, nop).def(dividend, GPR).finish();
543 func.build(block, divide)
544 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
545 .finish();
546
547 let (order, live) = read(&func);
548 let mut assignment = Assignment::empty(func.vregs());
549 assignment.put(dividend, Place::Reg(RAX));
550
551 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
554 }
555
556 #[test]
557 fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
558 let mut names = Interner::new();
559 let mut func = Func::new(names.intern("f"));
560 let nop = Opcode::new(names.intern("x64.nop"));
561 let wide = Opcode::new(names.intern("x64.wide"));
562 let block = func.create_block();
563 let only = func.new_vreg(GPR);
564 func.build(block, nop).def(only, GPR).finish();
565 func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
566
567 let (order, live) = read(&func);
568 let mut assignment = Assignment::empty(func.vregs());
569 assignment.put(only, Place::Reg(RAX));
570
571 let said = said(&func, &order, &live, &assignment);
572 assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
573 }
574
575 #[test]
576 fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
577 let mut names = Interner::new();
578 let mut func = Func::new(names.intern("f"));
579 let nop = Opcode::new(names.intern("x64.nop"));
580 let add = Opcode::new(names.intern("x64.add"));
581 let block = func.create_block();
582 let left = func.new_vreg(GPR);
583 let right = func.new_vreg(GPR);
584 let sum = func.new_vreg(GPR);
585 func.build(block, nop).def(left, GPR).finish();
586 func.build(block, nop).def(right, GPR).finish();
587 func.build(block, add)
588 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
589 .uses(left, GPR)
590 .uses(right, GPR)
591 .finish();
592 func.build(block, nop).uses(sum, GPR).finish();
593
594 let (order, live) = read(&func);
595 let mut assignment = Assignment::empty(func.vregs());
596 assignment.put(left, Place::Reg(RAX));
597 assignment.put(right, Place::Reg(RCX));
598 assignment.put(sum, Place::Reg(RAX));
599
600 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
603 }
604
605 #[test]
606 fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
607 let mut names = Interner::new();
608 let mut func = Func::new(names.intern("f"));
609 let nop = Opcode::new(names.intern("x64.nop"));
610 let add = Opcode::new(names.intern("x64.add"));
611 let block = func.create_block();
612 let left = func.new_vreg(GPR);
613 let right = func.new_vreg(GPR);
614 let sum = func.new_vreg(GPR);
615 func.build(block, nop).def(left, GPR).finish();
616 func.build(block, nop).def(right, GPR).finish();
617 func.build(block, add)
618 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
619 .uses(left, GPR)
620 .uses(right, GPR)
621 .finish();
622 func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
623
624 let (order, live) = read(&func);
625 let mut assignment = Assignment::empty(func.vregs());
626 assignment.put(left, Place::Reg(RAX));
627 assignment.put(right, Place::Reg(RCX));
628 assignment.put(sum, Place::Reg(RAX));
629
630 let said = said(&func, &order, &live, &assignment);
633 assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
634 }
635
636 #[test]
637 fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
638 let mut names = Interner::new();
639 let mut func = Func::new(names.intern("f"));
640 let nop = Opcode::new(names.intern("x64.nop"));
641 let add = Opcode::new(names.intern("x64.add"));
642 let block = func.create_block();
643 let left = func.new_vreg(GPR);
644 let right = func.new_vreg(GPR);
645 let sum = func.new_vreg(GPR);
646 func.build(block, nop).def(left, GPR).finish();
647 func.build(block, nop).def(right, GPR).finish();
648 func.build(block, add)
649 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
650 .uses(left, GPR)
651 .uses(right, GPR)
652 .finish();
653 func.build(block, nop).uses(sum, GPR).finish();
654
655 let (order, live) = read(&func);
656 let mut assignment = Assignment::empty(func.vregs());
657 assignment.put(left, Place::Reg(RAX));
658 assignment.put(right, Place::Reg(RCX));
659 assignment.put(sum, Place::Reg(RCX));
660
661 let said = said(&func, &order, &live, &assignment);
664 assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
665 }
666
667 #[test]
668 fn a_two_address_instruction_may_not_write_the_register_it_read_over_its_own_last_answer() {
669 let mut names = Interner::new();
670 let mut func = Func::new(names.intern("f"));
671 let nop = Opcode::new(names.intern("x64.nop"));
672 let add = Opcode::new(names.intern("x64.add"));
673 let head = func.create_block();
674 let latch = func.create_block();
675 let out = func.create_block();
676 let source = func.new_vreg(GPR);
677 let carried = func.new_vreg(GPR);
678 func.build(head, nop).def(source, GPR).finish();
679 func.build(head, nop).def(carried, GPR).finish();
680 *func.succs_mut(head) = vec![BlockCall::to(latch)];
681 func.build(latch, add)
682 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
683 .uses(source, GPR)
684 .uses(carried, GPR)
685 .finish();
686 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
687 func.build(out, nop).uses(carried, GPR).finish();
688
689 let (order, live) = read(&func);
690 let mut assignment = Assignment::empty(func.vregs());
691 assignment.put(source, Place::Reg(RAX));
692 assignment.put(carried, Place::Reg(RAX));
693
694 let said = said(&func, &order, &live, &assignment);
698 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
699 }
700
701 #[test]
702 fn a_report_names_every_problem() {
703 let mut names = Interner::new();
704 let mut func = Func::new(names.intern("f"));
705 let opcode = Opcode::new(names.intern("x64.nop"));
706 let block = func.create_block();
707 let first = func.new_vreg(GPR);
708 let second = func.new_vreg(GPR);
709 func.build(block, opcode).def(first, GPR).finish();
710 func.build(block, opcode).def(second, GPR).finish();
711 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
712
713 let (order, live) = read(&func);
714 let mut assignment = Assignment::empty(func.vregs());
715 assignment.put(first, Place::Reg(RAX));
716 assignment.put(second, Place::Reg(RAX));
717
718 let problems = check(&func, &order, &live, &assignment);
719 assert_eq!(
720 report(&problems),
721 "the allocation is wrong in 1 place\n %0 and %1 are both live and both in register 0"
722 );
723 assert_eq!(report(&[]), "the allocation is wrong in 0 places");
724 }
725}