1use std::collections::HashMap;
66use std::fmt;
67
68use rucc_mir::{Block, 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 state.insert(spot(operand.class, at), operand.reg);
345 }
346 for &edit in &filed.after[inst.index()] {
347 moved(&mut state, &edits[edit]);
348 }
349 }
350 state
351}
352
353#[allow(clippy::too_many_arguments, reason = "an edge is the two blocks and everything between")]
359fn cross(
360 shape: &Shape,
361 filed: &File,
362 edits: &[Edit],
363 assignment: &Assignment,
364 from: Block,
365 call: &Call,
366 single: bool,
367 out: &State,
368 faults: &mut Vec<Fault>,
369) -> State {
370 let mut state = out.clone();
371 let params = &shape.params[call.block.index()];
372 let list =
373 if single { &filed.end_of[from.index()] } else { &filed.start_of[call.block.index()] };
374 for &edit in list {
375 moved(&mut state, &edits[edit]);
376 }
377
378 let mut arrived = Vec::new();
382 for (param, &arg) in params.iter().zip(&call.args) {
383 let Some(at) = home(assignment, param.reg) else { continue };
384 let found = state.get(&spot(param.class, at)).copied();
385 if found != Some(arg) {
386 let (to, class) = (call.block, param.class);
387 faults.push(Fault::Arrived { from, to, place: at, class, wanted: arg, found });
388 }
389 arrived.push((spot(param.class, at), param.reg));
390 }
391 for (spot, reg) in arrived {
392 state.insert(spot, reg);
393 }
394 state
395}
396
397fn moved(state: &mut State, edit: &Edit) {
402 let to = spot(edit.class, edit.mov.to);
403 let from = spot(edit.class, edit.mov.from);
404 match state.get(&from).copied() {
405 Some(reg) => state.insert(to, reg),
406 None => state.remove(&to),
407 };
408}
409
410fn narrow(entry: &mut Option<State>, over: &State) -> bool {
416 match entry {
417 None => {
418 *entry = Some(over.clone());
419 true
420 }
421 Some(state) => {
422 let before = state.len();
423 state.retain(|spot, reg| over.get(spot) == Some(&*reg));
424 state.len() != before
425 }
426 }
427}
428
429fn landed(operand: &Operand) -> Option<Place> {
431 operand.reg.phys().map(Place::Reg)
432}
433
434fn home(assignment: &Assignment, reg: Reg) -> Option<Place> {
440 assignment.place(reg).or_else(|| reg.phys().map(Place::Reg))
441}
442
443fn name(reg: Reg) -> String {
445 match reg.number() {
446 Some(number) => format!("%{number}"),
447 None => match reg.phys() {
448 Some(at) => format!("register {}", at.number()),
449 None => "nothing".to_owned(),
450 },
451 }
452}
453
454fn spelled(class: RegClass, place: Place) -> String {
457 match place {
458 Place::Reg(at) => format!("register {} of class {}", at.number(), class.number()),
459 Place::Slot(slot) => format!("slot {slot}"),
460 }
461}
462
463fn holding(found: Option<Reg>) -> String {
465 match found {
466 Some(reg) => format!("holds {}", name(reg)),
467 None => "holds nothing anything has put there".to_owned(),
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use rucc_base::Interner;
474 use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
475 use rucc_target::x86_64::{GPR, RAX, RSP, SYSV};
476
477 use super::*;
478 use crate::assign::{Env, assign};
479 use crate::live::Live;
480 use crate::moves::Move;
481 use crate::order::Order;
482 use crate::rewrite::rewrite;
483
484 fn env() -> Env {
486 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
487 Env::new().with(GPR, order, scratch)
488 }
489
490 fn narrow(count: usize) -> Env {
492 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
493 }
494
495 fn allocate(func: &mut Func, env: &Env) -> (Shape, Assignment, Vec<Edit>) {
497 let order = Order::of(func);
498 let live = Live::of(func, &order);
499 let mut assignment = assign(func, &order, &live, env);
500 let taken = shape(func);
501 let edits = rewrite(func, &mut assignment, env);
502 (taken, assignment, edits)
503 }
504
505 fn said(func: &Func, taken: &Shape, assignment: &Assignment, edits: &[Edit]) -> Vec<String> {
507 trace(func, taken, assignment, edits).iter().map(ToString::to_string).collect()
508 }
509
510 #[test]
511 fn every_value_an_instruction_reads_is_the_one_that_was_written_where_it_reads_it() {
512 let mut names = Interner::new();
513 let mut func = Func::new(names.intern("f"));
514 let opcode = Opcode::new(names.intern("x64.nop"));
515 let block = func.create_block();
516 let first = func.new_vreg(GPR);
517 let second = func.new_vreg(GPR);
518 func.build(block, opcode).def(first, GPR).finish();
519 func.build(block, opcode).def(second, GPR).finish();
520 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
521
522 let (taken, assignment, edits) = allocate(&mut func, &env());
523 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
524 }
525
526 #[test]
527 fn a_value_that_lives_on_the_stack_is_followed_through_the_slot_it_lives_in() {
528 let mut names = Interner::new();
529 let mut func = Func::new(names.intern("f"));
530 let opcode = Opcode::new(names.intern("x64.nop"));
531 let block = func.create_block();
532 let first = func.new_vreg(GPR);
533 let second = func.new_vreg(GPR);
534 let third = func.new_vreg(GPR);
535 func.build(block, opcode).def(first, GPR).finish();
536 func.build(block, opcode).def(second, GPR).finish();
537 func.build(block, opcode).def(third, GPR).finish();
538 func.build(block, opcode).uses(first, GPR).uses(second, GPR).uses(third, GPR).finish();
539
540 let (taken, assignment, edits) = allocate(&mut func, &narrow(2));
543 assert_eq!(assignment.spilled(), 1);
544 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
545 }
546
547 #[test]
548 fn an_operand_the_rewrite_pointed_at_the_wrong_register_is_reported() {
549 let mut names = Interner::new();
550 let mut func = Func::new(names.intern("f"));
551 let opcode = Opcode::new(names.intern("x64.nop"));
552 let block = func.create_block();
553 let first = func.new_vreg(GPR);
554 let second = func.new_vreg(GPR);
555 let read = {
556 func.build(block, opcode).def(first, GPR).finish();
557 func.build(block, opcode).def(second, GPR).finish();
558 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish()
559 };
560
561 let (taken, assignment, edits) = allocate(&mut func, &env());
562 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
563
564 let list = func[read].operands;
568 func[list][0].reg = func[list][1].reg;
569
570 assert_eq!(
571 said(&func, &taken, &assignment, &edits),
572 ["instruction 2 reads %0 out of register 1 of class 0, which holds %1"]
573 );
574 }
575
576 #[test]
577 fn a_move_that_writes_the_wrong_register_is_an_instruction_reading_the_wrong_value() {
578 let mut names = Interner::new();
579 let mut func = Func::new(names.intern("f"));
580 let opcode = Opcode::new(names.intern("x64.nop"));
581 let block = func.create_block();
582 let dividend = func.new_vreg(GPR);
583 let quotient = func.new_vreg(GPR);
584 func.build(block, opcode).def(dividend, GPR).finish();
585 func.build(block, opcode)
586 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
587 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
588 .finish();
589 func.build(block, opcode).uses(quotient, GPR).finish();
590 func.build(block, opcode).uses(dividend, GPR).finish();
591
592 let (taken, assignment, mut edits) = allocate(&mut func, &env());
593 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
594
595 edits[0].mov.to = Place::Reg(SYSV.int_order[2]);
599 assert_eq!(
600 said(&func, &taken, &assignment, &edits),
601 ["instruction 1 reads %0 out of register 0 of class 0, which holds nothing anything \
602 has put there"]
603 );
604 }
605
606 #[test]
607 fn a_value_carried_over_an_edge_goes_on_under_the_name_the_block_it_arrives_in_gives_it() {
608 let mut names = Interner::new();
609 let mut func = Func::new(names.intern("f"));
610 let opcode = Opcode::new(names.intern("x64.nop"));
611 let head = func.create_block();
612 let tail = func.create_block();
613 let value = func.new_vreg(GPR);
614 func.build(head, opcode).def(value, GPR).finish();
615 let arrived = func.append_param(tail, GPR);
616 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![value])];
617 func.build(tail, opcode).uses(arrived, GPR).finish();
618
619 let (taken, assignment, edits) = allocate(&mut func, &env());
620 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
621 }
622
623 #[test]
624 fn two_values_that_swap_on_an_edge_arrive_the_right_way_round_only_in_the_order_they_were_put_in()
625 {
626 let mut names = Interner::new();
627 let mut func = Func::new(names.intern("f"));
628 let opcode = Opcode::new(names.intern("x64.nop"));
629 let head = func.create_block();
630 let body = func.create_block();
631 let first = func.new_vreg(GPR);
632 let second = func.new_vreg(GPR);
633 func.build(head, opcode).def(first, GPR).finish();
634 func.build(head, opcode).def(second, GPR).finish();
635 let left = func.append_param(body, GPR);
636 let right = func.append_param(body, GPR);
637 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
638 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
639 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
640
641 let (taken, assignment, mut edits) = allocate(&mut func, &env());
642 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
643
644 let (to, from) = (Place::Reg(SYSV.int_order[0]), Place::Reg(SYSV.int_order[1]));
648 edits.truncate(edits.len() - 3);
649 edits.push(Edit { at: At::EndOf(body), mov: Move::new(to, from), class: GPR });
650 edits.push(Edit { at: At::EndOf(body), mov: Move::new(from, to), class: GPR });
651
652 assert_eq!(
653 said(&func, &taken, &assignment, &edits),
654 ["the edge from block 1 to block 1 was to leave %2 in register 1 of class 0, which \
655 holds %3"]
656 );
657 }
658
659 #[test]
660 fn a_loop_is_walked_until_it_settles_rather_than_reported_the_first_time_round() {
661 let mut names = Interner::new();
662 let mut func = Func::new(names.intern("f"));
663 let opcode = Opcode::new(names.intern("x64.nop"));
664 let head = func.create_block();
665 let body = func.create_block();
666 let latch = func.create_block();
667 let out = func.create_block();
668 let start = func.new_vreg(GPR);
669 func.build(head, opcode).def(start, GPR).finish();
670 let counter = func.append_param(body, GPR);
671 *func.succs_mut(head) = vec![BlockCall::with(body, vec![start])];
672 let next = func.new_vreg(GPR);
673 func.build(body, opcode).def(next, GPR).uses(counter, GPR).finish();
674 *func.succs_mut(body) = vec![BlockCall::to(latch), BlockCall::to(out)];
675 func.build(latch, opcode).finish();
676 *func.succs_mut(latch) = vec![BlockCall::with(body, vec![next])];
677 func.build(out, opcode).finish();
678
679 let (taken, assignment, edits) = allocate(&mut func, &env());
682 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
683 }
684
685 #[test]
686 fn a_value_the_two_ways_into_a_block_leave_in_different_places_is_not_one_it_may_read() {
687 let mut names = Interner::new();
688 let mut func = Func::new(names.intern("f"));
689 let opcode = Opcode::new(names.intern("x64.nop"));
690 let entry = func.create_block();
691 let arm = func.create_block();
692 let tail = func.create_block();
693 let value = func.new_vreg(GPR);
694 func.build(entry, opcode).def(value, GPR).finish();
695 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
696 func.build(arm, opcode).finish();
697 *func.succs_mut(arm) = vec![BlockCall::to(tail)];
698 func.build(tail, opcode).uses(value, GPR).finish();
699
700 let (taken, assignment, mut edits) = allocate(&mut func, &env());
701 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
702
703 let at = Place::Reg(SYSV.int_order[0]);
707 let elsewhere = Place::Reg(SYSV.int_order[1]);
708 edits.push(Edit { at: At::EndOf(arm), mov: Move::new(at, elsewhere), class: GPR });
709
710 assert_eq!(
711 said(&func, &taken, &assignment, &edits),
712 ["instruction 2 reads %0 out of register 0 of class 0, which holds nothing anything \
713 has put there"]
714 );
715 }
716
717 #[test]
718 fn a_register_the_function_names_itself_is_one_it_may_read_without_writing_it_first() {
719 let mut names = Interner::new();
720 let mut func = Func::new(names.intern("f"));
721 let opcode = Opcode::new(names.intern("x64.nop"));
722 let block = func.create_block();
723 func.build(block, opcode).operand(Operand::read(Reg::physical(RSP), GPR)).finish();
724
725 let (taken, assignment, edits) = allocate(&mut func, &env());
729 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
730 }
731
732 #[test]
733 fn a_register_the_function_names_itself_is_readable_after_a_value_has_been_put_in_it() {
734 let mut names = Interner::new();
735 let mut func = Func::new(names.intern("f"));
736 let opcode = Opcode::new(names.intern("x64.nop"));
737 let block = func.create_block();
738 let argument = func.new_vreg(GPR);
739 func.build(block, opcode)
740 .operand(Operand::write(argument, GPR).with(Constraint::Fixed(SYSV.int_order[0])))
741 .finish();
742 func.build(block, opcode)
743 .operand(Operand::read(Reg::physical(SYSV.int_order[0]), GPR))
744 .finish();
745
746 let (taken, assignment, edits) = allocate(&mut func, &env());
752 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
753 }
754
755 #[test]
756 fn what_is_wrong_is_reported_in_a_sentence_that_says_how_many_things_are_wrong() {
757 let fault = Fault::Read {
758 inst: Inst::new(3),
759 place: Place::Slot(1),
760 class: GPR,
761 wanted: Reg::virtual_reg(2),
762 found: None,
763 };
764 assert_eq!(
765 report(&[fault]),
766 "the rewrite loses a value in 1 place\n instruction 3 reads %2 out of slot 1, which \
767 holds nothing anything has put there"
768 );
769 }
770}