1use std::fmt;
45
46use rucc_mir::{Constraint, Func, Inst, Reg, Role};
47use rucc_target::{PhysReg, RegClass};
48
49use crate::assign::{Assignment, Place};
50use crate::live::{Live, Range};
51use crate::order::{Order, Point};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum Problem {
56 Nowhere {
58 reg: Reg,
60 },
61 Shared {
64 first: Reg,
66 second: Reg,
68 place: Place,
70 },
71 InTheWay {
74 reg: Reg,
76 at: PhysReg,
78 inst: Inst,
80 },
81 NotOnTheStack {
83 reg: Reg,
85 inst: Inst,
87 },
88}
89
90impl fmt::Display for Problem {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Problem::Nowhere { reg } => write!(f, "{} has nowhere to live", name(*reg)),
94 Problem::Shared { first, second, place } => {
95 let (first, second) = (name(*first), name(*second));
96 write!(f, "{first} and {second} are both live and both in {}", place_name(*place))
97 }
98 Problem::InTheWay { reg, at, inst } => {
99 let reg = name(*reg);
100 let inst = inst.index();
101 write!(f, "{reg} is in register {}, which instruction {inst} wants", at.number())
102 }
103 Problem::NotOnTheStack { reg, inst } => {
104 let reg = name(*reg);
105 write!(f, "{reg} is not on the stack, and instruction {} needs it", inst.index())
106 }
107 }
108 }
109}
110
111#[must_use]
122pub fn check(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<Problem> {
123 let mut problems = Vec::new();
124 let reuses = reuses(func, order);
125 let mut values = Vec::new();
126 for (number, reuse) in reuses.iter().enumerate() {
127 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
128 let (Some(mut range), Some(class)) = (live.range(reg), func.class_of(reg)) else {
129 continue;
130 };
131 let Some(place) = assignment.place(reg) else {
132 problems.push(Problem::Nowhere { reg });
133 continue;
134 };
135 if let Some(reuse) = reuse {
140 range.start = range.start.min(reuse.at);
141 }
142 values.push(Value { reg, class, range, place });
143 }
144 overlaps(&values, &reuses, live, &mut problems);
145 instructions(func, order, assignment, &values, &reuses, &mut problems);
146 problems
147}
148
149#[must_use]
151pub fn report(problems: &[Problem]) -> String {
152 let places = if problems.len() == 1 { "place" } else { "places" };
153 let mut report = format!("the allocation is wrong in {} {places}", problems.len());
154 for problem in problems {
155 report.push_str("\n ");
156 report.push_str(&problem.to_string());
157 }
158 report
159}
160
161#[derive(Debug, Clone, Copy)]
163struct Value {
164 reg: Reg,
165 class: RegClass,
166 range: Range,
167 place: Place,
168}
169
170#[derive(Debug, Clone, Copy)]
172struct Reuse {
173 source: Reg,
174 at: Point,
175}
176
177fn overlaps(values: &[Value], reuses: &[Option<Reuse>], live: &Live, problems: &mut Vec<Problem>) {
182 let mut sorted = values.to_vec();
183 sorted.sort_by_key(|value| (value.range.start, value.reg));
184 let mut active: Vec<Value> = Vec::new();
185 for value in sorted {
186 active.retain(|held| held.range.end >= value.range.start);
187 for held in &active {
188 if !together(*held, value) || coalesced(*held, value, reuses, live) {
189 continue;
190 }
191 problems.push(Problem::Shared {
192 first: held.reg,
193 second: value.reg,
194 place: value.place,
195 });
196 }
197 active.push(value);
198 }
199}
200
201fn together(first: Value, second: Value) -> bool {
207 match (first.place, second.place) {
208 (Place::Reg(first_at), Place::Reg(second_at)) => {
209 first_at == second_at && first.class == second.class
210 }
211 (Place::Slot(first_slot), Place::Slot(second_slot)) => first_slot == second_slot,
212 _ => false,
213 }
214}
215
216fn coalesced(first: Value, second: Value, reuses: &[Option<Reuse>], live: &Live) -> bool {
223 let pair = |source: Value, dest: Value| {
224 let Some(reuse) = reuses[index(dest.reg)] else { return false };
225 reuse.source == source.reg && live.range(source.reg).is_some_and(|r| r.end == reuse.at)
226 };
227 pair(first, second) || pair(second, first)
228}
229
230fn instructions(
233 func: &Func,
234 order: &Order,
235 assignment: &Assignment,
236 values: &[Value],
237 reuses: &[Option<Reuse>],
238 problems: &mut Vec<Problem>,
239) {
240 for block in func.blocks() {
241 for inst in func.insts(block) {
242 for operand in &func[func[inst].operands] {
243 if operand.constraint == Constraint::Stack
244 && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
245 {
246 problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
247 }
248 let at = match operand.constraint {
252 Constraint::Fixed(at) => Some(at),
253 _ => operand.reg.phys(),
254 };
255 let Some(at) = at else { continue };
256 let early = order.early(inst);
257 let point = if operand.role == Role::Def { order.late(inst) } else { early };
258 for value in values {
259 let mine = value.reg == operand.reg
260 || reuses[index(value.reg)].is_some_and(|reuse| {
261 reuse.source == operand.reg
262 && reuse.at == early
263 && value.place == Place::Reg(at)
264 });
265 if mine || value.class != operand.class {
266 continue;
267 }
268 if value.place == Place::Reg(at) && value.range.covers(point) {
269 problems.push(Problem::InTheWay { reg: value.reg, at, inst });
270 }
271 }
272 }
273 }
274 }
275}
276
277fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
279 let mut reuses = vec![None; func.vregs()];
280 for block in func.blocks() {
281 for inst in func.insts(block) {
282 let operands = &func[func[inst].operands];
283 for operand in operands {
284 let Constraint::Reuse(other) = operand.constraint else { continue };
285 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
286 let Some(number) = number else { continue };
287 let source = operands[usize::from(other)].reg;
288 reuses[number] = Some(Reuse { source, at: order.early(inst) });
289 }
290 }
291 }
292 reuses
293}
294
295fn index(reg: Reg) -> usize {
298 reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
299}
300
301fn name(reg: Reg) -> String {
303 match reg.number() {
304 Some(number) => format!("%{number}"),
305 None => format!("register {}", reg.phys().expect("a physical register").number()),
306 }
307}
308
309fn place_name(place: Place) -> String {
312 match place {
313 Place::Reg(at) => format!("register {}", at.number()),
314 Place::Slot(slot) => format!("slot {slot}"),
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use rucc_base::Interner;
321 use rucc_mir::{Opcode, Operand};
322 use rucc_target::x86_64::{GPR, RAX, RCX, SYSV};
323
324 use super::*;
325 use crate::assign::{Env, assign};
326
327 fn env() -> Env {
329 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
330 Env::new().with(GPR, order, scratch)
331 }
332
333 fn allocated(func: &Func) -> Vec<String> {
336 let order = Order::of(func);
337 let live = Live::of(func, &order);
338 let assignment = assign(func, &order, &live, &env());
339 said(func, &order, &live, &assignment)
340 }
341
342 fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
344 check(func, order, live, assignment).iter().map(ToString::to_string).collect()
345 }
346
347 fn read(func: &Func) -> (Order, Live) {
349 let order = Order::of(func);
350 let live = Live::of(func, &order);
351 (order, live)
352 }
353
354 #[test]
355 fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
356 let mut names = Interner::new();
357 let mut func = Func::new(names.intern("f"));
358 let opcode = Opcode::new(names.intern("x64.nop"));
359 let block = func.create_block();
360 let first = func.new_vreg(GPR);
361 let second = func.new_vreg(GPR);
362 func.build(block, opcode).def(first, GPR).finish();
363 func.build(block, opcode).def(second, GPR).finish();
364 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
365
366 assert_eq!(allocated(&func), Vec::<String>::new());
367 }
368
369 #[test]
370 fn a_value_with_nowhere_to_live_is_found() {
371 let mut names = Interner::new();
372 let mut func = Func::new(names.intern("f"));
373 let opcode = Opcode::new(names.intern("x64.nop"));
374 let block = func.create_block();
375 let only = func.new_vreg(GPR);
376 func.build(block, opcode).def(only, GPR).finish();
377 func.build(block, opcode).uses(only, GPR).finish();
378
379 let (order, live) = read(&func);
380 let assignment = Assignment::empty(func.vregs());
381
382 assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
383 }
384
385 #[test]
386 fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
387 let mut names = Interner::new();
388 let mut func = Func::new(names.intern("f"));
389 let opcode = Opcode::new(names.intern("x64.nop"));
390 let block = func.create_block();
391 let first = func.new_vreg(GPR);
392 let second = func.new_vreg(GPR);
393 func.build(block, opcode).def(first, GPR).finish();
394 func.build(block, opcode).def(second, GPR).finish();
395 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
396
397 let (order, live) = read(&func);
398 let mut assignment = Assignment::empty(func.vregs());
399 assignment.put(first, Place::Reg(RAX));
400 assignment.put(second, Place::Reg(RAX));
401
402 let said = said(&func, &order, &live, &assignment);
403 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
404 }
405
406 #[test]
407 fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
408 let mut names = Interner::new();
409 let mut func = Func::new(names.intern("f"));
410 let opcode = Opcode::new(names.intern("x64.nop"));
411 let block = func.create_block();
412 let first = func.new_vreg(GPR);
413 let second = func.new_vreg(GPR);
414 func.build(block, opcode).def(first, GPR).finish();
415 func.build(block, opcode).def(second, GPR).finish();
416 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
417
418 let (order, live) = read(&func);
419 let mut assignment = Assignment::empty(func.vregs());
420 let slot = assignment.take_slot(GPR);
421 assignment.put(first, Place::Slot(slot));
422 assignment.put(second, Place::Slot(slot));
423
424 let said = said(&func, &order, &live, &assignment);
425 assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
426 }
427
428 #[test]
429 fn two_values_that_are_never_both_wanted_may_share_anything() {
430 let mut names = Interner::new();
431 let mut func = Func::new(names.intern("f"));
432 let opcode = Opcode::new(names.intern("x64.nop"));
433 let block = func.create_block();
434 let first = func.new_vreg(GPR);
435 let second = func.new_vreg(GPR);
436 func.build(block, opcode).def(first, GPR).finish();
437 func.build(block, opcode).uses(first, GPR).finish();
438 func.build(block, opcode).def(second, GPR).finish();
439 func.build(block, opcode).uses(second, GPR).finish();
440
441 let (order, live) = read(&func);
442 let mut assignment = Assignment::empty(func.vregs());
443 assignment.put(first, Place::Reg(RAX));
444 assignment.put(second, Place::Reg(RAX));
445
446 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
447 }
448
449 #[test]
450 fn a_value_left_in_a_register_an_instruction_wants_is_found() {
451 let mut names = Interner::new();
452 let mut func = Func::new(names.intern("f"));
453 let nop = Opcode::new(names.intern("x64.nop"));
454 let divide = Opcode::new(names.intern("x64.idiv"));
455 let block = func.create_block();
456 let held = func.new_vreg(GPR);
457 let dividend = func.new_vreg(GPR);
458 func.build(block, nop).def(held, GPR).finish();
459 func.build(block, nop).def(dividend, GPR).finish();
460 func.build(block, divide)
463 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
464 .finish();
465 func.build(block, nop).uses(held, GPR).finish();
466
467 let (order, live) = read(&func);
468 let mut assignment = Assignment::empty(func.vregs());
469 assignment.put(held, Place::Reg(RAX));
470 assignment.put(dividend, Place::Reg(RCX));
471
472 let said = said(&func, &order, &live, &assignment);
473 assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
474 }
475
476 #[test]
477 fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
478 let mut names = Interner::new();
479 let mut func = Func::new(names.intern("f"));
480 let nop = Opcode::new(names.intern("x64.nop"));
481 let divide = Opcode::new(names.intern("x64.idiv"));
482 let block = func.create_block();
483 let dividend = func.new_vreg(GPR);
484 func.build(block, nop).def(dividend, GPR).finish();
485 func.build(block, divide)
486 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
487 .finish();
488
489 let (order, live) = read(&func);
490 let mut assignment = Assignment::empty(func.vregs());
491 assignment.put(dividend, Place::Reg(RAX));
492
493 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
496 }
497
498 #[test]
499 fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
500 let mut names = Interner::new();
501 let mut func = Func::new(names.intern("f"));
502 let nop = Opcode::new(names.intern("x64.nop"));
503 let wide = Opcode::new(names.intern("x64.wide"));
504 let block = func.create_block();
505 let only = func.new_vreg(GPR);
506 func.build(block, nop).def(only, GPR).finish();
507 func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
508
509 let (order, live) = read(&func);
510 let mut assignment = Assignment::empty(func.vregs());
511 assignment.put(only, Place::Reg(RAX));
512
513 let said = said(&func, &order, &live, &assignment);
514 assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
515 }
516
517 #[test]
518 fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
519 let mut names = Interner::new();
520 let mut func = Func::new(names.intern("f"));
521 let nop = Opcode::new(names.intern("x64.nop"));
522 let add = Opcode::new(names.intern("x64.add"));
523 let block = func.create_block();
524 let left = func.new_vreg(GPR);
525 let right = func.new_vreg(GPR);
526 let sum = func.new_vreg(GPR);
527 func.build(block, nop).def(left, GPR).finish();
528 func.build(block, nop).def(right, GPR).finish();
529 func.build(block, add)
530 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
531 .uses(left, GPR)
532 .uses(right, GPR)
533 .finish();
534 func.build(block, nop).uses(sum, GPR).finish();
535
536 let (order, live) = read(&func);
537 let mut assignment = Assignment::empty(func.vregs());
538 assignment.put(left, Place::Reg(RAX));
539 assignment.put(right, Place::Reg(RCX));
540 assignment.put(sum, Place::Reg(RAX));
541
542 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
545 }
546
547 #[test]
548 fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
549 let mut names = Interner::new();
550 let mut func = Func::new(names.intern("f"));
551 let nop = Opcode::new(names.intern("x64.nop"));
552 let add = Opcode::new(names.intern("x64.add"));
553 let block = func.create_block();
554 let left = func.new_vreg(GPR);
555 let right = func.new_vreg(GPR);
556 let sum = func.new_vreg(GPR);
557 func.build(block, nop).def(left, GPR).finish();
558 func.build(block, nop).def(right, GPR).finish();
559 func.build(block, add)
560 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
561 .uses(left, GPR)
562 .uses(right, GPR)
563 .finish();
564 func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
565
566 let (order, live) = read(&func);
567 let mut assignment = Assignment::empty(func.vregs());
568 assignment.put(left, Place::Reg(RAX));
569 assignment.put(right, Place::Reg(RCX));
570 assignment.put(sum, Place::Reg(RAX));
571
572 let said = said(&func, &order, &live, &assignment);
575 assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
576 }
577
578 #[test]
579 fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
580 let mut names = Interner::new();
581 let mut func = Func::new(names.intern("f"));
582 let nop = Opcode::new(names.intern("x64.nop"));
583 let add = Opcode::new(names.intern("x64.add"));
584 let block = func.create_block();
585 let left = func.new_vreg(GPR);
586 let right = func.new_vreg(GPR);
587 let sum = func.new_vreg(GPR);
588 func.build(block, nop).def(left, GPR).finish();
589 func.build(block, nop).def(right, GPR).finish();
590 func.build(block, add)
591 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
592 .uses(left, GPR)
593 .uses(right, GPR)
594 .finish();
595 func.build(block, nop).uses(sum, GPR).finish();
596
597 let (order, live) = read(&func);
598 let mut assignment = Assignment::empty(func.vregs());
599 assignment.put(left, Place::Reg(RAX));
600 assignment.put(right, Place::Reg(RCX));
601 assignment.put(sum, Place::Reg(RCX));
602
603 let said = said(&func, &order, &live, &assignment);
606 assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
607 }
608
609 #[test]
610 fn a_report_names_every_problem() {
611 let mut names = Interner::new();
612 let mut func = Func::new(names.intern("f"));
613 let opcode = Opcode::new(names.intern("x64.nop"));
614 let block = func.create_block();
615 let first = func.new_vreg(GPR);
616 let second = func.new_vreg(GPR);
617 func.build(block, opcode).def(first, GPR).finish();
618 func.build(block, opcode).def(second, GPR).finish();
619 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
620
621 let (order, live) = read(&func);
622 let mut assignment = Assignment::empty(func.vregs());
623 assignment.put(first, Place::Reg(RAX));
624 assignment.put(second, Place::Reg(RAX));
625
626 let problems = check(&func, &order, &live, &assignment);
627 assert_eq!(
628 report(&problems),
629 "the allocation is wrong in 1 place\n %0 and %1 are both live and both in register 0"
630 );
631 assert_eq!(report(&[]), "the allocation is wrong in 0 places");
632 }
633}