1use std::collections::HashMap;
59use std::fmt;
60
61use rucc_mir::{Block, Func, Inst, Operand, Param, Reg, Role};
62use rucc_target::RegClass;
63
64use crate::assign::{Assignment, Place};
65use crate::rewrite::{At, Edit};
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum Fault {
70 Read {
72 inst: Inst,
74 place: Place,
76 class: RegClass,
78 wanted: Reg,
80 found: Option<Reg>,
82 },
83 Arrived {
85 from: Block,
87 to: Block,
89 place: Place,
91 class: RegClass,
93 wanted: Reg,
95 found: Option<Reg>,
97 },
98}
99
100impl fmt::Display for Fault {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 Fault::Read { inst, place, class, wanted, found } => write!(
104 f,
105 "instruction {} reads {} out of {}, which {}",
106 inst.index(),
107 name(*wanted),
108 spelled(*class, *place),
109 holding(*found)
110 ),
111 Fault::Arrived { from, to, place, class, wanted, found } => write!(
112 f,
113 "the edge from block {} to block {} was to leave {} in {}, which {}",
114 from.index(),
115 to.index(),
116 name(*wanted),
117 spelled(*class, *place),
118 holding(*found)
119 ),
120 }
121 }
122}
123
124#[derive(Debug, Clone, Default)]
129pub struct Shape {
130 operands: Vec<Vec<Operand>>,
132 params: Vec<Vec<Param>>,
134 succs: Vec<Vec<Call>>,
136}
137
138#[derive(Debug, Clone)]
140struct Call {
141 block: Block,
142 args: Vec<Reg>,
143}
144
145#[must_use]
152pub fn shape(func: &Func) -> Shape {
153 let mut shape = Shape {
154 operands: vec![Vec::new(); func.inst_count()],
155 params: vec![Vec::new(); func.block_count()],
156 succs: vec![Vec::new(); func.block_count()],
157 };
158 for block in func.blocks() {
159 shape.params[block.index()] = func[block].params.clone();
160 shape.succs[block.index()] = func[block]
161 .succs
162 .iter()
163 .map(|call| Call { block: call.block, args: call.args.clone() })
164 .collect();
165 for inst in func.insts(block) {
166 shape.operands[inst.index()] = func[func[inst].operands].to_vec();
167 }
168 }
169 shape
170}
171
172#[must_use]
182pub fn trace(func: &Func, shape: &Shape, assignment: &Assignment, edits: &[Edit]) -> Vec<Fault> {
183 let filed = File::of(func, edits);
184 let mut entry: Vec<Option<State>> = vec![None; func.block_count()];
185 let Some(start) = func.entry() else { return Vec::new() };
186
187 entry[start.index()] = Some(arrived(shape));
188 let mut queue = vec![start];
189 let mut ignored = Vec::new();
190 while let Some(block) = queue.pop() {
191 let Some(state) = entry[block.index()].clone() else { continue };
192 ignored.clear();
193 let out = body(func, shape, &filed, edits, block, state, &mut ignored);
194 let single = shape.succs[block.index()].len() == 1;
195 for call in &shape.succs[block.index()] {
196 let over =
197 cross(shape, &filed, edits, assignment, block, call, single, &out, &mut ignored);
198 if narrow(&mut entry[call.block.index()], &over) {
199 queue.push(call.block);
200 }
201 }
202 }
203
204 let mut faults = Vec::new();
208 for block in func.blocks() {
209 let Some(state) = entry[block.index()].clone() else { continue };
210 let out = body(func, shape, &filed, edits, block, state, &mut faults);
211 let single = shape.succs[block.index()].len() == 1;
212 for call in &shape.succs[block.index()] {
213 cross(shape, &filed, edits, assignment, block, call, single, &out, &mut faults);
214 }
215 }
216 faults
217}
218
219#[must_use]
221pub fn report(faults: &[Fault]) -> String {
222 let places = if faults.len() == 1 { "place" } else { "places" };
223 let mut report = format!("the rewrite loses a value in {} {places}", faults.len());
224 for fault in faults {
225 report.push_str("\n ");
226 report.push_str(&fault.to_string());
227 }
228 report
229}
230
231fn arrived(shape: &Shape) -> State {
244 let mut state = State::new();
245 for operands in &shape.operands {
246 for operand in operands {
247 if operand.role == Role::Use && operand.reg.phys().is_some() {
248 state
249 .entry(spot(operand.class, Place::Reg(operand.reg.phys().expect("physical"))))
250 .or_insert(operand.reg);
251 }
252 }
253 }
254 state
255}
256
257type State = HashMap<Spot, Reg>;
263
264type Spot = (u8, Place);
270
271fn spot(class: RegClass, place: Place) -> Spot {
273 (class.number(), place)
274}
275
276#[derive(Debug, Default)]
282struct File {
283 before: Vec<Vec<usize>>,
284 after: Vec<Vec<usize>>,
285 start_of: Vec<Vec<usize>>,
286 end_of: Vec<Vec<usize>>,
287}
288
289impl File {
290 fn of(func: &Func, edits: &[Edit]) -> Self {
292 let mut filed = File {
293 before: vec![Vec::new(); func.inst_count()],
294 after: vec![Vec::new(); func.inst_count()],
295 start_of: vec![Vec::new(); func.block_count()],
296 end_of: vec![Vec::new(); func.block_count()],
297 };
298 for (index, edit) in edits.iter().enumerate() {
299 match edit.at {
300 At::Before(inst) => filed.before[inst.index()].push(index),
301 At::After(inst) => filed.after[inst.index()].push(index),
302 At::StartOf(block) => filed.start_of[block.index()].push(index),
303 At::EndOf(block) => filed.end_of[block.index()].push(index),
304 }
305 }
306 filed
307 }
308}
309
310fn body(
315 func: &Func,
316 shape: &Shape,
317 filed: &File,
318 edits: &[Edit],
319 block: Block,
320 mut state: State,
321 faults: &mut Vec<Fault>,
322) -> State {
323 for inst in func.insts(block) {
324 for &edit in &filed.before[inst.index()] {
325 moved(&mut state, &edits[edit]);
326 }
327 let was = &shape.operands[inst.index()];
328 let now = &func[func[inst].operands];
329
330 for (operand, place) in was.iter().zip(now.iter()) {
334 let Some(at) = landed(place) else { continue };
335 if operand.role != Role::Use {
336 continue;
337 }
338 let found = state.get(&spot(operand.class, at)).copied();
339 if found != Some(operand.reg) {
340 let (class, wanted) = (operand.class, operand.reg);
341 faults.push(Fault::Read { inst, place: at, class, wanted, found });
342 }
343 }
344 for (operand, place) in was.iter().zip(now.iter()) {
345 let Some(at) = landed(place) else { continue };
346 if !operand.role.is_def() {
347 continue;
348 }
349 state.insert(spot(operand.class, at), operand.reg);
350 }
351 for &edit in &filed.after[inst.index()] {
352 moved(&mut state, &edits[edit]);
353 }
354 }
355 state
356}
357
358#[allow(clippy::too_many_arguments, reason = "an edge is the two blocks and everything between")]
364fn cross(
365 shape: &Shape,
366 filed: &File,
367 edits: &[Edit],
368 assignment: &Assignment,
369 from: Block,
370 call: &Call,
371 single: bool,
372 out: &State,
373 faults: &mut Vec<Fault>,
374) -> State {
375 let mut state = out.clone();
376 let params = &shape.params[call.block.index()];
377 let list =
378 if single { &filed.end_of[from.index()] } else { &filed.start_of[call.block.index()] };
379 for &edit in list {
380 moved(&mut state, &edits[edit]);
381 }
382
383 let mut arrived = Vec::new();
387 for (param, &arg) in params.iter().zip(&call.args) {
388 let Some(at) = home(assignment, param.reg) else { continue };
389 let found = state.get(&spot(param.class, at)).copied();
390 if found != Some(arg) {
391 let (to, class) = (call.block, param.class);
392 faults.push(Fault::Arrived { from, to, place: at, class, wanted: arg, found });
393 }
394 arrived.push((spot(param.class, at), param.reg));
395 }
396 for (spot, reg) in arrived {
397 state.insert(spot, reg);
398 }
399 state
400}
401
402fn moved(state: &mut State, edit: &Edit) {
407 let to = spot(edit.class, edit.mov.to);
408 let from = spot(edit.class, edit.mov.from);
409 match state.get(&from).copied() {
410 Some(reg) => state.insert(to, reg),
411 None => state.remove(&to),
412 };
413}
414
415fn narrow(entry: &mut Option<State>, over: &State) -> bool {
421 match entry {
422 None => {
423 *entry = Some(over.clone());
424 true
425 }
426 Some(state) => {
427 let before = state.len();
428 state.retain(|spot, reg| over.get(spot) == Some(&*reg));
429 state.len() != before
430 }
431 }
432}
433
434fn landed(operand: &Operand) -> Option<Place> {
436 operand.reg.phys().map(Place::Reg)
437}
438
439fn home(assignment: &Assignment, reg: Reg) -> Option<Place> {
445 assignment.place(reg).or_else(|| reg.phys().map(Place::Reg))
446}
447
448fn name(reg: Reg) -> String {
450 match reg.number() {
451 Some(number) => format!("%{number}"),
452 None => match reg.phys() {
453 Some(at) => format!("register {}", at.number()),
454 None => "nothing".to_owned(),
455 },
456 }
457}
458
459fn spelled(class: RegClass, place: Place) -> String {
462 match place {
463 Place::Reg(at) => format!("register {} of class {}", at.number(), class.number()),
464 Place::Slot(slot) => format!("slot {slot}"),
465 }
466}
467
468fn holding(found: Option<Reg>) -> String {
470 match found {
471 Some(reg) => format!("holds {}", name(reg)),
472 None => "holds nothing anything has put there".to_owned(),
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use rucc_base::Interner;
479 use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
480 use rucc_target::x86_64::{GPR, RAX, RSP, SYSV};
481
482 use super::*;
483 use crate::assign::{Env, assign};
484 use crate::live::Live;
485 use crate::moves::Move;
486 use crate::order::Order;
487 use crate::rewrite::rewrite;
488
489 fn env() -> Env {
491 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
492 Env::new().with(GPR, order, scratch)
493 }
494
495 fn narrow(count: usize) -> Env {
497 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
498 }
499
500 fn allocate(func: &mut Func, env: &Env) -> (Shape, Assignment, Vec<Edit>) {
502 let order = Order::of(func);
503 let live = Live::of(func, &order);
504 let mut assignment = assign(func, &order, &live, env);
505 let taken = shape(func);
506 let edits = rewrite(func, &mut assignment, env);
507 (taken, assignment, edits)
508 }
509
510 fn said(func: &Func, taken: &Shape, assignment: &Assignment, edits: &[Edit]) -> Vec<String> {
512 trace(func, taken, assignment, edits).iter().map(ToString::to_string).collect()
513 }
514
515 #[test]
516 fn every_value_an_instruction_reads_is_the_one_that_was_written_where_it_reads_it() {
517 let mut names = Interner::new();
518 let mut func = Func::new(names.intern("f"));
519 let opcode = Opcode::new(names.intern("x64.nop"));
520 let block = func.create_block();
521 let first = func.new_vreg(GPR);
522 let second = func.new_vreg(GPR);
523 func.build(block, opcode).def(first, GPR).finish();
524 func.build(block, opcode).def(second, GPR).finish();
525 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
526
527 let (taken, assignment, edits) = allocate(&mut func, &env());
528 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
529 }
530
531 #[test]
532 fn a_value_that_lives_on_the_stack_is_followed_through_the_slot_it_lives_in() {
533 let mut names = Interner::new();
534 let mut func = Func::new(names.intern("f"));
535 let opcode = Opcode::new(names.intern("x64.nop"));
536 let block = func.create_block();
537 let first = func.new_vreg(GPR);
538 let second = func.new_vreg(GPR);
539 let third = func.new_vreg(GPR);
540 func.build(block, opcode).def(first, GPR).finish();
541 func.build(block, opcode).def(second, GPR).finish();
542 func.build(block, opcode).def(third, GPR).finish();
543 func.build(block, opcode).uses(first, GPR).uses(second, GPR).uses(third, GPR).finish();
544
545 let (taken, assignment, edits) = allocate(&mut func, &narrow(2));
548 assert_eq!(assignment.spilled(), 1);
549 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
550 }
551
552 #[test]
553 fn an_operand_the_rewrite_pointed_at_the_wrong_register_is_reported() {
554 let mut names = Interner::new();
555 let mut func = Func::new(names.intern("f"));
556 let opcode = Opcode::new(names.intern("x64.nop"));
557 let block = func.create_block();
558 let first = func.new_vreg(GPR);
559 let second = func.new_vreg(GPR);
560 let read = {
561 func.build(block, opcode).def(first, GPR).finish();
562 func.build(block, opcode).def(second, GPR).finish();
563 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish()
564 };
565
566 let (taken, assignment, edits) = allocate(&mut func, &env());
567 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
568
569 let list = func[read].operands;
573 func[list][0].reg = func[list][1].reg;
574
575 assert_eq!(
576 said(&func, &taken, &assignment, &edits),
577 ["instruction 2 reads %0 out of register 1 of class 0, which holds %1"]
578 );
579 }
580
581 #[test]
582 fn a_move_that_writes_the_wrong_register_is_an_instruction_reading_the_wrong_value() {
583 let mut names = Interner::new();
584 let mut func = Func::new(names.intern("f"));
585 let opcode = Opcode::new(names.intern("x64.nop"));
586 let block = func.create_block();
587 let dividend = func.new_vreg(GPR);
588 let quotient = func.new_vreg(GPR);
589 func.build(block, opcode).def(dividend, GPR).finish();
590 func.build(block, opcode)
591 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
592 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
593 .finish();
594 func.build(block, opcode).uses(quotient, GPR).finish();
595 func.build(block, opcode).uses(dividend, GPR).finish();
596
597 let (taken, assignment, mut edits) = allocate(&mut func, &env());
598 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
599
600 edits[0].mov.to = Place::Reg(SYSV.int_order[2]);
604 assert_eq!(
605 said(&func, &taken, &assignment, &edits),
606 ["instruction 1 reads %0 out of register 0 of class 0, which holds nothing anything \
607 has put there"]
608 );
609 }
610
611 #[test]
612 fn a_value_carried_over_an_edge_goes_on_under_the_name_the_block_it_arrives_in_gives_it() {
613 let mut names = Interner::new();
614 let mut func = Func::new(names.intern("f"));
615 let opcode = Opcode::new(names.intern("x64.nop"));
616 let head = func.create_block();
617 let tail = func.create_block();
618 let value = func.new_vreg(GPR);
619 func.build(head, opcode).def(value, GPR).finish();
620 let arrived = func.append_param(tail, GPR);
621 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![value])];
622 func.build(tail, opcode).uses(arrived, GPR).finish();
623
624 let (taken, assignment, edits) = allocate(&mut func, &env());
625 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
626 }
627
628 #[test]
629 fn two_values_that_swap_on_an_edge_arrive_the_right_way_round_only_in_the_order_they_were_put_in()
630 {
631 let mut names = Interner::new();
632 let mut func = Func::new(names.intern("f"));
633 let opcode = Opcode::new(names.intern("x64.nop"));
634 let head = func.create_block();
635 let body = func.create_block();
636 let first = func.new_vreg(GPR);
637 let second = func.new_vreg(GPR);
638 func.build(head, opcode).def(first, GPR).finish();
639 func.build(head, opcode).def(second, GPR).finish();
640 let left = func.append_param(body, GPR);
641 let right = func.append_param(body, GPR);
642 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
643 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
644 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
645
646 let (taken, assignment, mut edits) = allocate(&mut func, &env());
647 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
648
649 let (to, from) = (Place::Reg(SYSV.int_order[0]), Place::Reg(SYSV.int_order[1]));
653 edits.truncate(edits.len() - 3);
654 edits.push(Edit { at: At::EndOf(body), mov: Move::new(to, from), class: GPR });
655 edits.push(Edit { at: At::EndOf(body), mov: Move::new(from, to), class: GPR });
656
657 assert_eq!(
658 said(&func, &taken, &assignment, &edits),
659 ["the edge from block 1 to block 1 was to leave %2 in register 1 of class 0, which \
660 holds %3"]
661 );
662 }
663
664 #[test]
665 fn a_loop_is_walked_until_it_settles_rather_than_reported_the_first_time_round() {
666 let mut names = Interner::new();
667 let mut func = Func::new(names.intern("f"));
668 let opcode = Opcode::new(names.intern("x64.nop"));
669 let head = func.create_block();
670 let body = func.create_block();
671 let latch = func.create_block();
672 let out = func.create_block();
673 let start = func.new_vreg(GPR);
674 func.build(head, opcode).def(start, GPR).finish();
675 let counter = func.append_param(body, GPR);
676 *func.succs_mut(head) = vec![BlockCall::with(body, vec![start])];
677 let next = func.new_vreg(GPR);
678 func.build(body, opcode).def(next, GPR).uses(counter, GPR).finish();
679 *func.succs_mut(body) = vec![BlockCall::to(latch), BlockCall::to(out)];
680 func.build(latch, opcode).finish();
681 *func.succs_mut(latch) = vec![BlockCall::with(body, vec![next])];
682 func.build(out, opcode).finish();
683
684 let (taken, assignment, edits) = allocate(&mut func, &env());
687 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
688 }
689
690 #[test]
691 fn a_value_the_two_ways_into_a_block_leave_in_different_places_is_not_one_it_may_read() {
692 let mut names = Interner::new();
693 let mut func = Func::new(names.intern("f"));
694 let opcode = Opcode::new(names.intern("x64.nop"));
695 let entry = func.create_block();
696 let arm = func.create_block();
697 let tail = func.create_block();
698 let value = func.new_vreg(GPR);
699 func.build(entry, opcode).def(value, GPR).finish();
700 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
701 func.build(arm, opcode).finish();
702 *func.succs_mut(arm) = vec![BlockCall::to(tail)];
703 func.build(tail, opcode).uses(value, GPR).finish();
704
705 let (taken, assignment, mut edits) = allocate(&mut func, &env());
706 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
707
708 let at = Place::Reg(SYSV.int_order[0]);
712 let elsewhere = Place::Reg(SYSV.int_order[1]);
713 edits.push(Edit { at: At::EndOf(arm), mov: Move::new(at, elsewhere), class: GPR });
714
715 assert_eq!(
716 said(&func, &taken, &assignment, &edits),
717 ["instruction 2 reads %0 out of register 0 of class 0, which holds nothing anything \
718 has put there"]
719 );
720 }
721
722 #[test]
723 fn a_register_the_function_arrives_holding_is_one_it_may_read_without_writing_it_first() {
724 let mut names = Interner::new();
725 let mut func = Func::new(names.intern("f"));
726 let opcode = Opcode::new(names.intern("x64.nop"));
727 let block = func.create_block();
728 func.build(block, opcode).operand(Operand::read(Reg::physical(RSP), GPR)).finish();
729
730 let (taken, assignment, edits) = allocate(&mut func, &env());
734 assert_eq!(said(&func, &taken, &assignment, &edits), Vec::<String>::new());
735 }
736
737 #[test]
738 fn what_is_wrong_is_reported_in_a_sentence_that_says_how_many_things_are_wrong() {
739 let fault = Fault::Read {
740 inst: Inst::new(3),
741 place: Place::Slot(1),
742 class: GPR,
743 wanted: Reg::virtual_reg(2),
744 found: None,
745 };
746 assert_eq!(
747 report(&[fault]),
748 "the rewrite loses a value in 1 place\n instruction 3 reads %2 out of slot 1, which \
749 holds nothing anything has put there"
750 );
751 }
752}