1use std::collections::HashMap;
66use std::fmt;
67
68use rucc_mir::{Block, Constraint, Func, Inst, Operand, Param, Reg, Role};
69use rucc_target::RegClass;
70
71use crate::assign::{Assignment, Place};
72use crate::rewrite::{At, Edit};
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum Fault {
77 Read {
79 inst: Inst,
81 place: Place,
83 class: RegClass,
85 wanted: Reg,
87 found: Option<Reg>,
89 },
90 Arrived {
92 from: Block,
94 to: Block,
96 place: Place,
98 class: RegClass,
100 wanted: Reg,
102 found: Option<Reg>,
104 },
105}
106
107impl fmt::Display for Fault {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 Fault::Read { inst, place, class, wanted, found } => write!(
111 f,
112 "instruction {} reads {} out of {}, which {}",
113 inst.index(),
114 name(*wanted),
115 spelled(*class, *place),
116 holding(*found)
117 ),
118 Fault::Arrived { from, to, place, class, wanted, found } => write!(
119 f,
120 "the edge from block {} to block {} was to leave {} in {}, which {}",
121 from.index(),
122 to.index(),
123 name(*wanted),
124 spelled(*class, *place),
125 holding(*found)
126 ),
127 }
128 }
129}
130
131#[derive(Debug, Clone, Default)]
136pub struct Shape {
137 operands: Vec<Vec<Operand>>,
139 params: Vec<Vec<Param>>,
141 succs: Vec<Vec<Call>>,
143}
144
145#[derive(Debug, Clone)]
147struct Call {
148 block: Block,
149 args: Vec<Reg>,
150}
151
152#[must_use]
159pub fn shape(func: &Func) -> Shape {
160 let mut shape = Shape {
161 operands: vec![Vec::new(); func.inst_count()],
162 params: vec![Vec::new(); func.block_count()],
163 succs: vec![Vec::new(); func.block_count()],
164 };
165 for block in func.blocks() {
166 shape.params[block.index()] = func[block].params.clone();
167 shape.succs[block.index()] = func[block]
168 .succs
169 .iter()
170 .map(|call| Call { block: call.block, args: call.args.clone() })
171 .collect();
172 for inst in func.insts(block) {
173 shape.operands[inst.index()] = func[func[inst].operands].to_vec();
174 }
175 }
176 shape
177}
178
179#[must_use]
189pub fn trace(func: &Func, shape: &Shape, assignment: &Assignment, edits: &[Edit]) -> Vec<Fault> {
190 let filed = File::of(func, edits);
191 let mut entry: Vec<Option<State>> = vec![None; func.block_count()];
192 let Some(start) = func.entry() else { return Vec::new() };
193
194 entry[start.index()] = Some(State::new());
199 let mut queue = vec![start];
200 let mut ignored = Vec::new();
201 while let Some(block) = queue.pop() {
202 let Some(state) = entry[block.index()].clone() else { continue };
203 ignored.clear();
204 let out = body(func, shape, &filed, edits, block, state, &mut ignored);
205 let single = shape.succs[block.index()].len() == 1;
206 for call in &shape.succs[block.index()] {
207 let over =
208 cross(shape, &filed, edits, assignment, block, call, single, &out, &mut ignored);
209 if narrow(&mut entry[call.block.index()], &over) {
210 queue.push(call.block);
211 }
212 }
213 }
214
215 let mut faults = Vec::new();
219 for block in func.blocks() {
220 let Some(state) = entry[block.index()].clone() else { continue };
221 let out = body(func, shape, &filed, edits, block, state, &mut faults);
222 let single = shape.succs[block.index()].len() == 1;
223 for call in &shape.succs[block.index()] {
224 cross(shape, &filed, edits, assignment, block, call, single, &out, &mut faults);
225 }
226 }
227 faults
228}
229
230#[must_use]
232pub fn report(faults: &[Fault]) -> String {
233 let places = if faults.len() == 1 { "place" } else { "places" };
234 let mut report = format!("the rewrite loses a value in {} {places}", faults.len());
235 for fault in faults {
236 report.push_str("\n ");
237 report.push_str(&fault.to_string());
238 }
239 report
240}
241
242type State = HashMap<Spot, Reg>;
248
249type Spot = (u8, Place);
255
256fn spot(class: RegClass, place: Place) -> Spot {
258 (class.number(), place)
259}
260
261#[derive(Debug, Default)]
267struct File {
268 before: Vec<Vec<usize>>,
269 after: Vec<Vec<usize>>,
270 start_of: Vec<Vec<usize>>,
271 end_of: Vec<Vec<usize>>,
272}
273
274impl File {
275 fn of(func: &Func, edits: &[Edit]) -> Self {
277 let mut filed = File {
278 before: vec![Vec::new(); func.inst_count()],
279 after: vec![Vec::new(); func.inst_count()],
280 start_of: vec![Vec::new(); func.block_count()],
281 end_of: vec![Vec::new(); func.block_count()],
282 };
283 for (index, edit) in edits.iter().enumerate() {
284 match edit.at {
285 At::Before(inst) => filed.before[inst.index()].push(index),
286 At::After(inst) => filed.after[inst.index()].push(index),
287 At::StartOf(block) => filed.start_of[block.index()].push(index),
288 At::EndOf(block) => filed.end_of[block.index()].push(index),
289 }
290 }
291 filed
292 }
293}
294
295fn body(
300 func: &Func,
301 shape: &Shape,
302 filed: &File,
303 edits: &[Edit],
304 block: Block,
305 mut state: State,
306 faults: &mut Vec<Fault>,
307) -> State {
308 for inst in func.insts(block) {
309 for &edit in &filed.before[inst.index()] {
310 moved(&mut state, &edits[edit]);
311 }
312 let was = &shape.operands[inst.index()];
313 let now = &func[func[inst].operands];
314
315 for (operand, place) in was.iter().zip(now.iter()) {
326 let Some(at) = landed(place) else { continue };
327 if operand.role != Role::Use {
328 continue;
329 }
330 if operand.reg.phys().is_some() {
331 continue;
332 }
333 let found = state.get(&spot(operand.class, at)).copied();
334 if found != Some(operand.reg) {
335 let (class, wanted) = (operand.class, operand.reg);
336 faults.push(Fault::Read { inst, place: at, class, wanted, found });
337 }
338 }
339 for (operand, place) in was.iter().zip(now.iter()) {
340 let Some(at) = landed(place) else { continue };
341 if !operand.role.is_def() {
342 continue;
343 }
344 let held = state.get(&spot(operand.class, at)).and_then(|&held| func.width(held));
347 let under = |above| held.is_some_and(|width| width <= above);
348 if matches!(operand.constraint, Constraint::Above(above) if under(above)) {
349 continue;
350 }
351 state.insert(spot(operand.class, at), operand.reg);
352 }
353 for &edit in &filed.after[inst.index()] {
354 moved(&mut state, &edits[edit]);
355 }
356 }
357 state
358}
359
360#[allow(clippy::too_many_arguments, reason = "an edge is the two blocks and everything between")]
366fn cross(
367 shape: &Shape,
368 filed: &File,
369 edits: &[Edit],
370 assignment: &Assignment,
371 from: Block,
372 call: &Call,
373 single: bool,
374 out: &State,
375 faults: &mut Vec<Fault>,
376) -> State {
377 let mut state = out.clone();
378 let params = &shape.params[call.block.index()];
379 let list =
380 if single { &filed.end_of[from.index()] } else { &filed.start_of[call.block.index()] };
381 for &edit in list {
382 moved(&mut state, &edits[edit]);
383 }
384
385 let mut arrived = Vec::new();
389 for (param, &arg) in params.iter().zip(&call.args) {
390 let Some(at) = home(assignment, param.reg) else { continue };
391 let found = state.get(&spot(param.class, at)).copied();
392 if found != Some(arg) {
393 let (to, class) = (call.block, param.class);
394 faults.push(Fault::Arrived { from, to, place: at, class, wanted: arg, found });
395 }
396 arrived.push((spot(param.class, at), param.reg));
397 }
398 for (spot, reg) in arrived {
399 state.insert(spot, reg);
400 }
401 state
402}
403
404fn moved(state: &mut State, edit: &Edit) {
409 let to = spot(edit.class, edit.mov.to);
410 let from = spot(edit.class, edit.mov.from);
411 match state.get(&from).copied() {
412 Some(reg) => state.insert(to, reg),
413 None => state.remove(&to),
414 };
415}
416
417fn narrow(entry: &mut Option<State>, over: &State) -> bool {
423 match entry {
424 None => {
425 *entry = Some(over.clone());
426 true
427 }
428 Some(state) => {
429 let before = state.len();
430 state.retain(|spot, reg| over.get(spot) == Some(&*reg));
431 state.len() != before
432 }
433 }
434}
435
436fn landed(operand: &Operand) -> Option<Place> {
438 operand.reg.phys().map(Place::Reg)
439}
440
441fn home(assignment: &Assignment, reg: Reg) -> Option<Place> {
447 assignment.place(reg).or_else(|| reg.phys().map(Place::Reg))
448}
449
450fn name(reg: Reg) -> String {
452 match reg.number() {
453 Some(number) => format!("%{number}"),
454 None => match reg.phys() {
455 Some(at) => format!("register {}", at.number()),
456 None => "nothing".to_owned(),
457 },
458 }
459}
460
461fn spelled(class: RegClass, place: Place) -> String {
464 match place {
465 Place::Reg(at) => format!("register {} of class {}", at.number(), class.number()),
466 Place::Slot(slot) => format!("slot {slot}"),
467 }
468}
469
470fn holding(found: Option<Reg>) -> String {
472 match found {
473 Some(reg) => format!("holds {}", name(reg)),
474 None => "holds nothing anything has put there".to_owned(),
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use rucc_base::Interner;
481 use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
482 use rucc_target::x86_64::{GPR, RAX, RSP, SYSV};
483
484 use super::*;
485 use crate::assign::{Env, assign};
486 use crate::live::Live;
487 use crate::moves::Move;
488 use crate::order::Order;
489 use crate::rewrite::rewrite;
490
491 fn env() -> Env {
493 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
494 Env::new().with(GPR, order, scratch)
495 }
496
497 fn narrow(count: usize) -> Env {
499 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
500 }
501
502 fn allocate(func: &mut Func, env: &Env) -> (Shape, Assignment, Vec<Edit>) {
504 let order = Order::of(func);
505 let live = Live::of(func, &order);
506 let mut assignment = assign(func, &order, &live, env);
507 let taken = shape(func);
508 let edits = rewrite(func, &mut assignment, env);
509 (taken, assignment, edits)
510 }
511
512 fn said(func: &Func, taken: &Shape, assignment: &Assignment, edits: &[Edit]) -> Vec<String> {
514 trace(func, taken, assignment, edits).iter().map(ToString::to_string).collect()
515 }
516
517 #[test]
518 fn every_value_an_instruction_reads_is_the_one_that_was_written_where_it_reads_it() {
519 let mut names = Interner::new();
520 let mut func = Func::new(names.intern("f"));
521 let opcode = Opcode::new(names.intern("x64.nop"));
522 let block = func.create_block();
523 let first = func.new_vreg(GPR);
524 let second = func.new_vreg(GPR);
525 func.build(block, opcode).def(first, GPR).finish();
526 func.build(block, opcode).def(second, GPR).finish();
527 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
528
529 let (taken, assignment, edits) = allocate(&mut func, &env());
530 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
531 }
532
533 #[test]
534 fn a_value_that_lives_on_the_stack_is_followed_through_the_slot_it_lives_in() {
535 let mut names = Interner::new();
536 let mut func = Func::new(names.intern("f"));
537 let opcode = Opcode::new(names.intern("x64.nop"));
538 let block = func.create_block();
539 let first = func.new_vreg(GPR);
540 let second = func.new_vreg(GPR);
541 let third = func.new_vreg(GPR);
542 func.build(block, opcode).def(first, GPR).finish();
543 func.build(block, opcode).def(second, GPR).finish();
544 func.build(block, opcode).def(third, GPR).finish();
545 func.build(block, opcode).uses(first, GPR).uses(second, GPR).uses(third, GPR).finish();
546
547 let (taken, assignment, edits) = allocate(&mut func, &narrow(2));
550 assert_eq!(assignment.spilled(), 1);
551 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
552 }
553
554 #[test]
555 fn an_operand_the_rewrite_pointed_at_the_wrong_register_is_reported() {
556 let mut names = Interner::new();
557 let mut func = Func::new(names.intern("f"));
558 let opcode = Opcode::new(names.intern("x64.nop"));
559 let block = func.create_block();
560 let first = func.new_vreg(GPR);
561 let second = func.new_vreg(GPR);
562 let read = {
563 func.build(block, opcode).def(first, GPR).finish();
564 func.build(block, opcode).def(second, GPR).finish();
565 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish()
566 };
567
568 let (taken, assignment, edits) = allocate(&mut func, &env());
569 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
570
571 let list = func[read].operands;
575 func[list][0].reg = func[list][1].reg;
576
577 assert_eq!(
578 said(&func, &taken, &assignment, &edits),
579 ["instruction 2 reads %0 out of register 1 of class 0, which holds %1"]
580 );
581 }
582
583 #[test]
584 fn a_move_that_writes_the_wrong_register_is_an_instruction_reading_the_wrong_value() {
585 let mut names = Interner::new();
586 let mut func = Func::new(names.intern("f"));
587 let opcode = Opcode::new(names.intern("x64.nop"));
588 let block = func.create_block();
589 let dividend = func.new_vreg(GPR);
590 let quotient = func.new_vreg(GPR);
591 func.build(block, opcode).def(dividend, GPR).finish();
592 func.build(block, opcode)
593 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
594 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
595 .finish();
596 func.build(block, opcode).uses(quotient, GPR).finish();
597 func.build(block, opcode).uses(dividend, GPR).finish();
598
599 let (taken, assignment, mut edits) = allocate(&mut func, &env());
600 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
601
602 edits[0].mov.to = Place::Reg(SYSV.int_order[2]);
606 assert_eq!(
607 said(&func, &taken, &assignment, &edits),
608 ["instruction 1 reads %0 out of register 0 of class 0, which holds nothing anything \
609 has put there"]
610 );
611 }
612
613 #[test]
614 fn a_value_carried_over_an_edge_goes_on_under_the_name_the_block_it_arrives_in_gives_it() {
615 let mut names = Interner::new();
616 let mut func = Func::new(names.intern("f"));
617 let opcode = Opcode::new(names.intern("x64.nop"));
618 let head = func.create_block();
619 let tail = func.create_block();
620 let value = func.new_vreg(GPR);
621 func.build(head, opcode).def(value, GPR).finish();
622 let arrived = func.append_param(tail, GPR);
623 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![value])];
624 func.build(tail, opcode).uses(arrived, GPR).finish();
625
626 let (taken, assignment, edits) = allocate(&mut func, &env());
627 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
628 }
629
630 #[test]
631 fn two_values_that_swap_on_an_edge_arrive_the_right_way_round_only_in_the_order_they_were_put_in()
632 {
633 let mut names = Interner::new();
634 let mut func = Func::new(names.intern("f"));
635 let opcode = Opcode::new(names.intern("x64.nop"));
636 let head = func.create_block();
637 let body = func.create_block();
638 let first = func.new_vreg(GPR);
639 let second = func.new_vreg(GPR);
640 func.build(head, opcode).def(first, GPR).finish();
641 func.build(head, opcode).def(second, GPR).finish();
642 let left = func.append_param(body, GPR);
643 let right = func.append_param(body, GPR);
644 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
645 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
646 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
647
648 let (taken, assignment, mut edits) = allocate(&mut func, &env());
649 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
650
651 let (to, from) = (Place::Reg(SYSV.int_order[0]), Place::Reg(SYSV.int_order[1]));
655 edits.truncate(edits.len() - 3);
656 edits.push(Edit { at: At::EndOf(body), mov: Move::new(to, from), class: GPR });
657 edits.push(Edit { at: At::EndOf(body), mov: Move::new(from, to), class: GPR });
658
659 assert_eq!(
660 said(&func, &taken, &assignment, &edits),
661 ["the edge from block 1 to block 1 was to leave %2 in register 1 of class 0, which \
662 holds %3"]
663 );
664 }
665
666 #[test]
667 fn a_loop_is_walked_until_it_settles_rather_than_reported_the_first_time_round() {
668 let mut names = Interner::new();
669 let mut func = Func::new(names.intern("f"));
670 let opcode = Opcode::new(names.intern("x64.nop"));
671 let head = func.create_block();
672 let body = func.create_block();
673 let latch = func.create_block();
674 let out = func.create_block();
675 let start = func.new_vreg(GPR);
676 func.build(head, opcode).def(start, GPR).finish();
677 let counter = func.append_param(body, GPR);
678 *func.succs_mut(head) = vec![BlockCall::with(body, vec![start])];
679 let next = func.new_vreg(GPR);
680 func.build(body, opcode).def(next, GPR).uses(counter, GPR).finish();
681 *func.succs_mut(body) = vec![BlockCall::to(latch), BlockCall::to(out)];
682 func.build(latch, opcode).finish();
683 *func.succs_mut(latch) = vec![BlockCall::with(body, vec![next])];
684 func.build(out, opcode).finish();
685
686 let (taken, assignment, edits) = allocate(&mut func, &env());
689 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
690 }
691
692 #[test]
693 fn a_value_the_two_ways_into_a_block_leave_in_different_places_is_not_one_it_may_read() {
694 let mut names = Interner::new();
695 let mut func = Func::new(names.intern("f"));
696 let opcode = Opcode::new(names.intern("x64.nop"));
697 let entry = func.create_block();
698 let arm = func.create_block();
699 let tail = func.create_block();
700 let value = func.new_vreg(GPR);
701 func.build(entry, opcode).def(value, GPR).finish();
702 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
703 func.build(arm, opcode).finish();
704 *func.succs_mut(arm) = vec![BlockCall::to(tail)];
705 func.build(tail, opcode).uses(value, GPR).finish();
706
707 let (taken, assignment, mut edits) = allocate(&mut func, &env());
708 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
709
710 let at = Place::Reg(SYSV.int_order[0]);
714 let elsewhere = Place::Reg(SYSV.int_order[1]);
715 edits.push(Edit { at: At::EndOf(arm), mov: Move::new(at, elsewhere), class: GPR });
716
717 assert_eq!(
718 said(&func, &taken, &assignment, &edits),
719 ["instruction 2 reads %0 out of register 0 of class 0, which holds nothing anything \
720 has put there"]
721 );
722 }
723
724 #[test]
725 fn a_register_the_function_names_itself_is_one_it_may_read_without_writing_it_first() {
726 let mut names = Interner::new();
727 let mut func = Func::new(names.intern("f"));
728 let opcode = Opcode::new(names.intern("x64.nop"));
729 let block = func.create_block();
730 func.build(block, opcode).operand(Operand::read(Reg::physical(RSP), GPR)).finish();
731
732 let (taken, assignment, edits) = allocate(&mut func, &env());
736 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
737 }
738
739 #[test]
740 fn a_register_the_function_names_itself_is_readable_after_a_value_has_been_put_in_it() {
741 let mut names = Interner::new();
742 let mut func = Func::new(names.intern("f"));
743 let opcode = Opcode::new(names.intern("x64.nop"));
744 let block = func.create_block();
745 let argument = func.new_vreg(GPR);
746 func.build(block, opcode)
747 .operand(Operand::write(argument, GPR).with(Constraint::Fixed(SYSV.int_order[0])))
748 .finish();
749 func.build(block, opcode)
750 .operand(Operand::read(Reg::physical(SYSV.int_order[0]), GPR))
751 .finish();
752
753 let (taken, assignment, edits) = allocate(&mut func, &env());
759 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
760 }
761
762 #[test]
763 fn what_is_wrong_is_reported_in_a_sentence_that_says_how_many_things_are_wrong() {
764 let fault = Fault::Read {
765 inst: Inst::new(3),
766 place: Place::Slot(1),
767 class: GPR,
768 wanted: Reg::virtual_reg(2),
769 found: None,
770 };
771 assert_eq!(
772 report(&[fault]),
773 "the rewrite loses a value in 1 place\n instruction 3 reads %2 out of slot 1, which \
774 holds nothing anything has put there"
775 );
776 }
777}