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 {
270 let pair = |source: Value<'_>, dest: Value<'_>| {
271 let Some(reuse) = reuses[index(dest.reg)] else { return false };
272 reuse.source == source.reg
273 && live.area(dest.reg).is_some_and(|area| !area.covers(reuse.at))
274 && live.range(source.reg).is_some_and(|r| r.end == reuse.at)
275 };
276 pair(first, second) || pair(second, first)
277}
278
279fn instructions(
282 func: &Func,
283 order: &Order,
284 assignment: &Assignment,
285 values: &[Value<'_>],
286 reuses: &[Option<Reuse>],
287 problems: &mut Vec<Problem>,
288) {
289 for block in func.blocks() {
290 for inst in func.insts(block) {
291 for operand in &func[func[inst].operands] {
292 if operand.constraint == Constraint::Stack
293 && matches!(assignment.place(operand.reg), Some(Place::Reg(_)))
294 {
295 problems.push(Problem::NotOnTheStack { reg: operand.reg, inst });
296 }
297 let at = match operand.constraint {
301 Constraint::Fixed(at) => Some(at),
302 _ => operand.reg.phys(),
303 };
304 let Some(at) = at else { continue };
305 let early = order.early(inst);
306 let point = if operand.role == Role::Def { order.late(inst) } else { early };
307 for value in values {
308 let mine = value.reg == operand.reg
309 || reuses[index(value.reg)].is_some_and(|reuse| {
310 reuse.source == operand.reg
311 && reuse.at == early
312 && value.place == Place::Reg(at)
313 });
314 if mine || value.class != operand.class {
315 continue;
316 }
317 if value.place == Place::Reg(at) && value.area.covers(point) {
322 problems.push(Problem::InTheWay { reg: value.reg, at, inst });
323 }
324 }
325 }
326 }
327 }
328}
329
330fn reuses(func: &Func, order: &Order) -> Vec<Option<Reuse>> {
332 let mut reuses = vec![None; func.vregs()];
333 for block in func.blocks() {
334 for inst in func.insts(block) {
335 let operands = &func[func[inst].operands];
336 for operand in operands {
337 let Constraint::Reuse(other) = operand.constraint else { continue };
338 let number = operand.reg.number().and_then(|number| usize::try_from(number).ok());
339 let Some(number) = number else { continue };
340 let source = operands[usize::from(other)].reg;
341 reuses[number] = Some(Reuse { source, at: order.early(inst) });
342 }
343 }
344 }
345 reuses
346}
347
348fn index(reg: Reg) -> usize {
351 reg.number().and_then(|number| usize::try_from(number).ok()).unwrap_or(0)
352}
353
354fn name(reg: Reg) -> String {
356 match reg.number() {
357 Some(number) => format!("%{number}"),
358 None => format!("register {}", reg.phys().expect("a physical register").number()),
359 }
360}
361
362fn place_name(place: Place) -> String {
365 match place {
366 Place::Reg(at) => format!("register {}", at.number()),
367 Place::Slot(slot) => format!("slot {slot}"),
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use rucc_base::Interner;
374 use rucc_mir::{BlockCall, Opcode, Operand};
375 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, SYSV};
376
377 use super::*;
378 use crate::assign::{Env, assign};
379
380 fn env() -> Env {
382 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
383 Env::new().with(GPR, order, scratch)
384 }
385
386 fn allocated(func: &Func) -> Vec<String> {
389 let order = Order::of(func);
390 let live = Live::of(func, &order);
391 let assignment = assign(func, &order, &live, &env());
392 said(func, &order, &live, &assignment)
393 }
394
395 fn said(func: &Func, order: &Order, live: &Live, assignment: &Assignment) -> Vec<String> {
397 check(func, order, live, assignment).iter().map(ToString::to_string).collect()
398 }
399
400 fn read(func: &Func) -> (Order, Live) {
402 let order = Order::of(func);
403 let live = Live::of(func, &order);
404 (order, live)
405 }
406
407 #[test]
408 fn an_allocation_the_allocator_worked_out_has_nothing_wrong_with_it() {
409 let mut names = Interner::new();
410 let mut func = Func::new(names.intern("f"));
411 let opcode = Opcode::new(names.intern("x64.nop"));
412 let block = func.create_block();
413 let first = func.new_vreg(GPR);
414 let second = func.new_vreg(GPR);
415 func.build(block, opcode).def(first, GPR).finish();
416 func.build(block, opcode).def(second, GPR).finish();
417 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
418
419 assert_eq!(allocated(&func), Vec::<String>::new());
420 }
421
422 #[test]
423 fn a_value_with_nowhere_to_live_is_found() {
424 let mut names = Interner::new();
425 let mut func = Func::new(names.intern("f"));
426 let opcode = Opcode::new(names.intern("x64.nop"));
427 let block = func.create_block();
428 let only = func.new_vreg(GPR);
429 func.build(block, opcode).def(only, GPR).finish();
430 func.build(block, opcode).uses(only, GPR).finish();
431
432 let (order, live) = read(&func);
433 let assignment = Assignment::empty(func.vregs());
434
435 assert_eq!(said(&func, &order, &live, &assignment), ["%0 has nowhere to live"]);
436 }
437
438 #[test]
439 fn a_value_read_before_anything_writes_it_is_found() {
440 let mut names = Interner::new();
441 let mut func = Func::new(names.intern("f"));
442 let opcode = Opcode::new(names.intern("x64.nop"));
443 let block = func.create_block();
444 let never = func.new_vreg(GPR);
445 func.build(block, opcode).uses(never, GPR).finish();
446
447 let (order, live) = read(&func);
448 let mut assignment = Assignment::empty(func.vregs());
449 assignment.put(never, Place::Reg(RAX));
450
451 assert_eq!(
452 said(&func, &order, &live, &assignment),
453 ["%0 is read before anything writes it"]
454 );
455 }
456
457 #[test]
458 fn two_values_that_are_both_wanted_and_share_a_register_are_found() {
459 let mut names = Interner::new();
460 let mut func = Func::new(names.intern("f"));
461 let opcode = Opcode::new(names.intern("x64.nop"));
462 let block = func.create_block();
463 let first = func.new_vreg(GPR);
464 let second = func.new_vreg(GPR);
465 func.build(block, opcode).def(first, GPR).finish();
466 func.build(block, opcode).def(second, GPR).finish();
467 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
468
469 let (order, live) = read(&func);
470 let mut assignment = Assignment::empty(func.vregs());
471 assignment.put(first, Place::Reg(RAX));
472 assignment.put(second, Place::Reg(RAX));
473
474 let said = said(&func, &order, &live, &assignment);
475 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
476 }
477
478 #[test]
479 fn a_value_that_lives_in_a_hole_of_another_may_share_its_register() {
480 let mut names = Interner::new();
481 let mut func = Func::new(names.intern("f"));
482 let opcode = Opcode::new(names.intern("x64.nop"));
483 let entry = func.create_block();
484 let arm = func.create_block();
485 let tail = func.create_block();
486 let across = func.new_vreg(GPR);
487 let inside = func.new_vreg(GPR);
488 func.build(entry, opcode).def(across, GPR).finish();
489 *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
490 func.build(arm, opcode).def(inside, GPR).finish();
491 func.build(arm, opcode).uses(inside, GPR).finish();
492 func.build(tail, opcode).uses(across, GPR).finish();
493
494 let (order, live) = read(&func);
495 let mut assignment = Assignment::empty(func.vregs());
496 assignment.put(across, Place::Reg(RAX));
497 assignment.put(inside, Place::Reg(RAX));
498
499 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
505 }
506
507 #[test]
508 fn two_values_that_are_both_wanted_and_share_a_slot_are_found() {
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 block = func.create_block();
513 let first = func.new_vreg(GPR);
514 let second = func.new_vreg(GPR);
515 func.build(block, opcode).def(first, GPR).finish();
516 func.build(block, opcode).def(second, GPR).finish();
517 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
518
519 let (order, live) = read(&func);
520 let mut assignment = Assignment::empty(func.vregs());
521 let slot = assignment.take_slot(GPR);
522 assignment.put(first, Place::Slot(slot));
523 assignment.put(second, Place::Slot(slot));
524
525 let said = said(&func, &order, &live, &assignment);
526 assert_eq!(said, ["%0 and %1 are both live and both in slot 0"]);
527 }
528
529 #[test]
530 fn two_values_that_are_never_both_wanted_may_share_anything() {
531 let mut names = Interner::new();
532 let mut func = Func::new(names.intern("f"));
533 let opcode = Opcode::new(names.intern("x64.nop"));
534 let block = func.create_block();
535 let first = func.new_vreg(GPR);
536 let second = func.new_vreg(GPR);
537 func.build(block, opcode).def(first, GPR).finish();
538 func.build(block, opcode).uses(first, GPR).finish();
539 func.build(block, opcode).def(second, GPR).finish();
540 func.build(block, opcode).uses(second, GPR).finish();
541
542 let (order, live) = read(&func);
543 let mut assignment = Assignment::empty(func.vregs());
544 assignment.put(first, Place::Reg(RAX));
545 assignment.put(second, Place::Reg(RAX));
546
547 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
548 }
549
550 #[test]
551 fn a_value_left_in_a_register_an_instruction_wants_is_found() {
552 let mut names = Interner::new();
553 let mut func = Func::new(names.intern("f"));
554 let nop = Opcode::new(names.intern("x64.nop"));
555 let divide = Opcode::new(names.intern("x64.idiv"));
556 let block = func.create_block();
557 let held = func.new_vreg(GPR);
558 let dividend = func.new_vreg(GPR);
559 func.build(block, nop).def(held, GPR).finish();
560 func.build(block, nop).def(dividend, GPR).finish();
561 func.build(block, divide)
564 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
565 .finish();
566 func.build(block, nop).uses(held, GPR).finish();
567
568 let (order, live) = read(&func);
569 let mut assignment = Assignment::empty(func.vregs());
570 assignment.put(held, Place::Reg(RAX));
571 assignment.put(dividend, Place::Reg(RCX));
572
573 let said = said(&func, &order, &live, &assignment);
574 assert_eq!(said, ["%0 is in register 0, which instruction 2 wants"]);
575 }
576
577 #[test]
578 fn the_value_an_instruction_wants_a_register_for_may_be_in_it_already() {
579 let mut names = Interner::new();
580 let mut func = Func::new(names.intern("f"));
581 let nop = Opcode::new(names.intern("x64.nop"));
582 let divide = Opcode::new(names.intern("x64.idiv"));
583 let block = func.create_block();
584 let dividend = func.new_vreg(GPR);
585 func.build(block, nop).def(dividend, GPR).finish();
586 func.build(block, divide)
587 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
588 .finish();
589
590 let (order, live) = read(&func);
591 let mut assignment = Assignment::empty(func.vregs());
592 assignment.put(dividend, Place::Reg(RAX));
593
594 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
597 }
598
599 #[test]
600 fn a_value_that_can_only_be_read_from_memory_and_is_in_a_register_is_found() {
601 let mut names = Interner::new();
602 let mut func = Func::new(names.intern("f"));
603 let nop = Opcode::new(names.intern("x64.nop"));
604 let wide = Opcode::new(names.intern("x64.wide"));
605 let block = func.create_block();
606 let only = func.new_vreg(GPR);
607 func.build(block, nop).def(only, GPR).finish();
608 func.build(block, wide).operand(Operand::read(only, GPR).with(Constraint::Stack)).finish();
609
610 let (order, live) = read(&func);
611 let mut assignment = Assignment::empty(func.vregs());
612 assignment.put(only, Place::Reg(RAX));
613
614 let said = said(&func, &order, &live, &assignment);
615 assert_eq!(said, ["%0 is not on the stack, and instruction 1 needs it"]);
616 }
617
618 #[test]
619 fn a_two_address_instruction_may_write_the_register_it_read_a_finished_value_from() {
620 let mut names = Interner::new();
621 let mut func = Func::new(names.intern("f"));
622 let nop = Opcode::new(names.intern("x64.nop"));
623 let add = Opcode::new(names.intern("x64.add"));
624 let block = func.create_block();
625 let left = func.new_vreg(GPR);
626 let right = func.new_vreg(GPR);
627 let sum = func.new_vreg(GPR);
628 func.build(block, nop).def(left, GPR).finish();
629 func.build(block, nop).def(right, GPR).finish();
630 func.build(block, add)
631 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
632 .uses(left, GPR)
633 .uses(right, GPR)
634 .finish();
635 func.build(block, nop).uses(sum, GPR).finish();
636
637 let (order, live) = read(&func);
638 let mut assignment = Assignment::empty(func.vregs());
639 assignment.put(left, Place::Reg(RAX));
640 assignment.put(right, Place::Reg(RCX));
641 assignment.put(sum, Place::Reg(RAX));
642
643 assert_eq!(said(&func, &order, &live, &assignment), Vec::<String>::new());
646 }
647
648 #[test]
649 fn a_two_address_instruction_may_not_write_over_a_value_wanted_afterwards() {
650 let mut names = Interner::new();
651 let mut func = Func::new(names.intern("f"));
652 let nop = Opcode::new(names.intern("x64.nop"));
653 let add = Opcode::new(names.intern("x64.add"));
654 let block = func.create_block();
655 let left = func.new_vreg(GPR);
656 let right = func.new_vreg(GPR);
657 let sum = func.new_vreg(GPR);
658 func.build(block, nop).def(left, GPR).finish();
659 func.build(block, nop).def(right, GPR).finish();
660 func.build(block, add)
661 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
662 .uses(left, GPR)
663 .uses(right, GPR)
664 .finish();
665 func.build(block, nop).uses(sum, GPR).uses(left, GPR).finish();
666
667 let (order, live) = read(&func);
668 let mut assignment = Assignment::empty(func.vregs());
669 assignment.put(left, Place::Reg(RAX));
670 assignment.put(right, Place::Reg(RCX));
671 assignment.put(sum, Place::Reg(RAX));
672
673 let said = said(&func, &order, &live, &assignment);
676 assert_eq!(said, ["%0 and %2 are both live and both in register 0"]);
677 }
678
679 #[test]
680 fn a_two_address_instruction_may_not_write_the_register_it_reads_its_other_operand_from() {
681 let mut names = Interner::new();
682 let mut func = Func::new(names.intern("f"));
683 let nop = Opcode::new(names.intern("x64.nop"));
684 let add = Opcode::new(names.intern("x64.add"));
685 let block = func.create_block();
686 let left = func.new_vreg(GPR);
687 let right = func.new_vreg(GPR);
688 let sum = func.new_vreg(GPR);
689 func.build(block, nop).def(left, GPR).finish();
690 func.build(block, nop).def(right, GPR).finish();
691 func.build(block, add)
692 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
693 .uses(left, GPR)
694 .uses(right, GPR)
695 .finish();
696 func.build(block, nop).uses(sum, GPR).finish();
697
698 let (order, live) = read(&func);
699 let mut assignment = Assignment::empty(func.vregs());
700 assignment.put(left, Place::Reg(RAX));
701 assignment.put(right, Place::Reg(RCX));
702 assignment.put(sum, Place::Reg(RCX));
703
704 let said = said(&func, &order, &live, &assignment);
707 assert_eq!(said, ["%1 and %2 are both live and both in register 1"]);
708 }
709
710 #[test]
711 fn a_two_address_instruction_may_not_write_the_register_it_read_over_its_own_last_answer() {
712 let mut names = Interner::new();
713 let mut func = Func::new(names.intern("f"));
714 let nop = Opcode::new(names.intern("x64.nop"));
715 let add = Opcode::new(names.intern("x64.add"));
716 let head = func.create_block();
717 let latch = func.create_block();
718 let out = func.create_block();
719 let source = func.new_vreg(GPR);
720 let carried = func.new_vreg(GPR);
721 func.build(head, nop).def(source, GPR).finish();
722 func.build(head, nop).def(carried, GPR).finish();
723 *func.succs_mut(head) = vec![BlockCall::to(latch)];
724 func.build(latch, add)
725 .operand(Operand::write(carried, GPR).with(Constraint::Reuse(1)))
726 .uses(source, GPR)
727 .uses(carried, GPR)
728 .finish();
729 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
730 func.build(out, nop).uses(carried, GPR).finish();
731
732 let (order, live) = read(&func);
733 let mut assignment = Assignment::empty(func.vregs());
734 assignment.put(source, Place::Reg(RAX));
735 assignment.put(carried, Place::Reg(RAX));
736
737 let said = said(&func, &order, &live, &assignment);
741 assert_eq!(said, ["%0 and %1 are both live and both in register 0"]);
742 }
743
744 #[test]
745 fn a_two_address_answer_with_a_hole_in_front_of_it_still_may_not_take_the_other_operand() {
746 let mut names = Interner::new();
747 let mut func = Func::new(names.intern("f"));
748 let nop = Opcode::new(names.intern("x64.nop"));
749 let add = Opcode::new(names.intern("x64.add"));
750 let entry = func.create_block();
751 let head = func.create_block();
752 let arm = func.create_block();
753 let latch = func.create_block();
754 let out = func.create_block();
755 let seed = func.new_vreg(GPR);
756 let sum = func.new_vreg(GPR);
757 let inside = func.new_vreg(GPR);
758 let loaded = func.new_vreg(GPR);
759 func.build(entry, nop).def(seed, GPR).finish();
760 func.build(entry, nop).def(sum, GPR).finish();
761 *func.succs_mut(entry) = vec![BlockCall::to(head)];
762 func.build(head, nop).uses(sum, GPR).finish();
763 *func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
764 func.build(arm, nop).def(inside, GPR).finish();
765 func.build(arm, nop).uses(inside, GPR).finish();
766 *func.succs_mut(arm) = vec![BlockCall::to(out)];
767 func.build(latch, nop).def(loaded, GPR).finish();
768 func.build(latch, add)
769 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
770 .uses(seed, GPR)
771 .uses(loaded, GPR)
772 .finish();
773 *func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
774
775 let (order, live) = read(&func);
776 let mut assignment = Assignment::empty(func.vregs());
777 assignment.put(seed, Place::Reg(RCX));
778 assignment.put(sum, Place::Reg(RAX));
779 assignment.put(inside, Place::Reg(RDX));
780 assignment.put(loaded, Place::Reg(RAX));
781
782 let said = said(&func, &order, &live, &assignment);
788 assert_eq!(said, ["%1 and %3 are both live and both in register 0"]);
789 }
790
791 #[test]
792 fn a_report_names_every_problem() {
793 let mut names = Interner::new();
794 let mut func = Func::new(names.intern("f"));
795 let opcode = Opcode::new(names.intern("x64.nop"));
796 let block = func.create_block();
797 let first = func.new_vreg(GPR);
798 let second = func.new_vreg(GPR);
799 func.build(block, opcode).def(first, GPR).finish();
800 func.build(block, opcode).def(second, GPR).finish();
801 func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
802
803 let (order, live) = read(&func);
804 let mut assignment = Assignment::empty(func.vregs());
805 assignment.put(first, Place::Reg(RAX));
806 assignment.put(second, Place::Reg(RAX));
807
808 let problems = check(&func, &order, &live, &assignment);
809 assert_eq!(
810 report(&problems),
811 "the allocation is wrong in 1 place\n %0 and %1 are both live and both in register 0"
812 );
813 assert_eq!(report(&[]), "the allocation is wrong in 0 places");
814 }
815}