1use std::collections::{HashMap, HashSet};
58use std::fmt;
59
60use rucc_mir::{Constraint, Func, Inst, Reg, Role};
61use rucc_target::{PhysReg, RegClass};
62
63use crate::assign::{Assignment, Place};
64use crate::live::{Area, Live, Range};
65use crate::order::{Order, Point};
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum Problem {
70 Nowhere {
72 reg: Reg,
74 },
75 Shared {
78 first: Reg,
80 second: Reg,
82 place: Place,
84 },
85 InTheWay {
88 reg: Reg,
90 at: PhysReg,
92 inst: Inst,
94 },
95 NotOnTheStack {
97 reg: Reg,
99 inst: Inst,
101 },
102 NeverWritten {
105 reg: Reg,
107 },
108}
109
110impl fmt::Display for Problem {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match self {
113 Problem::Nowhere { reg } => write!(f, "{} has nowhere to live", name(*reg)),
114 Problem::Shared { first, second, place } => {
115 let (first, second) = (name(*first), name(*second));
116 write!(f, "{first} and {second} are both live and both in {}", place_name(*place))
117 }
118 Problem::InTheWay { reg, at, inst } => {
119 let reg = name(*reg);
120 let inst = inst.index();
121 write!(f, "{reg} is in register {}, which instruction {inst} wants", at.number())
122 }
123 Problem::NotOnTheStack { reg, inst } => {
124 let reg = name(*reg);
125 write!(f, "{reg} is not on the stack, and instruction {} needs it", inst.index())
126 }
127 Problem::NeverWritten { reg } => {
128 write!(f, "{} is read before anything writes it", name(*reg))
129 }
130 }
131 }
132}
133
134#[must_use]
145pub fn check(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<Problem> {
146 let mut problems = Vec::new();
147 if let Some(entry) = func.entry() {
150 for reg in live.live_in(entry) {
151 problems.push(Problem::NeverWritten { reg });
152 }
153 }
154 let reuses = reuses(func, order);
155 let mut values = Vec::new();
156 for (number, reuse) in reuses.iter().enumerate() {
157 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
158 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
159 continue;
160 };
161 let Some(place) = assignment.place(reg) else {
162 problems.push(Problem::Nowhere { reg });
163 continue;
164 };
165 if let Some(reuse) = reuse {
170 area = area.with(reuse.at);
171 }
172 values.push(Value { reg, class, range: area.hull(), area, place });
173 }
174 overlaps(&values, &reuses, live, &mut problems);
175 instructions(func, order, assignment, &values, &reuses, &mut problems);
176 problems
177}
178
179#[must_use]
181pub fn report(problems: &[Problem]) -> String {
182 let places = if problems.len() == 1 { "place" } else { "places" };
183 let mut report = format!("the allocation is wrong in {} {places}", problems.len());
184 for problem in problems {
185 report.push_str("\n ");
186 report.push_str(&problem.to_string());
187 }
188 report
189}
190
191#[derive(Debug, Clone, Copy)]
193struct Value<'a> {
194 reg: Reg,
195 class: RegClass,
196 range: Range,
198 area: Area<'a>,
201 place: Place,
202}
203
204#[derive(Debug, Clone, Copy)]
206struct Reuse {
207 source: Reg,
208 at: Point,
209}
210
211fn overlaps(
224 values: &[Value<'_>],
225 reuses: &[Option<Reuse>],
226 live: &Live,
227 problems: &mut Vec<Problem>,
228) {
229 let mut sorted = values.to_vec();
230 sorted.sort_by_key(|value| (value.range.start, value.reg));
231 let mut places: HashMap<(Place, Option<RegClass>), Vec<usize>> = HashMap::new();
232 for (position, value) in sorted.iter().enumerate() {
233 places.entry(place_of(*value)).or_default().push(position);
234 }
235 let mut found = Vec::new();
236 for positions in places.values() {
237 let mut active: Vec<usize> = Vec::new();
238 for &position in positions {
239 let value = sorted[position];
240 active.retain(|&held| sorted[held].range.end >= value.range.start);
241 for &held in &active {
242 let held_value = sorted[held];
243 if !held_value.area.overlaps(value.area)
244 || coalesced(held_value, value, reuses, live)
245 {
246 continue;
247 }
248 let problem = Problem::Shared {
249 first: held_value.reg,
250 second: value.reg,
251 place: value.place,
252 };
253 found.push((position, held, problem));
254 }
255 active.push(position);
256 }
257 }
258 found.sort_by_key(|&(position, held, _)| (position, held));
261 problems.extend(found.into_iter().map(|(_, _, problem)| problem));
262}
263
264fn place_of(value: Value<'_>) -> (Place, Option<RegClass>) {
270 match value.place {
271 Place::Reg(_) => (value.place, Some(value.class)),
272 Place::Slot(_) => (value.place, None),
273 }
274}
275
276fn coalesced(first: Value<'_>, second: Value<'_>, reuses: &[Option<Reuse>], live: &Live) -> bool {
288 let pair = |source: Value<'_>, dest: Value<'_>| {
289 let Some(reuse) = reuses[index(dest.reg)] else { return false };
290 reuse.source == source.reg && crate::assign::apart(live, source.reg, dest.reg)
291 };
292 pair(first, second) || pair(second, first)
293}
294
295fn instructions(
298 func: &Func,
299 order: &Order,
300 assignment: &Assignment,
301 values: &[Value<'_>],
302 reuses: &[Option<Reuse>],
303 problems: &mut Vec<Problem>,
304) {
305 let mut held: HashMap<PhysReg, Vec<Value<'_>>> = HashMap::new();
308 let saved: HashSet<(Reg, Inst)> =
311 assignment.saves().iter().map(|save| (save.reg, save.inst)).collect();
312 for value in values {
313 if let Place::Reg(at) = value.place {
314 held.entry(at).or_default().push(*value);
315 }
316 }
317 for block in func.blocks() {
318 for inst in func.insts(block) {
319 for operand in &func[func[inst].operands] {
320 if operand.constraint == Constraint::Stack
321 && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
322 {
323 problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
324 }
325 let at = match operand.constraint {
329 Constraint::Fixed(at) => Some(at),
330 _ => operand.reg.phys(),
331 };
332 let Some(at) = at else { continue };
333 let Some(here) = held.get(&at) else { continue };
334 let early = order.early(inst);
335 let point = if operand.role == Role::Def { order.late(inst) } else { early };
336 for value in here {
337 let mine = value.reg == operand.reg
338 || reuses[index(value.reg)].is_some_and(|reuse| {
339 reuse.source == operand.reg
340 && reuse.at == early
341 && value.place == Place::Reg(at)
342 });
343 if mine || value.class != operand.class {
344 continue;
345 }
346 let width = func.width(value.reg);
349 let under = |above| width.is_some_and(|width| width <= above);
350 if matches!(operand.constraint, Constraint::Above(above) if under(above)) {
351 continue;
352 }
353 let destroyed = operand.role == Role::Def && operand.reg.phys().is_some();
357 if destroyed && saved.contains(&(value.reg, inst)) {
358 continue;
359 }
360 if value.place == Place::Reg(at) && value.area.covers(point) {
365 problems.push(Problem::InTheWay { reg: value.reg, at, inst });
366 }
367 }
368 }
369 }
370 }
371}
372
373fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
375 let mut reuses = vec![None; func.vregs()];
376 for block in func.blocks() {
377 for inst in func.insts(block) {
378 let operands = &func[func[inst].operands];
379 for operand in operands {
380 let Constraint::Reuse(other) = operand.constraint else { continue };
381 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
382 let Some(number) = number else { continue };
383 let source = operands[usize::from(other)].reg;
384 reuses[number] = Some(Reuse { source, at: order.early(inst) });
385 }
386 }
387 }
388 reuses
389}
390
391fn index(reg: Reg) -> usize {
394 reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
395}
396
397fn name(reg: Reg) -> String {
399 match reg.number() {
400 Some(number) => format!("%{number}"),
401 None => format!("register {}", reg.phys().expect("a physical register").number()),
402 }
403}
404
405fn place_name(place: Place) -> String {
408 match place {
409 Place::Reg(at) => format!("register {}", at.number()),
410 Place::Slot(slot) => format!("slot {slot}"),
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use rucc_base::Interner;
417 use rucc_mir::{BlockCall, Opcode, Operand};
418 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, SYSV};
419
420 use super::*;
421 use crate::assign::{Env, assign};
422
423 fn env() -> Env {
425 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
426 Env::new().with(GPR, order, scratch)
427 }
428
429 fn allocated(func: &Func) -> Vec<String> {
432 let order = Order::of(func);
433 let live = Live::of(func, &order);
434 let assignment = assign(func, &order, &live, &env());
435 said(func, &order, &live, &assignment)
436 }
437
438 fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
440 check(func, order, live, assignment).iter().map(ToString::to_string).collect()
441 }
442
443 fn read(func: &Func) -> (Order, Live) {
445 let order = Order::of(func);
446 let live = Live::of(func, &order);
447 (order, live)
448 }
449
450 fn under_the_top(width: u32) -> Vec<String> {
452 let mut names = Interner::new();
453 let mut func = Func::new(names.intern("f"));
454 let opcode = Opcode::new(names.intern("x64.nop"));
455 let block = func.create_block();
456 let held = func.new_vreg(GPR);
457 func.set_width(held, width);
458 func.build(block, opcode).def(held, GPR).finish();
459 func.build(block, opcode)
460 .operand(Operand::write(Reg::physical(RCX), GPR).with(Constraint::Above(8)))
461 .finish();
462 func.build(block, opcode).uses(held, GPR).finish();
463 let (order, live) = read(&func);
464 let mut assignment = Assignment::empty(func.vregs());
465 assignment.put(held, Place::Reg(RCX));
466 said(&func, &order, &live, &assignment)
467 }
468
469 #[test]
470 fn a_value_under_the_part_an_instruction_writes_is_not_in_its_way() {
471 assert!(under_the_top(8).is_empty(), "{:?}", under_the_top(8));
472 let wide = under_the_top(16);
473 assert!(wide.len() == 1 && wide[0].contains("%0"), "{wide:?}");
474 assert_eq!(under_the_top(0), wide);
475 }
476
477 #[test]
478 fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
479 let mut names = Interner::new();
480 let mut func = Func::new(names.intern("f"));
481 let opcode = Opcode::new(names.intern("x64.nop"));
482 let block = func.create_block();
483 let first = func.new_vreg(GPR);
484 let second = func.new_vreg(GPR);
485 func.build(block, opcode).def(first, GPR).finish();
486 func.build(block, opcode).def(second, GPR).finish();
487 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
488
489 assert_eq!(allocated(&func), Vec::<String>::new());
490 }
491
492 #[test]
493 fn a_value_with_nowhere_to_live_is_found() {
494 let mut names = Interner::new();
495 let mut func = Func::new(names.intern("f"));
496 let opcode = Opcode::new(names.intern("x64.nop"));
497 let block = func.create_block();
498 let only = func.new_vreg(GPR);
499 func.build(block, opcode).def(only, GPR).finish();
500 func.build(block, opcode).uses(only, GPR).finish();
501
502 let (order, live) = read(&func);
503 let assignment = Assignment::empty(func.vregs());
504
505 assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
506 }
507
508 #[test]
509 fn a_value_read_before_anything_writes_it_is_found() {
510 let mut names = Interner::new();
511 let mut func = Func::new(names.intern("f"));
512 let opcode = Opcode::new(names.intern("x64.nop"));
513 let block = func.create_block();
514 let never = func.new_vreg(GPR);
515 func.build(block, opcode).uses(never, GPR).finish();
516
517 let (order, live) = read(&func);
518 let mut assignment = Assignment::empty(func.vregs());
519 assignment.put(never, Place::Reg(RAX));
520
521 assert_eq!(
522 said(&func, &order, &live, &assignment),
523 ["%0 is read before anything writes it"]
524 );
525 }
526
527 #[test]
528 fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
529 let mut names = Interner::new();
530 let mut func = Func::new(names.intern("f"));
531 let opcode = Opcode::new(names.intern("x64.nop"));
532 let block = func.create_block();
533 let first = func.new_vreg(GPR);
534 let second = 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).uses(first, GPR).uses(second, GPR).finish();
538
539 let (order, live) = read(&func);
540 let mut assignment = Assignment::empty(func.vregs());
541 assignment.put(first, Place::Reg(RAX));
542 assignment.put(second, Place::Reg(RAX));
543
544 let said = said(&func, &order, &live, &assignment);
545 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
546 }
547
548 #[test]
549 fn values_sharing_different_places_are_reported_in_the_order_they_start() {
550 let mut names = Interner::new();
554 let mut func = Func::new(names.intern("f"));
555 let opcode = Opcode::new(names.intern("x64.nop"));
556 let block = func.create_block();
557 let regs: Vec<Reg> = (0..6).map(|_| func.new_vreg(GPR)).collect();
558 for reg in ®s {
559 func.build(block, opcode).def(*reg, GPR).finish();
560 }
561 let mut last = func.build(block, opcode);
562 for reg in ®s {
563 last = last.uses(*reg, GPR);
564 }
565 last.finish();
566
567 let (order, live) = read(&func);
568 let mut assignment = Assignment::empty(func.vregs());
569 let places = [Place::Slot(0), Place::Reg(RCX), Place::Reg(RAX)];
570 for (number, reg) in regs.iter().enumerate() {
571 assignment.put(*reg, places[number % 3]);
572 }
573
574 let rcx = RCX.number();
575 let rax = RAX.number();
576 assert_eq!(
577 said(&func, &order, &live, &assignment),
578 [
579 "%0 and %3 are both live and both in slot 0".to_string(),
580 format!("%1 and %4 are both live and both in register {rcx}"),
581 format!("%2 and %5 are both live and both in register {rax}"),
582 ]
583 );
584 }
585
586 #[test]
587 fn a_value_that_lives_in_a_hole_of_another_may_share_its_register() {
588 let mut names = Interner::new();
589 let mut func = Func::new(names.intern("f"));
590 let opcode = Opcode::new(names.intern("x64.nop"));
591 let entry = func.create_block();
592 let arm = func.create_block();
593 let tail = func.create_block();
594 let across = func.new_vreg(GPR);
595 let inside = func.new_vreg(GPR);
596 func.build(entry, opcode).def(across, GPR).finish();
597 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
598 func.build(arm, opcode).def(inside, GPR).finish();
599 func.build(arm, opcode).uses(inside, GPR).finish();
600 func.build(tail, opcode).uses(across, GPR).finish();
601
602 let (order, live) = read(&func);
603 let mut assignment = Assignment::empty(func.vregs());
604 assignment.put(across, Place::Reg(RAX));
605 assignment.put(inside, Place::Reg(RAX));
606
607 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
613 }
614
615 #[test]
616 fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
617 let mut names = Interner::new();
618 let mut func = Func::new(names.intern("f"));
619 let opcode = Opcode::new(names.intern("x64.nop"));
620 let block = func.create_block();
621 let first = func.new_vreg(GPR);
622 let second = func.new_vreg(GPR);
623 func.build(block, opcode).def(first, GPR).finish();
624 func.build(block, opcode).def(second, GPR).finish();
625 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
626
627 let (order, live) = read(&func);
628 let mut assignment = Assignment::empty(func.vregs());
629 let slot = assignment.take_slot(GPR);
630 assignment.put(first, Place::Slot(slot));
631 assignment.put(second, Place::Slot(slot));
632
633 let said = said(&func, &order, &live, &assignment);
634 assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
635 }
636
637 #[test]
638 fn two_values_that_are_never_both_wanted_may_share_anything() {
639 let mut names = Interner::new();
640 let mut func = Func::new(names.intern("f"));
641 let opcode = Opcode::new(names.intern("x64.nop"));
642 let block = func.create_block();
643 let first = func.new_vreg(GPR);
644 let second = func.new_vreg(GPR);
645 func.build(block, opcode).def(first, GPR).finish();
646 func.build(block, opcode).uses(first, GPR).finish();
647 func.build(block, opcode).def(second, GPR).finish();
648 func.build(block, opcode).uses(second, GPR).finish();
649
650 let (order, live) = read(&func);
651 let mut assignment = Assignment::empty(func.vregs());
652 assignment.put(first, Place::Reg(RAX));
653 assignment.put(second, Place::Reg(RAX));
654
655 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
656 }
657
658 #[test]
659 fn a_value_left_in_a_register_an_instruction_wants_is_found() {
660 let mut names = Interner::new();
661 let mut func = Func::new(names.intern("f"));
662 let nop = Opcode::new(names.intern("x64.nop"));
663 let divide = Opcode::new(names.intern("x64.idiv"));
664 let block = func.create_block();
665 let held = func.new_vreg(GPR);
666 let dividend = func.new_vreg(GPR);
667 func.build(block, nop).def(held, GPR).finish();
668 func.build(block, nop).def(dividend, GPR).finish();
669 func.build(block, divide)
672 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
673 .finish();
674 func.build(block, nop).uses(held, GPR).finish();
675
676 let (order, live) = read(&func);
677 let mut assignment = Assignment::empty(func.vregs());
678 assignment.put(held, Place::Reg(RAX));
679 assignment.put(dividend, Place::Reg(RCX));
680
681 let said = said(&func, &order, &live, &assignment);
682 assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
683 }
684
685 #[test]
686 fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
687 let mut names = Interner::new();
688 let mut func = Func::new(names.intern("f"));
689 let nop = Opcode::new(names.intern("x64.nop"));
690 let divide = Opcode::new(names.intern("x64.idiv"));
691 let block = func.create_block();
692 let dividend = func.new_vreg(GPR);
693 func.build(block, nop).def(dividend, GPR).finish();
694 func.build(block, divide)
695 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
696 .finish();
697
698 let (order, live) = read(&func);
699 let mut assignment = Assignment::empty(func.vregs());
700 assignment.put(dividend, Place::Reg(RAX));
701
702 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
705 }
706
707 #[test]
708 fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
709 let mut names = Interner::new();
710 let mut func = Func::new(names.intern("f"));
711 let nop = Opcode::new(names.intern("x64.nop"));
712 let wide = Opcode::new(names.intern("x64.wide"));
713 let block = func.create_block();
714 let only = func.new_vreg(GPR);
715 func.build(block, nop).def(only, GPR).finish();
716 func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
717
718 let (order, live) = read(&func);
719 let mut assignment = Assignment::empty(func.vregs());
720 assignment.put(only, Place::Reg(RAX));
721
722 let said = said(&func, &order, &live, &assignment);
723 assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
724 }
725
726 #[test]
727 fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
728 let mut names = Interner::new();
729 let mut func = Func::new(names.intern("f"));
730 let nop = Opcode::new(names.intern("x64.nop"));
731 let add = Opcode::new(names.intern("x64.add"));
732 let block = func.create_block();
733 let left = func.new_vreg(GPR);
734 let right = func.new_vreg(GPR);
735 let sum = func.new_vreg(GPR);
736 func.build(block, nop).def(left, GPR).finish();
737 func.build(block, nop).def(right, GPR).finish();
738 func.build(block, add)
739 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
740 .uses(left, GPR)
741 .uses(right, GPR)
742 .finish();
743 func.build(block, nop).uses(sum, GPR).finish();
744
745 let (order, live) = read(&func);
746 let mut assignment = Assignment::empty(func.vregs());
747 assignment.put(left, Place::Reg(RAX));
748 assignment.put(right, Place::Reg(RCX));
749 assignment.put(sum, Place::Reg(RAX));
750
751 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
754 }
755
756 #[test]
757 fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
758 let mut names = Interner::new();
759 let mut func = Func::new(names.intern("f"));
760 let nop = Opcode::new(names.intern("x64.nop"));
761 let add = Opcode::new(names.intern("x64.add"));
762 let block = func.create_block();
763 let left = func.new_vreg(GPR);
764 let right = func.new_vreg(GPR);
765 let sum = func.new_vreg(GPR);
766 func.build(block, nop).def(left, GPR).finish();
767 func.build(block, nop).def(right, GPR).finish();
768 func.build(block, add)
769 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
770 .uses(left, GPR)
771 .uses(right, GPR)
772 .finish();
773 func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
774
775 let (order, live) = read(&func);
776 let mut assignment = Assignment::empty(func.vregs());
777 assignment.put(left, Place::Reg(RAX));
778 assignment.put(right, Place::Reg(RCX));
779 assignment.put(sum, Place::Reg(RAX));
780
781 let said = said(&func, &order, &live, &assignment);
784 assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
785 }
786
787 #[test]
788 fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
789 let mut names = Interner::new();
790 let mut func = Func::new(names.intern("f"));
791 let nop = Opcode::new(names.intern("x64.nop"));
792 let add = Opcode::new(names.intern("x64.add"));
793 let block = func.create_block();
794 let left = func.new_vreg(GPR);
795 let right = func.new_vreg(GPR);
796 let sum = func.new_vreg(GPR);
797 func.build(block, nop).def(left, GPR).finish();
798 func.build(block, nop).def(right, GPR).finish();
799 func.build(block, add)
800 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
801 .uses(left, GPR)
802 .uses(right, GPR)
803 .finish();
804 func.build(block, nop).uses(sum, GPR).finish();
805
806 let (order, live) = read(&func);
807 let mut assignment = Assignment::empty(func.vregs());
808 assignment.put(left, Place::Reg(RAX));
809 assignment.put(right, Place::Reg(RCX));
810 assignment.put(sum, Place::Reg(RCX));
811
812 let said = said(&func, &order, &live, &assignment);
815 assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
816 }
817
818 #[test]
819 fn a_two_address_instruction_may_not_write_the_register_it_read_over_its_own_last_answer() {
820 let mut names = Interner::new();
821 let mut func = Func::new(names.intern("f"));
822 let nop = Opcode::new(names.intern("x64.nop"));
823 let add = Opcode::new(names.intern("x64.add"));
824 let head = func.create_block();
825 let latch = func.create_block();
826 let out = func.create_block();
827 let source = func.new_vreg(GPR);
828 let carried = func.new_vreg(GPR);
829 func.build(head, nop).def(source, GPR).finish();
830 func.build(head, nop).def(carried, GPR).finish();
831 *func.succs_mut(head) = vec![BlockCall::to(latch)];
832 func.build(latch, add)
833 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
834 .uses(source, GPR)
835 .uses(carried, GPR)
836 .finish();
837 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
838 func.build(out, nop).uses(carried, GPR).finish();
839
840 let (order, live) = read(&func);
841 let mut assignment = Assignment::empty(func.vregs());
842 assignment.put(source, Place::Reg(RAX));
843 assignment.put(carried, Place::Reg(RAX));
844
845 let said = said(&func, &order, &live, &assignment);
849 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
850 }
851
852 #[test]
853 fn a_two_address_answer_with_a_hole_in_front_of_it_still_may_not_take_the_other_operand() {
854 let mut names = Interner::new();
855 let mut func = Func::new(names.intern("f"));
856 let nop = Opcode::new(names.intern("x64.nop"));
857 let add = Opcode::new(names.intern("x64.add"));
858 let entry = func.create_block();
859 let head = func.create_block();
860 let arm = func.create_block();
861 let latch = func.create_block();
862 let out = func.create_block();
863 let seed = func.new_vreg(GPR);
864 let sum = func.new_vreg(GPR);
865 let inside = func.new_vreg(GPR);
866 let loaded = func.new_vreg(GPR);
867 func.build(entry, nop).def(seed, GPR).finish();
868 func.build(entry, nop).def(sum, GPR).finish();
869 *func.succs_mut(entry) = vec![BlockCall::to(head)];
870 func.build(head, nop).uses(sum, GPR).finish();
871 *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
872 func.build(arm, nop).def(inside, GPR).finish();
873 func.build(arm, nop).uses(inside, GPR).finish();
874 *func.succs_mut(arm) = vec![BlockCall::to(out)];
875 func.build(latch, nop).def(loaded, GPR).finish();
876 func.build(latch, add)
877 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
878 .uses(seed, GPR)
879 .uses(loaded, GPR)
880 .finish();
881 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
882
883 let (order, live) = read(&func);
884 let mut assignment = Assignment::empty(func.vregs());
885 assignment.put(seed, Place::Reg(RCX));
886 assignment.put(sum, Place::Reg(RAX));
887 assignment.put(inside, Place::Reg(RDX));
888 assignment.put(loaded, Place::Reg(RAX));
889
890 let said = said(&func, &order, &live, &assignment);
896 assert_eq!(said, ["%1 and %3 are both live and both in register 0"]);
897 }
898
899 #[test]
900 fn a_report_names_every_problem() {
901 let mut names = Interner::new();
902 let mut func = Func::new(names.intern("f"));
903 let opcode = Opcode::new(names.intern("x64.nop"));
904 let block = func.create_block();
905 let first = func.new_vreg(GPR);
906 let second = func.new_vreg(GPR);
907 func.build(block, opcode).def(first, GPR).finish();
908 func.build(block, opcode).def(second, GPR).finish();
909 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
910
911 let (order, live) = read(&func);
912 let mut assignment = Assignment::empty(func.vregs());
913 assignment.put(first, Place::Reg(RAX));
914 assignment.put(second, Place::Reg(RAX));
915
916 let problems = check(&func, &order, &live, &assignment);
917 assert_eq!(
918 report(&problems),
919 "the allocation is wrong in 1 place\n %0 and %1 are both live and both in register 0"
920 );
921 assert_eq!(report(&[]), "the allocation is wrong in 0 places");
922 }
923}