1use std::fmt;
54
55use rucc_mir::{Constraint, Func, Inst, Reg, Role};
56use rucc_target::{PhysReg, RegClass};
57
58use crate::assign::{Assignment, Place};
59use crate::live::{Area, Live, Range};
60use crate::order::{Order, Point};
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Problem {
65 Nowhere {
67 reg: Reg,
69 },
70 Shared {
73 first: Reg,
75 second: Reg,
77 place: Place,
79 },
80 InTheWay {
83 reg: Reg,
85 at: PhysReg,
87 inst: Inst,
89 },
90 NotOnTheStack {
92 reg: Reg,
94 inst: Inst,
96 },
97 NeverWritten {
100 reg: Reg,
102 },
103}
104
105impl fmt::Display for Problem {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 Problem::Nowhere { reg } => write!(f, "{} has nowhere to live", name(*reg)),
109 Problem::Shared { first, second, place } => {
110 let (first, second) = (name(*first), name(*second));
111 write!(f, "{first} and {second} are both live and both in {}", place_name(*place))
112 }
113 Problem::InTheWay { reg, at, inst } => {
114 let reg = name(*reg);
115 let inst = inst.index();
116 write!(f, "{reg} is in register {}, which instruction {inst} wants", at.number())
117 }
118 Problem::NotOnTheStack { reg, inst } => {
119 let reg = name(*reg);
120 write!(f, "{reg} is not on the stack, and instruction {} needs it", inst.index())
121 }
122 Problem::NeverWritten { reg } => {
123 write!(f, "{} is read before anything writes it", name(*reg))
124 }
125 }
126 }
127}
128
129#[must_use]
140pub fn check(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<Problem> {
141 let mut problems = Vec::new();
142 if let Some(entry) = func.entry() {
145 for reg in live.live_in(entry) {
146 problems.push(Problem::NeverWritten { reg });
147 }
148 }
149 let reuses = reuses(func, order);
150 let mut values = Vec::new();
151 for (number, reuse) in reuses.iter().enumerate() {
152 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
153 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
154 continue;
155 };
156 let Some(place) = assignment.place(reg) else {
157 problems.push(Problem::Nowhere { reg });
158 continue;
159 };
160 if let Some(reuse) = reuse {
165 area = area.with(reuse.at);
166 }
167 values.push(Value { reg, class, range: area.hull(), area, place });
168 }
169 overlaps(&values, &reuses, live, &mut problems);
170 instructions(func, order, assignment, &values, &reuses, &mut problems);
171 problems
172}
173
174#[must_use]
176pub fn report(problems: &[Problem]) -> String {
177 let places = if problems.len() == 1 { "place" } else { "places" };
178 let mut report = format!("the allocation is wrong in {} {places}", problems.len());
179 for problem in problems {
180 report.push_str("\n ");
181 report.push_str(&problem.to_string());
182 }
183 report
184}
185
186#[derive(Debug, Clone, Copy)]
188struct Value<'a> {
189 reg: Reg,
190 class: RegClass,
191 range: Range,
193 area: Area<'a>,
196 place: Place,
197}
198
199#[derive(Debug, Clone, Copy)]
201struct Reuse {
202 source: Reg,
203 at: Point,
204}
205
206fn overlaps(
213 values: &[Value<'_>],
214 reuses: &[Option<Reuse>],
215 live: &Live,
216 problems: &mut Vec<Problem>,
217) {
218 let mut sorted = values.to_vec();
219 sorted.sort_by_key(|value| (value.range.start, value.reg));
220 let mut active: Vec<Value<'_>> = Vec::new();
221 for value in sorted {
222 active.retain(|held| held.range.end >= value.range.start);
223 for held in &active {
224 if !together(*held, value)
225 || !held.area.overlaps(value.area)
226 || coalesced(*held, value, reuses, live)
227 {
228 continue;
229 }
230 problems.push(Problem::Shared {
231 first: held.reg,
232 second: value.reg,
233 place: value.place,
234 });
235 }
236 active.push(value);
237 }
238}
239
240fn together(first: Value<'_>, second: Value<'_>) -> bool {
246 match (first.place, second.place) {
247 (Place::Reg(first_at), Place::Reg(second_at)) => {
248 first_at == second_at && first.class == second.class
249 }
250 (Place::Slot(first_slot), Place::Slot(second_slot)) => first_slot == second_slot,
251 _ => false,
252 }
253}
254
255fn coalesced(first: Value<'_>, second: Value<'_>, reuses: &[Option<Reuse>], live: &Live) -> bool {
267 let pair = |source: Value<'_>, dest: Value<'_>| {
268 let Some(reuse) = reuses[index(dest.reg)] else { return false };
269 reuse.source == source.reg && crate::assign::apart(live, source.reg, dest.reg)
270 };
271 pair(first, second) || pair(second, first)
272}
273
274fn instructions(
277 func: &Func,
278 order: &Order,
279 assignment: &Assignment,
280 values: &[Value<'_>],
281 reuses: &[Option<Reuse>],
282 problems: &mut Vec<Problem>,
283) {
284 for block in func.blocks() {
285 for inst in func.insts(block) {
286 for operand in &func[func[inst].operands] {
287 if operand.constraint == Constraint::Stack
288 && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
289 {
290 problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
291 }
292 let at = match operand.constraint {
296 Constraint::Fixed(at) => Some(at),
297 _ => operand.reg.phys(),
298 };
299 let Some(at) = at else { continue };
300 let early = order.early(inst);
301 let point = if operand.role == Role::Def { order.late(inst) } else { early };
302 for value in values {
303 let mine = value.reg == operand.reg
304 || reuses[index(value.reg)].is_some_and(|reuse| {
305 reuse.source == operand.reg
306 && reuse.at == early
307 && value.place == Place::Reg(at)
308 });
309 if mine || value.class != operand.class {
310 continue;
311 }
312 let width = func.width(value.reg);
315 let under = |above| width.is_some_and(|width| width <= above);
316 if matches!(operand.constraint, Constraint::Above(above) if under(above)) {
317 continue;
318 }
319 if value.place == Place::Reg(at) && value.area.covers(point) {
324 problems.push(Problem::InTheWay { reg: value.reg, at, inst });
325 }
326 }
327 }
328 }
329 }
330}
331
332fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
334 let mut reuses = vec![None; func.vregs()];
335 for block in func.blocks() {
336 for inst in func.insts(block) {
337 let operands = &func[func[inst].operands];
338 for operand in operands {
339 let Constraint::Reuse(other) = operand.constraint else { continue };
340 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
341 let Some(number) = number else { continue };
342 let source = operands[usize::from(other)].reg;
343 reuses[number] = Some(Reuse { source, at: order.early(inst) });
344 }
345 }
346 }
347 reuses
348}
349
350fn index(reg: Reg) -> usize {
353 reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
354}
355
356fn name(reg: Reg) -> String {
358 match reg.number() {
359 Some(number) => format!("%{number}"),
360 None => format!("register {}", reg.phys().expect("a physical register").number()),
361 }
362}
363
364fn place_name(place: Place) -> String {
367 match place {
368 Place::Reg(at) => format!("register {}", at.number()),
369 Place::Slot(slot) => format!("slot {slot}"),
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use rucc_base::Interner;
376 use rucc_mir::{BlockCall, Opcode, Operand};
377 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, SYSV};
378
379 use super::*;
380 use crate::assign::{Env, assign};
381
382 fn env() -> Env {
384 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
385 Env::new().with(GPR, order, scratch)
386 }
387
388 fn allocated(func: &Func) -> Vec<String> {
391 let order = Order::of(func);
392 let live = Live::of(func, &order);
393 let assignment = assign(func, &order, &live, &env());
394 said(func, &order, &live, &assignment)
395 }
396
397 fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
399 check(func, order, live, assignment).iter().map(ToString::to_string).collect()
400 }
401
402 fn read(func: &Func) -> (Order, Live) {
404 let order = Order::of(func);
405 let live = Live::of(func, &order);
406 (order, live)
407 }
408
409 fn under_the_top(width: u32) -> Vec<String> {
411 let mut names = Interner::new();
412 let mut func = Func::new(names.intern("f"));
413 let opcode = Opcode::new(names.intern("x64.nop"));
414 let block = func.create_block();
415 let held = func.new_vreg(GPR);
416 func.set_width(held, width);
417 func.build(block, opcode).def(held, GPR).finish();
418 func.build(block, opcode)
419 .operand(Operand::write(Reg::physical(RCX), GPR).with(Constraint::Above(8)))
420 .finish();
421 func.build(block, opcode).uses(held, GPR).finish();
422 let (order, live) = read(&func);
423 let mut assignment = Assignment::empty(func.vregs());
424 assignment.put(held, Place::Reg(RCX));
425 said(&func, &order, &live, &assignment)
426 }
427
428 #[test]
429 fn a_value_under_the_part_an_instruction_writes_is_not_in_its_way() {
430 assert!(under_the_top(8).is_empty(), "{:?}", under_the_top(8));
431 let wide = under_the_top(16);
432 assert!(wide.len() == 1 && wide[0].contains("%0"), "{wide:?}");
433 assert_eq!(under_the_top(0), wide);
434 }
435
436 #[test]
437 fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
438 let mut names = Interner::new();
439 let mut func = Func::new(names.intern("f"));
440 let opcode = Opcode::new(names.intern("x64.nop"));
441 let block = func.create_block();
442 let first = func.new_vreg(GPR);
443 let second = func.new_vreg(GPR);
444 func.build(block, opcode).def(first, GPR).finish();
445 func.build(block, opcode).def(second, GPR).finish();
446 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
447
448 assert_eq!(allocated(&func), Vec::<String>::new());
449 }
450
451 #[test]
452 fn a_value_with_nowhere_to_live_is_found() {
453 let mut names = Interner::new();
454 let mut func = Func::new(names.intern("f"));
455 let opcode = Opcode::new(names.intern("x64.nop"));
456 let block = func.create_block();
457 let only = func.new_vreg(GPR);
458 func.build(block, opcode).def(only, GPR).finish();
459 func.build(block, opcode).uses(only, GPR).finish();
460
461 let (order, live) = read(&func);
462 let assignment = Assignment::empty(func.vregs());
463
464 assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
465 }
466
467 #[test]
468 fn a_value_read_before_anything_writes_it_is_found() {
469 let mut names = Interner::new();
470 let mut func = Func::new(names.intern("f"));
471 let opcode = Opcode::new(names.intern("x64.nop"));
472 let block = func.create_block();
473 let never = func.new_vreg(GPR);
474 func.build(block, opcode).uses(never, GPR).finish();
475
476 let (order, live) = read(&func);
477 let mut assignment = Assignment::empty(func.vregs());
478 assignment.put(never, Place::Reg(RAX));
479
480 assert_eq!(
481 said(&func, &order, &live, &assignment),
482 ["%0 is read before anything writes it"]
483 );
484 }
485
486 #[test]
487 fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
488 let mut names = Interner::new();
489 let mut func = Func::new(names.intern("f"));
490 let opcode = Opcode::new(names.intern("x64.nop"));
491 let block = func.create_block();
492 let first = func.new_vreg(GPR);
493 let second = func.new_vreg(GPR);
494 func.build(block, opcode).def(first, GPR).finish();
495 func.build(block, opcode).def(second, GPR).finish();
496 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
497
498 let (order, live) = read(&func);
499 let mut assignment = Assignment::empty(func.vregs());
500 assignment.put(first, Place::Reg(RAX));
501 assignment.put(second, Place::Reg(RAX));
502
503 let said = said(&func, &order, &live, &assignment);
504 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
505 }
506
507 #[test]
508 fn a_value_that_lives_in_a_hole_of_another_may_share_its_register() {
509 let mut names = Interner::new();
510 let mut func = Func::new(names.intern("f"));
511 let opcode = Opcode::new(names.intern("x64.nop"));
512 let entry = func.create_block();
513 let arm = func.create_block();
514 let tail = func.create_block();
515 let across = func.new_vreg(GPR);
516 let inside = func.new_vreg(GPR);
517 func.build(entry, opcode).def(across, GPR).finish();
518 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
519 func.build(arm, opcode).def(inside, GPR).finish();
520 func.build(arm, opcode).uses(inside, GPR).finish();
521 func.build(tail, opcode).uses(across, GPR).finish();
522
523 let (order, live) = read(&func);
524 let mut assignment = Assignment::empty(func.vregs());
525 assignment.put(across, Place::Reg(RAX));
526 assignment.put(inside, Place::Reg(RAX));
527
528 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
534 }
535
536 #[test]
537 fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
538 let mut names = Interner::new();
539 let mut func = Func::new(names.intern("f"));
540 let opcode = Opcode::new(names.intern("x64.nop"));
541 let block = func.create_block();
542 let first = func.new_vreg(GPR);
543 let second = func.new_vreg(GPR);
544 func.build(block, opcode).def(first, GPR).finish();
545 func.build(block, opcode).def(second, GPR).finish();
546 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
547
548 let (order, live) = read(&func);
549 let mut assignment = Assignment::empty(func.vregs());
550 let slot = assignment.take_slot(GPR);
551 assignment.put(first, Place::Slot(slot));
552 assignment.put(second, Place::Slot(slot));
553
554 let said = said(&func, &order, &live, &assignment);
555 assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
556 }
557
558 #[test]
559 fn two_values_that_are_never_both_wanted_may_share_anything() {
560 let mut names = Interner::new();
561 let mut func = Func::new(names.intern("f"));
562 let opcode = Opcode::new(names.intern("x64.nop"));
563 let block = func.create_block();
564 let first = func.new_vreg(GPR);
565 let second = func.new_vreg(GPR);
566 func.build(block, opcode).def(first, GPR).finish();
567 func.build(block, opcode).uses(first, GPR).finish();
568 func.build(block, opcode).def(second, GPR).finish();
569 func.build(block, opcode).uses(second, GPR).finish();
570
571 let (order, live) = read(&func);
572 let mut assignment = Assignment::empty(func.vregs());
573 assignment.put(first, Place::Reg(RAX));
574 assignment.put(second, Place::Reg(RAX));
575
576 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
577 }
578
579 #[test]
580 fn a_value_left_in_a_register_an_instruction_wants_is_found() {
581 let mut names = Interner::new();
582 let mut func = Func::new(names.intern("f"));
583 let nop = Opcode::new(names.intern("x64.nop"));
584 let divide = Opcode::new(names.intern("x64.idiv"));
585 let block = func.create_block();
586 let held = func.new_vreg(GPR);
587 let dividend = func.new_vreg(GPR);
588 func.build(block, nop).def(held, GPR).finish();
589 func.build(block, nop).def(dividend, GPR).finish();
590 func.build(block, divide)
593 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
594 .finish();
595 func.build(block, nop).uses(held, GPR).finish();
596
597 let (order, live) = read(&func);
598 let mut assignment = Assignment::empty(func.vregs());
599 assignment.put(held, Place::Reg(RAX));
600 assignment.put(dividend, Place::Reg(RCX));
601
602 let said = said(&func, &order, &live, &assignment);
603 assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
604 }
605
606 #[test]
607 fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
608 let mut names = Interner::new();
609 let mut func = Func::new(names.intern("f"));
610 let nop = Opcode::new(names.intern("x64.nop"));
611 let divide = Opcode::new(names.intern("x64.idiv"));
612 let block = func.create_block();
613 let dividend = func.new_vreg(GPR);
614 func.build(block, nop).def(dividend, GPR).finish();
615 func.build(block, divide)
616 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
617 .finish();
618
619 let (order, live) = read(&func);
620 let mut assignment = Assignment::empty(func.vregs());
621 assignment.put(dividend, Place::Reg(RAX));
622
623 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
626 }
627
628 #[test]
629 fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
630 let mut names = Interner::new();
631 let mut func = Func::new(names.intern("f"));
632 let nop = Opcode::new(names.intern("x64.nop"));
633 let wide = Opcode::new(names.intern("x64.wide"));
634 let block = func.create_block();
635 let only = func.new_vreg(GPR);
636 func.build(block, nop).def(only, GPR).finish();
637 func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
638
639 let (order, live) = read(&func);
640 let mut assignment = Assignment::empty(func.vregs());
641 assignment.put(only, Place::Reg(RAX));
642
643 let said = said(&func, &order, &live, &assignment);
644 assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
645 }
646
647 #[test]
648 fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
649 let mut names = Interner::new();
650 let mut func = Func::new(names.intern("f"));
651 let nop = Opcode::new(names.intern("x64.nop"));
652 let add = Opcode::new(names.intern("x64.add"));
653 let block = func.create_block();
654 let left = func.new_vreg(GPR);
655 let right = func.new_vreg(GPR);
656 let sum = func.new_vreg(GPR);
657 func.build(block, nop).def(left, GPR).finish();
658 func.build(block, nop).def(right, GPR).finish();
659 func.build(block, add)
660 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
661 .uses(left, GPR)
662 .uses(right, GPR)
663 .finish();
664 func.build(block, nop).uses(sum, GPR).finish();
665
666 let (order, live) = read(&func);
667 let mut assignment = Assignment::empty(func.vregs());
668 assignment.put(left, Place::Reg(RAX));
669 assignment.put(right, Place::Reg(RCX));
670 assignment.put(sum, Place::Reg(RAX));
671
672 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
675 }
676
677 #[test]
678 fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
679 let mut names = Interner::new();
680 let mut func = Func::new(names.intern("f"));
681 let nop = Opcode::new(names.intern("x64.nop"));
682 let add = Opcode::new(names.intern("x64.add"));
683 let block = func.create_block();
684 let left = func.new_vreg(GPR);
685 let right = func.new_vreg(GPR);
686 let sum = func.new_vreg(GPR);
687 func.build(block, nop).def(left, GPR).finish();
688 func.build(block, nop).def(right, GPR).finish();
689 func.build(block, add)
690 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
691 .uses(left, GPR)
692 .uses(right, GPR)
693 .finish();
694 func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
695
696 let (order, live) = read(&func);
697 let mut assignment = Assignment::empty(func.vregs());
698 assignment.put(left, Place::Reg(RAX));
699 assignment.put(right, Place::Reg(RCX));
700 assignment.put(sum, Place::Reg(RAX));
701
702 let said = said(&func, &order, &live, &assignment);
705 assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
706 }
707
708 #[test]
709 fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
710 let mut names = Interner::new();
711 let mut func = Func::new(names.intern("f"));
712 let nop = Opcode::new(names.intern("x64.nop"));
713 let add = Opcode::new(names.intern("x64.add"));
714 let block = func.create_block();
715 let left = func.new_vreg(GPR);
716 let right = func.new_vreg(GPR);
717 let sum = func.new_vreg(GPR);
718 func.build(block, nop).def(left, GPR).finish();
719 func.build(block, nop).def(right, GPR).finish();
720 func.build(block, add)
721 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
722 .uses(left, GPR)
723 .uses(right, GPR)
724 .finish();
725 func.build(block, nop).uses(sum, GPR).finish();
726
727 let (order, live) = read(&func);
728 let mut assignment = Assignment::empty(func.vregs());
729 assignment.put(left, Place::Reg(RAX));
730 assignment.put(right, Place::Reg(RCX));
731 assignment.put(sum, Place::Reg(RCX));
732
733 let said = said(&func, &order, &live, &assignment);
736 assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
737 }
738
739 #[test]
740 fn a_two_address_instruction_may_not_write_the_register_it_read_over_its_own_last_answer() {
741 let mut names = Interner::new();
742 let mut func = Func::new(names.intern("f"));
743 let nop = Opcode::new(names.intern("x64.nop"));
744 let add = Opcode::new(names.intern("x64.add"));
745 let head = func.create_block();
746 let latch = func.create_block();
747 let out = func.create_block();
748 let source = func.new_vreg(GPR);
749 let carried = func.new_vreg(GPR);
750 func.build(head, nop).def(source, GPR).finish();
751 func.build(head, nop).def(carried, GPR).finish();
752 *func.succs_mut(head) = vec![BlockCall::to(latch)];
753 func.build(latch, add)
754 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
755 .uses(source, GPR)
756 .uses(carried, GPR)
757 .finish();
758 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
759 func.build(out, nop).uses(carried, GPR).finish();
760
761 let (order, live) = read(&func);
762 let mut assignment = Assignment::empty(func.vregs());
763 assignment.put(source, Place::Reg(RAX));
764 assignment.put(carried, Place::Reg(RAX));
765
766 let said = said(&func, &order, &live, &assignment);
770 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
771 }
772
773 #[test]
774 fn a_two_address_answer_with_a_hole_in_front_of_it_still_may_not_take_the_other_operand() {
775 let mut names = Interner::new();
776 let mut func = Func::new(names.intern("f"));
777 let nop = Opcode::new(names.intern("x64.nop"));
778 let add = Opcode::new(names.intern("x64.add"));
779 let entry = func.create_block();
780 let head = func.create_block();
781 let arm = func.create_block();
782 let latch = func.create_block();
783 let out = func.create_block();
784 let seed = func.new_vreg(GPR);
785 let sum = func.new_vreg(GPR);
786 let inside = func.new_vreg(GPR);
787 let loaded = func.new_vreg(GPR);
788 func.build(entry, nop).def(seed, GPR).finish();
789 func.build(entry, nop).def(sum, GPR).finish();
790 *func.succs_mut(entry) = vec![BlockCall::to(head)];
791 func.build(head, nop).uses(sum, GPR).finish();
792 *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
793 func.build(arm, nop).def(inside, GPR).finish();
794 func.build(arm, nop).uses(inside, GPR).finish();
795 *func.succs_mut(arm) = vec![BlockCall::to(out)];
796 func.build(latch, nop).def(loaded, GPR).finish();
797 func.build(latch, add)
798 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
799 .uses(seed, GPR)
800 .uses(loaded, GPR)
801 .finish();
802 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
803
804 let (order, live) = read(&func);
805 let mut assignment = Assignment::empty(func.vregs());
806 assignment.put(seed, Place::Reg(RCX));
807 assignment.put(sum, Place::Reg(RAX));
808 assignment.put(inside, Place::Reg(RDX));
809 assignment.put(loaded, Place::Reg(RAX));
810
811 let said = said(&func, &order, &live, &assignment);
817 assert_eq!(said, ["%1 and %3 are both live and both in register 0"]);
818 }
819
820 #[test]
821 fn a_report_names_every_problem() {
822 let mut names = Interner::new();
823 let mut func = Func::new(names.intern("f"));
824 let opcode = Opcode::new(names.intern("x64.nop"));
825 let block = func.create_block();
826 let first = func.new_vreg(GPR);
827 let second = func.new_vreg(GPR);
828 func.build(block, opcode).def(first, GPR).finish();
829 func.build(block, opcode).def(second, GPR).finish();
830 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
831
832 let (order, live) = read(&func);
833 let mut assignment = Assignment::empty(func.vregs());
834 assignment.put(first, Place::Reg(RAX));
835 assignment.put(second, Place::Reg(RAX));
836
837 let problems = check(&func, &order, &live, &assignment);
838 assert_eq!(
839 report(&problems),
840 "the allocation is wrong in 1 place\n %0 and %1 are both live and both in register 0"
841 );
842 assert_eq!(report(&[]), "the allocation is wrong in 0 places");
843 }
844}