1use rucc_mir::{Block, Constraint, Func, Inst, Operand, Param, Reg};
75use rucc_target::{PhysReg, RegClass};
76
77use crate::assign::{Assignment, Env, Place};
78use crate::moves::{self, Move};
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct Edit {
83 pub at: At,
85 pub mov: Move<Place>,
87 pub class: RegClass,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum At {
94 Before(Inst),
96 After(Inst),
99 StartOf(Block),
101 EndOf(Block),
104}
105
106#[must_use]
116pub fn rewrite(func: &mut Func, assignment: &Assignment, env: &Env) -> Vec<Edit> {
117 let blocks: Vec<Block> = func.blocks().collect();
118 assert!(
119 func.entry().is_none_or(|entry| func[entry].params.is_empty()),
120 "what arrives in a function is not a block parameter"
121 );
122
123 let mut edits = Vec::new();
124 for &block in &blocks {
125 let insts: Vec<Inst> = func.insts(block).collect();
126 for inst in insts {
127 instruction(func, assignment, env, inst, &mut edits);
128 }
129 }
130
131 let preds = preds(func, &blocks);
132 for &block in &blocks {
133 edges(func, assignment, env, block, &preds, &mut edits);
134 }
135 for &block in &blocks {
136 func.params_mut(block).clear();
137 for call in func.succs_mut(block) {
138 call.args.clear();
139 }
140 }
141 edits
142}
143
144fn instruction(
146 func: &mut Func,
147 assignment: &Assignment,
148 env: &Env,
149 inst: Inst,
150 edits: &mut Vec<Edit>,
151) {
152 let list = func[inst].operands;
153 let mut operands: Vec<Operand> = func[list].to_vec();
154 let mut before: Vec<(Move<Place>, RegClass)> = Vec::new();
155 let mut after: Vec<(Move<Place>, RegClass)> = Vec::new();
156 let mut taken = Taken::new();
157
158 let places: Vec<Place> =
161 operands.iter().map(|operand| place(assignment, operand.reg)).collect();
162
163 let mut reusing: Vec<usize> = Vec::new();
167
168 for (index, operand) in operands.iter_mut().enumerate() {
169 let fixed = match operand.constraint {
170 Constraint::Fixed(at) => Some(at),
171 _ => None,
172 };
173 let at = match (place(assignment, operand.reg), fixed) {
174 (Place::Reg(at), None) => at,
175 (Place::Reg(at), Some(fixed)) => {
176 if at != fixed {
177 let (there, here) = (Place::Reg(fixed), Place::Reg(at));
178 push(&mut before, &mut after, operand, Move::new(there, here));
179 }
180 fixed
181 }
182 (Place::Slot(_), None) if matches!(operand.constraint, Constraint::Reuse(_)) => {
183 reusing.push(index);
184 continue;
185 }
186 (Place::Slot(slot), fixed) => {
187 let at = fixed.unwrap_or_else(|| taken.next(env, operand.class));
188 push(
189 &mut before,
190 &mut after,
191 operand,
192 Move::new(Place::Reg(at), Place::Slot(slot)),
193 );
194 at
195 }
196 };
197 operand.reg = Reg::physical(at);
198 }
199
200 for index in reusing {
201 let Constraint::Reuse(other) = operands[index].constraint else {
202 unreachable!("only an operand that reuses another was left for this pass")
203 };
204 let Place::Slot(slot) = places[index] else {
205 unreachable!("only a spilled operand was left for this pass")
206 };
207 let other = usize::from(other);
218 let at = match places[other] {
219 Place::Slot(_) => phys(operands[other].reg),
220 Place::Reg(_) => taken.next(env, operands[index].class),
221 };
222 push(
223 &mut before,
224 &mut after,
225 &operands[index],
226 Move::new(Place::Reg(at), Place::Slot(slot)),
227 );
228 operands[index].reg = Reg::physical(at);
229 }
230
231 for index in 0..operands.len() {
235 let Constraint::Reuse(other) = operands[index].constraint else { continue };
236 let (to, from) = (operands[index], operands[usize::from(other)]);
237 if to.reg != from.reg {
238 let mov = Move::new(Place::Reg(phys(to.reg)), Place::Reg(phys(from.reg)));
239 before.push((mov, to.class));
240 }
241 }
242
243 func[list].copy_from_slice(&operands);
244 edits.extend(before.into_iter().map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
245 edits.extend(after.into_iter().map(|(mov, class)| Edit { at: At::After(inst), mov, class }));
246}
247
248#[derive(Debug, Default)]
254struct Taken(Vec<usize>);
255
256impl Taken {
257 fn new() -> Self {
259 Self::default()
260 }
261
262 fn next(&mut self, env: &Env, class: RegClass) -> PhysReg {
271 let index = usize::from(class.number());
272 if self.0.len() <= index {
273 self.0.resize(index + 1, 0);
274 }
275 let scratch = *env
276 .scratch(class)
277 .get(self.0[index])
278 .expect("an instruction wanting more scratch registers than the class has");
279 self.0[index] += 1;
280 scratch
281 }
282}
283
284fn push(
287 before: &mut Vec<(Move<Place>, RegClass)>,
288 after: &mut Vec<(Move<Place>, RegClass)>,
289 operand: &Operand,
290 mov: Move<Place>,
291) {
292 if operand.role.is_def() {
293 after.push((Move::new(mov.from, mov.to), operand.class));
294 } else {
295 before.push((mov, operand.class));
296 }
297}
298
299fn edges(
301 func: &mut Func,
302 assignment: &Assignment,
303 env: &Env,
304 block: Block,
305 preds: &[usize],
306 edits: &mut Vec<Edit>,
307) {
308 let succs = func[block].succs.clone();
309 let single = succs.len() == 1;
310 for call in &succs {
311 let params = func[call.block].params.clone();
312 assert_eq!(
313 params.len(),
314 call.args.len(),
315 "an edge carries what the block it goes to asks for"
316 );
317 if params.is_empty() {
318 continue;
319 }
320 assert!(
321 single || preds[call.block.index()] == 1,
322 "a critical edge has nowhere to put its moves and has to be split before allocation"
323 );
324 let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
325 edits.extend(edge(assignment, env, ¶ms, &call.args, at));
326 }
327}
328
329fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
331 let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
332 classes.sort_unstable();
333 classes.dedup();
334
335 let mut edits = Vec::new();
336 for class in classes {
337 let parallel: Vec<Move<Place>> = params
340 .iter()
341 .zip(args)
342 .filter(|(param, _)| param.class == class)
343 .map(|(param, &arg)| Move::new(place(assignment, param.reg), place(assignment, arg)))
344 .collect();
345 let scratch = env.scratch(class);
346 let cycle = *scratch
347 .first()
348 .expect("a class whose values are passed on an edge and which has no scratch register");
349 for mov in moves::sequence(¶llel, Place::Reg(cycle)) {
350 match (mov.to, mov.from) {
351 (Place::Slot(_), Place::Slot(_)) => {
355 let through = Place::Reg(*scratch.get(1).expect(
356 "a class passing a spilled value to a spilled parameter and having only \
357 one scratch register",
358 ));
359 edits.push(Edit { at, mov: Move::new(through, mov.from), class });
360 edits.push(Edit { at, mov: Move::new(mov.to, through), class });
361 }
362 _ => edits.push(Edit { at, mov, class }),
363 }
364 }
365 }
366 edits
367}
368
369fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
371 let mut preds = vec![0; func.block_count()];
372 for &block in blocks {
373 for call in &func[block].succs {
374 preds[call.block.index()] += 1;
375 }
376 }
377 preds
378}
379
380fn place(assignment: &Assignment, reg: Reg) -> Place {
382 assignment.place(reg).unwrap_or_else(|| Place::Reg(phys(reg)))
383}
384
385fn phys(reg: Reg) -> PhysReg {
387 reg.phys().expect("a register the assignment says nothing about and that is not a register")
388}
389
390#[cfg(test)]
391mod tests {
392 use rucc_base::Interner;
393 use rucc_mir::{BlockCall, Opcode};
394 use rucc_target::x86_64::{GPR, RAX, RDX, REGS, SYSV, XMM};
395
396 use super::*;
397 use crate::assign::assign;
398 use crate::live::Live;
399 use crate::order::Order;
400
401 fn env() -> Env {
403 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
404 Env::new().with(GPR, order, scratch)
405 }
406
407 fn narrow(count: usize) -> Env {
409 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
410 }
411
412 fn named(class: RegClass, place: Place) -> String {
417 match place {
418 Place::Reg(reg) => REGS.name(class, reg).expect("a register").to_string(),
419 Place::Slot(slot) => format!("slot{slot}"),
420 }
421 }
422
423 fn run(func: &mut Func, env: &Env) -> Vec<String> {
425 let order = Order::of(func);
426 let live = Live::of(func, &order);
427 let assignment = assign(func, &order, &live, env);
428 rewrite(func, &assignment, env)
429 .into_iter()
430 .map(|edit| {
431 let at = match edit.at {
432 At::Before(inst) => format!("before {}", inst.index()),
433 At::After(inst) => format!("after {}", inst.index()),
434 At::StartOf(block) => format!("start of {}", block.index()),
435 At::EndOf(block) => format!("end of {}", block.index()),
436 };
437 format!(
438 "{at}: {} = {}",
439 named(edit.class, edit.mov.to),
440 named(edit.class, edit.mov.from)
441 )
442 })
443 .collect()
444 }
445
446 fn operands(func: &Func, inst: Inst) -> Vec<String> {
448 func[func[inst].operands]
449 .iter()
450 .map(|operand| named(operand.class, Place::Reg(phys(operand.reg))))
451 .collect()
452 }
453
454 #[test]
455 fn every_operand_ends_up_naming_the_register_its_value_was_given() {
456 let mut names = Interner::new();
457 let mut func = Func::new(names.intern("f"));
458 let opcode = Opcode::new(names.intern("x64.nop"));
459 let block = func.create_block();
460 let first = func.new_vreg(GPR);
461 let second = func.new_vreg(GPR);
462 func.build(block, opcode).def(first, GPR).finish();
463 func.build(block, opcode).def(second, GPR).finish();
464 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
465
466 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
467 assert_eq!(operands(&func, read), ["rax", "rcx"]);
468 }
469
470 #[test]
471 fn a_register_an_instruction_insists_on_costs_nothing_when_the_values_can_have_it() {
472 let mut names = Interner::new();
473 let mut func = Func::new(names.intern("f"));
474 let opcode = Opcode::new(names.intern("x64.nop"));
475 let block = func.create_block();
476 let dividend = func.new_vreg(GPR);
477 let quotient = func.new_vreg(GPR);
478 func.build(block, opcode).def(dividend, GPR).finish();
479 let divide = func
480 .build(block, opcode)
481 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
482 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
483 .finish();
484 func.build(block, opcode).uses(quotient, GPR).finish();
485
486 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
490 assert_eq!(operands(&func, divide), ["rax", "rax"]);
491 }
492
493 #[test]
494 fn a_register_an_instruction_insists_on_is_moved_into_when_the_value_cannot_have_it() {
495 let mut names = Interner::new();
496 let mut func = Func::new(names.intern("f"));
497 let opcode = Opcode::new(names.intern("x64.nop"));
498 let block = func.create_block();
499 let dividend = func.new_vreg(GPR);
500 let quotient = func.new_vreg(GPR);
501 func.build(block, opcode).def(dividend, GPR).finish();
502 let divide = func
503 .build(block, opcode)
504 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
505 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
506 .finish();
507 func.build(block, opcode).uses(quotient, GPR).finish();
508 func.build(block, opcode).uses(dividend, GPR).finish();
509
510 assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx"]);
514 assert_eq!(operands(&func, divide), ["rax", "rax"]);
515 }
516
517 #[test]
518 fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
519 let mut names = Interner::new();
520 let mut func = Func::new(names.intern("f"));
521 let opcode = Opcode::new(names.intern("x64.nop"));
522 let block = func.create_block();
523 let left = func.new_vreg(GPR);
524 let right = func.new_vreg(GPR);
525 let sum = func.new_vreg(GPR);
526 func.build(block, opcode).def(left, GPR).finish();
527 func.build(block, opcode).def(right, GPR).finish();
528 let add = func
529 .build(block, opcode)
530 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
531 .uses(left, GPR)
532 .uses(right, GPR)
533 .finish();
534 func.build(block, opcode).uses(left, GPR).finish();
535
536 assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
539 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
540 }
541
542 #[test]
543 fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
544 let mut names = Interner::new();
545 let mut func = Func::new(names.intern("f"));
546 let opcode = Opcode::new(names.intern("x64.nop"));
547 let block = func.create_block();
548 let left = func.new_vreg(GPR);
549 let right = func.new_vreg(GPR);
550 let sum = func.new_vreg(GPR);
551 func.build(block, opcode).def(left, GPR).finish();
552 func.build(block, opcode).def(right, GPR).finish();
553 let add = func
554 .build(block, opcode)
555 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
556 .uses(left, GPR)
557 .uses(right, GPR)
558 .finish();
559 func.build(block, opcode).uses(right, GPR).finish();
560
561 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
562 assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
563 }
564
565 #[test]
566 fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
567 let mut names = Interner::new();
568 let mut func = Func::new(names.intern("f"));
569 let opcode = Opcode::new(names.intern("x64.nop"));
570 let block = func.create_block();
571 let first = func.new_vreg(GPR);
572 let second = func.new_vreg(GPR);
573 func.build(block, opcode).def(first, GPR).finish();
574 func.build(block, opcode).def(second, GPR).finish();
575 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
576
577 assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
581 assert_eq!(operands(&func, read), ["rax", "rcx"]);
582 }
583
584 #[test]
591 fn a_two_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
592 let mut names = Interner::new();
593 let mut func = Func::new(names.intern("f"));
594 let opcode = Opcode::new(names.intern("x64.nop"));
595 let block = func.create_block();
596 let keeper = func.new_vreg(GPR);
597 let left = func.new_vreg(GPR);
598 let right = func.new_vreg(GPR);
599 let sum = func.new_vreg(GPR);
600 func.build(block, opcode).def(keeper, GPR).finish();
601 func.build(block, opcode)
602 .operand(Operand::write(left, GPR).with(Constraint::Stack))
603 .finish();
604 func.build(block, opcode)
605 .operand(Operand::write(right, GPR).with(Constraint::Stack))
606 .finish();
607 let add = func
608 .build(block, opcode)
609 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
610 .uses(left, GPR)
611 .uses(right, GPR)
612 .finish();
613 func.build(block, opcode).uses(keeper, GPR).finish();
614 func.build(block, opcode).uses(sum, GPR).finish();
615
616 assert_eq!(
620 run(&mut func, &narrow(1)),
621 [
622 "after 1: slot0 = rcx",
623 "after 2: slot1 = rcx",
624 "before 3: rcx = slot0",
625 "before 3: rdx = slot1",
626 "after 3: slot2 = rcx",
627 "before 5: rcx = slot2",
628 ]
629 );
630 assert_eq!(operands(&func, add), ["rcx", "rcx", "rdx"]);
631 }
632
633 #[test]
640 fn a_spilled_answer_does_not_write_over_a_register_the_assignment_gave_to_something_else() {
641 let mut names = Interner::new();
642 let mut func = Func::new(names.intern("f"));
643 let opcode = Opcode::new(names.intern("x64.nop"));
644 let block = func.create_block();
645 let left = func.new_vreg(GPR);
646 let right = func.new_vreg(GPR);
647 let sum = func.new_vreg(GPR);
648 func.build(block, opcode).def(left, GPR).finish();
649 func.build(block, opcode)
650 .operand(Operand::write(right, GPR).with(Constraint::Stack))
651 .finish();
652 let add = func
653 .build(block, opcode)
654 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
655 .uses(left, GPR)
656 .uses(right, GPR)
657 .finish();
658 func.build(block, opcode).uses(left, GPR).finish();
659 func.build(block, opcode).uses(sum, GPR).finish();
660
661 assert_eq!(
664 run(&mut func, &narrow(1)),
665 [
666 "after 1: slot0 = rcx",
667 "before 2: rcx = slot0",
668 "before 2: rdx = rax",
669 "after 2: slot1 = rdx",
670 "before 4: rcx = slot1",
671 ]
672 );
673 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
674 }
675
676 #[test]
681 fn an_instruction_reading_out_of_two_files_takes_the_first_scratch_register_of_each() {
682 let mut names = Interner::new();
683 let mut func = Func::new(names.intern("f"));
684 let opcode = Opcode::new(names.intern("x64.nop"));
685 let block = func.create_block();
686 let integer = func.new_vreg(GPR);
687 let number = func.new_vreg(XMM);
688 let spare = func.new_vreg(GPR);
689 let other = func.new_vreg(XMM);
690 func.build(block, opcode).def(integer, GPR).finish();
691 func.build(block, opcode).def(number, XMM).finish();
692 func.build(block, opcode).def(spare, GPR).finish();
693 func.build(block, opcode).def(other, XMM).finish();
694 func.build(block, opcode).uses(integer, GPR).uses(number, XMM).finish();
695 let read = func.build(block, opcode).uses(spare, GPR).uses(other, XMM).finish();
696
697 let env = Env::new().with(GPR, &SYSV.int_order[..1], &SYSV.int_order[1..3]).with(
700 XMM,
701 &SYSV.sse_order[..1],
702 &SYSV.sse_order[1..3],
703 );
704 assert_eq!(
705 run(&mut func, &env),
706 [
707 "after 2: slot0 = rcx",
708 "after 3: slot1 = xmm1",
709 "before 5: rcx = slot0",
710 "before 5: xmm1 = slot1",
711 ]
712 );
713 assert_eq!(operands(&func, read), ["rcx", "xmm1"]);
714 }
715
716 #[test]
717 fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
718 let mut names = Interner::new();
719 let mut func = Func::new(names.intern("f"));
720 let opcode = Opcode::new(names.intern("x64.nop"));
721 let head = func.create_block();
722 let tail = func.create_block();
723 let held = func.new_vreg(GPR);
724 let carried = func.new_vreg(GPR);
725 func.build(head, opcode).def(held, GPR).finish();
726 func.build(head, opcode).def(carried, GPR).finish();
727 func.build(head, opcode).uses(held, GPR).finish();
728 let param = func.append_param(tail, GPR);
729 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
730 let read = func.build(tail, opcode).uses(param, GPR).finish();
731
732 assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
736 assert_eq!(operands(&func, read), ["rax"]);
737 assert!(func[tail].params.is_empty());
740 assert!(func[head].succs[0].args.is_empty());
741 }
742
743 #[test]
744 fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
745 let mut names = Interner::new();
746 let mut func = Func::new(names.intern("f"));
747 let opcode = Opcode::new(names.intern("x64.nop"));
748 let head = func.create_block();
749 let left = func.create_block();
750 let right = func.create_block();
751 let held = func.new_vreg(GPR);
752 let carried = func.new_vreg(GPR);
753 func.build(head, opcode).def(held, GPR).finish();
754 func.build(head, opcode).def(carried, GPR).finish();
755 func.build(head, opcode).uses(held, GPR).finish();
756 let taken = func.append_param(left, GPR);
757 *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
758 func.build(left, opcode).uses(taken, GPR).finish();
759
760 assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
764 }
765
766 #[test]
767 fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
768 let mut names = Interner::new();
769 let mut func = Func::new(names.intern("f"));
770 let opcode = Opcode::new(names.intern("x64.nop"));
771 let head = func.create_block();
772 let body = func.create_block();
773 let first = func.new_vreg(GPR);
774 let second = func.new_vreg(GPR);
775 func.build(head, opcode).def(first, GPR).finish();
776 func.build(head, opcode).def(second, GPR).finish();
777 let left = func.append_param(body, GPR);
778 let right = func.append_param(body, GPR);
779 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
780 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
781 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
782
783 assert_eq!(
787 run(&mut func, &env()),
788 ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
789 );
790 }
791
792 #[test]
793 fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
794 let mut names = Interner::new();
795 let mut func = Func::new(names.intern("f"));
796 let opcode = Opcode::new(names.intern("x64.nop"));
797 let head = func.create_block();
798 let body = func.create_block();
799 let first = func.new_vreg(GPR);
800 let second = func.new_vreg(GPR);
801 func.build(head, opcode).def(first, GPR).finish();
802 func.build(head, opcode).def(second, GPR).finish();
803 let left = func.append_param(body, GPR);
804 let right = func.append_param(body, GPR);
805 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
806 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
807
808 assert_eq!(
813 run(&mut func, &narrow(1)),
814 [
815 "after 1: slot0 = rcx",
816 "before 2: rcx = slot1",
817 "end of 0: rdx = slot0",
818 "end of 0: slot1 = rdx",
819 ]
820 );
821 }
822
823 #[test]
824 #[should_panic(expected = "a critical edge has nowhere to put its moves")]
825 fn a_critical_edge_is_refused() {
826 let mut names = Interner::new();
827 let mut func = Func::new(names.intern("f"));
828 let opcode = Opcode::new(names.intern("x64.nop"));
829 let head = func.create_block();
830 let other = func.create_block();
831 let join = func.create_block();
832 let value = func.new_vreg(GPR);
833 func.build(head, opcode).def(value, GPR).finish();
834 let param = func.append_param(join, GPR);
835 *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
836 *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
837 func.build(join, opcode).uses(param, GPR).finish();
838
839 let _ = run(&mut func, &env());
840 }
841
842 #[test]
843 #[should_panic(expected = "what arrives in a function is not a block parameter")]
844 fn a_parameter_on_the_entry_block_is_refused() {
845 let mut names = Interner::new();
846 let mut func = Func::new(names.intern("f"));
847 let block = func.create_block();
848 let param = func.append_param(block, GPR);
849 let opcode = Opcode::new(names.intern("x64.nop"));
850 func.build(block, opcode).uses(param, GPR).finish();
851
852 let _ = run(&mut func, &env());
853 }
854
855 #[test]
856 fn a_value_already_in_a_register_is_left_where_it_is() {
857 let mut names = Interner::new();
858 let mut func = Func::new(names.intern("f"));
859 let opcode = Opcode::new(names.intern("x64.nop"));
860 let block = func.create_block();
861 let inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
862
863 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
864 assert_eq!(operands(&func, inst), ["rdx"]);
865 }
866}