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