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