1use rucc_base::hash::Map;
55use rucc_mir::{Block, Func, Inst, Param, Reg};
56use rucc_target::RegClass;
57
58use crate::assign::{Assignment, Env, Place};
59use crate::legalize::{self, Spare, place};
60use crate::moves::{self, Move};
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct Edit {
65 pub at: At,
67 pub mov: Move<Place>,
69 pub class: RegClass,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum At {
76 Before(Inst),
78 After(Inst),
81 StartOf(Block),
83 EndOf(Block),
86}
87
88#[must_use]
103pub fn rewrite(func: &mut Func, assignment: &mut Assignment, env: &Env) -> Vec<Edit> {
104 let blocks: Vec<Block> = func.blocks().collect();
105 assert!(
106 func.entry().is_none_or(|entry| func[entry].params.is_empty()),
107 "what arrives in a function is not a block parameter"
108 );
109
110 let mut edits = Vec::new();
111 let mut spare = Spare::default();
112 let insts: Vec<Inst> = blocks.iter().flat_map(|&block| func.insts(block)).collect();
115 let saves = saves(func, assignment);
116 for inst in insts {
117 let saved = saves.get(&inst).map_or(&[][..], Vec::as_slice);
118 instruction(func, assignment, env, &mut spare, inst, saved, &mut edits);
119 }
120
121 let preds = preds(func, &blocks);
122 for &block in &blocks {
123 behind_the_end(func, block, &preds, &mut edits);
124 }
125 for &block in &blocks {
126 edges(func, assignment, env, block, &preds, &mut edits);
127 }
128 for &block in &blocks {
129 func.params_mut(block).clear();
130 for call in func.succs_mut(block) {
131 call.args.clear();
132 }
133 }
134 edits
135}
136
137fn saves(func: &Func, assignment: &Assignment) -> Map<Inst, Vec<(Move<Place>, RegClass)>> {
140 let mut saves: Map<Inst, Vec<(Move<Place>, RegClass)>> = Map::default();
141 for save in assignment.saves() {
142 let (Some(Place::Reg(at)), Some(class)) =
143 (assignment.place(save.reg), func.class_of(save.reg))
144 else {
145 continue;
146 };
147 let store = Move::new(Place::Slot(save.slot), Place::Reg(at));
148 saves.entry(save.inst).or_default().push((store, class));
149 }
150 saves
151}
152
153fn instruction(
155 func: &mut Func,
156 assignment: &mut Assignment,
157 env: &Env,
158 spare: &mut Spare,
159 inst: Inst,
160 saved: &[(Move<Place>, RegClass)],
161 edits: &mut Vec<Edit>,
162) {
163 let legal = legalize::instruction(func, assignment, env, spare, inst);
164 let list = func[inst].operands;
165 func[list].copy_from_slice(&legal.operands);
166 let before = saved.iter().copied().chain(legal.before);
167 edits.extend(before.map(|(mov, class)| Edit { at: At::Before(inst), mov, class }));
168 let back = saved.iter().map(|&(store, class)| (Move::new(store.from, store.to), class));
169 edits.extend(legal.after.into_iter().chain(back).map(|(mov, class)| Edit {
170 at: At::After(inst),
171 mov,
172 class,
173 }));
174}
175
176fn behind_the_end(func: &Func, block: Block, preds: &[usize], edits: &mut Vec<Edit>) {
189 let succs = &func[block].succs;
190 if succs.len() < 2 {
191 return;
192 }
193 let Some(last) = func.insts(block).last() else { return };
194 if !func[func[last].operands].iter().any(|operand| operand.role.is_def()) {
195 return;
196 }
197 let behind: Vec<Edit> =
198 edits.iter().filter(|edit| edit.at == At::After(last)).copied().collect();
199 if behind.is_empty() {
200 return;
201 }
202 edits.retain(|edit| edit.at != At::After(last));
203 for call in succs {
204 assert!(
205 preds[call.block.index()] == 1,
206 "an edge out of an asm goto that writes something has to be split before allocation"
207 );
208 edits.extend(behind.iter().map(|&edit| Edit { at: At::StartOf(call.block), ..edit }));
209 }
210}
211
212fn edges(
214 func: &mut Func,
215 assignment: &Assignment,
216 env: &Env,
217 block: Block,
218 preds: &[usize],
219 edits: &mut Vec<Edit>,
220) {
221 let succs = func[block].succs.clone();
222 let single = succs.len() == 1;
223 for call in &succs {
224 let params = func[call.block].params.clone();
225 assert_eq!(
226 params.len(),
227 call.args.len(),
228 "an edge carries what the block it goes to asks for"
229 );
230 if params.is_empty() {
231 continue;
232 }
233 assert!(
234 single || preds[call.block.index()] == 1,
235 "a critical edge has nowhere to put its moves and has to be split before allocation"
236 );
237 let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
238 edits.extend(edge(assignment, env, ¶ms, &call.args, at));
239 }
240}
241
242fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
244 let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
245 classes.sort_unstable();
246 classes.dedup();
247
248 let mut edits = Vec::new();
249 for class in classes {
250 let parallel: Vec<Move<Option<Place>>> = params
257 .iter()
258 .zip(args)
259 .filter(|(param, _)| param.class == class)
260 .map(|(param, &arg)| {
261 Move::new(Some(place(assignment, param.reg)), Some(place(assignment, arg)))
262 })
263 .collect();
264 let scratch = env.scratch(class);
265 let cycle = scratch.first().map(|®| Place::Reg(reg));
266 for mov in moves::sequence(¶llel, cycle) {
267 let (Some(to), Some(from)) = (mov.to, mov.from) else {
268 panic!(
269 "a class whose values go round in a cycle on an edge and which has no scratch \
270 register"
271 )
272 };
273 let mov = Move::new(to, from);
274 match (mov.to, mov.from) {
275 (Place::Slot(_), Place::Slot(_)) => {
279 let through = Place::Reg(*scratch.get(1).expect(
280 "a class passing a spilled value to a spilled parameter and having only \
281 one scratch register",
282 ));
283 edits.push(Edit { at, mov: Move::new(through, mov.from), class });
284 edits.push(Edit { at, mov: Move::new(mov.to, through), class });
285 }
286 _ => edits.push(Edit { at, mov, class }),
287 }
288 }
289 }
290 edits
291}
292
293#[must_use]
303pub fn fits(func: &Func, assignment: &Assignment, env: &Env) -> bool {
304 let bare = |class: RegClass| env.scratch(class).is_empty();
305 let slots = assignment.slots();
306 let stacked = assignment.placed().any(|(_, at)| match at {
307 Place::Slot(slot) => usize::try_from(slot)
308 .ok()
309 .and_then(|slot| slots.get(slot))
310 .is_some_and(|&class| bare(class)),
311 Place::Reg(_) => false,
312 });
313 if stacked {
314 return false;
315 }
316 for block in func.blocks() {
317 for call in &func[block].succs {
318 let params = &func[call.block].params;
319 let mut classes: Vec<RegClass> =
320 params.iter().map(|param| param.class).filter(|&class| bare(class)).collect();
321 classes.sort_unstable();
322 classes.dedup();
323 for class in classes {
324 let parallel: Vec<Move<Option<Place>>> = params
327 .iter()
328 .zip(&call.args)
329 .filter(|(param, _)| param.class == class)
330 .map(|(param, &arg)| {
331 Move::new(Some(place(assignment, param.reg)), Some(place(assignment, arg)))
332 })
333 .collect();
334 if moves::sequence(¶llel, None).iter().any(|mov| mov.from.is_none()) {
335 return false;
336 }
337 }
338 }
339 }
340 true
341}
342
343fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
345 let mut preds = vec![0; func.block_count()];
346 for &block in blocks {
347 for call in &func[block].succs {
348 preds[call.block.index()] += 1;
349 }
350 }
351 preds
352}
353
354#[cfg(test)]
355mod tests {
356 use rucc_base::Interner;
357 use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
358 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, REGS, RSI, SYSV, XMM};
359
360 use super::*;
361 use crate::assign::assign;
362 use crate::legalize::phys;
363 use crate::live::Live;
364 use crate::order::Order;
365
366 fn env() -> Env {
368 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
369 Env::new().with(GPR, order, scratch)
370 }
371
372 fn narrow(count: usize) -> Env {
374 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
375 }
376
377 fn named(class: RegClass, place: Place) -> String {
382 match place {
383 Place::Reg(reg) => REGS.name(class, reg).expect("a register").to_string(),
384 Place::Slot(slot) => format!("slot{slot}"),
385 }
386 }
387
388 fn run(func: &mut Func, env: &Env) -> Vec<String> {
390 let order = Order::of(func);
391 let live = Live::of(func, &order);
392 let mut assignment = assign(func, &order, &live, env);
393 rewrite(func, &mut assignment, env)
394 .into_iter()
395 .map(|edit| {
396 let at = match edit.at {
397 At::Before(inst) => format!("before {}", inst.index()),
398 At::After(inst) => format!("after {}", inst.index()),
399 At::StartOf(block) => format!("start of {}", block.index()),
400 At::EndOf(block) => format!("end of {}", block.index()),
401 };
402 format!(
403 "{at}: {} = {}",
404 named(edit.class, edit.mov.to),
405 named(edit.class, edit.mov.from)
406 )
407 })
408 .collect()
409 }
410
411 fn operands(func: &Func, inst: Inst) -> Vec<String> {
413 func[func[inst].operands]
414 .iter()
415 .map(|operand| named(operand.class, Place::Reg(phys(operand.reg))))
416 .collect()
417 }
418
419 #[test]
420 fn every_operand_ends_up_naming_the_register_its_value_was_given() {
421 let mut names = Interner::new();
422 let mut func = Func::new(names.intern("f"));
423 let opcode = Opcode::new(names.intern("x64.nop"));
424 let block = func.create_block();
425 let first = func.new_vreg(GPR);
426 let second = func.new_vreg(GPR);
427 func.build(block, opcode).def(first, GPR).finish();
428 func.build(block, opcode).def(second, GPR).finish();
429 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
430
431 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
432 assert_eq!(operands(&func, read), ["rax", "rcx"]);
433 }
434
435 #[test]
436 fn a_register_an_instruction_insists_on_costs_nothing_when_the_values_can_have_it() {
437 let mut names = Interner::new();
438 let mut func = Func::new(names.intern("f"));
439 let opcode = Opcode::new(names.intern("x64.nop"));
440 let block = func.create_block();
441 let dividend = func.new_vreg(GPR);
442 let quotient = func.new_vreg(GPR);
443 func.build(block, opcode).def(dividend, GPR).finish();
444 let divide = func
445 .build(block, opcode)
446 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
447 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
448 .finish();
449 func.build(block, opcode).uses(quotient, GPR).finish();
450
451 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
455 assert_eq!(operands(&func, divide), ["rax", "rax"]);
456 }
457
458 #[test]
459 fn a_register_an_instruction_insists_on_is_moved_into_when_the_value_cannot_have_it() {
460 let mut names = Interner::new();
461 let mut func = Func::new(names.intern("f"));
462 let opcode = Opcode::new(names.intern("x64.nop"));
463 let block = func.create_block();
464 let dividend = func.new_vreg(GPR);
465 let quotient = func.new_vreg(GPR);
466 func.build(block, opcode).def(dividend, GPR).finish();
467 let divide = func
468 .build(block, opcode)
469 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
470 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
471 .finish();
472 func.build(block, opcode).uses(quotient, GPR).finish();
473 func.build(block, opcode).uses(dividend, GPR).finish();
474
475 assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx"]);
479 assert_eq!(operands(&func, divide), ["rax", "rax"]);
480 }
481
482 #[test]
483 fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
484 let mut names = Interner::new();
485 let mut func = Func::new(names.intern("f"));
486 let opcode = Opcode::new(names.intern("x64.nop"));
487 let block = func.create_block();
488 let left = func.new_vreg(GPR);
489 let right = func.new_vreg(GPR);
490 let sum = func.new_vreg(GPR);
491 func.build(block, opcode).def(left, GPR).finish();
492 func.build(block, opcode).def(right, GPR).finish();
493 let add = func
494 .build(block, opcode)
495 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
496 .uses(left, GPR)
497 .uses(right, GPR)
498 .finish();
499 func.build(block, opcode).uses(left, GPR).finish();
500
501 assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
504 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
505 }
506
507 #[test]
508 fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
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 left = func.new_vreg(GPR);
514 let right = func.new_vreg(GPR);
515 let sum = func.new_vreg(GPR);
516 func.build(block, opcode).def(left, GPR).finish();
517 func.build(block, opcode).def(right, GPR).finish();
518 let add = func
519 .build(block, opcode)
520 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
521 .uses(left, GPR)
522 .uses(right, GPR)
523 .finish();
524 func.build(block, opcode).uses(right, GPR).finish();
525
526 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
527 assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
528 }
529
530 #[test]
531 fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
532 let mut names = Interner::new();
533 let mut func = Func::new(names.intern("f"));
534 let opcode = Opcode::new(names.intern("x64.nop"));
535 let block = func.create_block();
536 let first = func.new_vreg(GPR);
537 let second = func.new_vreg(GPR);
538 func.build(block, opcode).def(first, GPR).finish();
539 func.build(block, opcode).def(second, GPR).finish();
540 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
541
542 assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
546 assert_eq!(operands(&func, read), ["rax", "rcx"]);
547 }
548
549 #[test]
556 fn a_two_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
557 let mut names = Interner::new();
558 let mut func = Func::new(names.intern("f"));
559 let opcode = Opcode::new(names.intern("x64.nop"));
560 let block = func.create_block();
561 let keeper = func.new_vreg(GPR);
562 let left = func.new_vreg(GPR);
563 let right = func.new_vreg(GPR);
564 let sum = func.new_vreg(GPR);
565 func.build(block, opcode).def(keeper, GPR).finish();
566 func.build(block, opcode)
567 .operand(Operand::write(left, GPR).with(Constraint::Stack))
568 .finish();
569 func.build(block, opcode)
570 .operand(Operand::write(right, GPR).with(Constraint::Stack))
571 .finish();
572 let add = func
573 .build(block, opcode)
574 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
575 .uses(left, GPR)
576 .uses(right, GPR)
577 .finish();
578 func.build(block, opcode).uses(keeper, GPR).finish();
579 func.build(block, opcode).uses(sum, GPR).finish();
580
581 assert_eq!(
585 run(&mut func, &narrow(1)),
586 [
587 "after 1: slot0 = rcx",
588 "after 2: slot1 = rcx",
589 "before 3: rcx = slot0",
590 "before 3: rdx = slot1",
591 "after 3: slot2 = rcx",
592 "before 5: rcx = slot2",
593 ]
594 );
595 assert_eq!(operands(&func, add), ["rcx", "rcx", "rdx"]);
596 }
597
598 #[test]
609 fn a_three_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
610 let mut names = Interner::new();
611 let mut func = Func::new(names.intern("f"));
612 let opcode = Opcode::new(names.intern("x64.nop"));
613 let block = func.create_block();
614 let keeper = func.new_vreg(GPR);
615 let base = func.new_vreg(GPR);
616 let index = func.new_vreg(GPR);
617 let address = func.new_vreg(GPR);
618 func.build(block, opcode).def(keeper, GPR).finish();
619 func.build(block, opcode)
620 .operand(Operand::write(base, GPR).with(Constraint::Stack))
621 .finish();
622 func.build(block, opcode)
623 .operand(Operand::write(index, GPR).with(Constraint::Stack))
624 .finish();
625 let lea =
626 func.build(block, opcode).def(address, GPR).uses(base, GPR).uses(index, GPR).finish();
627 func.build(block, opcode).uses(keeper, GPR).finish();
628 func.build(block, opcode).uses(address, GPR).finish();
629
630 assert_eq!(
633 run(&mut func, &narrow(1)),
634 [
635 "after 1: slot0 = rcx",
636 "after 2: slot1 = rcx",
637 "before 3: rcx = slot0",
638 "before 3: rdx = slot1",
639 "after 3: slot2 = rcx",
640 "before 5: rcx = slot2",
641 ]
642 );
643 assert_eq!(operands(&func, lea), ["rcx", "rcx", "rdx"]);
644 }
645
646 #[test]
653 fn a_spilled_answer_does_not_write_over_a_register_the_assignment_gave_to_something_else() {
654 let mut names = Interner::new();
655 let mut func = Func::new(names.intern("f"));
656 let opcode = Opcode::new(names.intern("x64.nop"));
657 let block = func.create_block();
658 let left = func.new_vreg(GPR);
659 let right = func.new_vreg(GPR);
660 let sum = func.new_vreg(GPR);
661 func.build(block, opcode).def(left, GPR).finish();
662 func.build(block, opcode)
663 .operand(Operand::write(right, GPR).with(Constraint::Stack))
664 .finish();
665 let add = func
666 .build(block, opcode)
667 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
668 .uses(left, GPR)
669 .uses(right, GPR)
670 .finish();
671 func.build(block, opcode).uses(left, GPR).finish();
672 func.build(block, opcode).uses(sum, GPR).finish();
673
674 assert_eq!(
677 run(&mut func, &narrow(1)),
678 [
679 "after 1: slot0 = rcx",
680 "before 2: rcx = slot0",
681 "before 2: rdx = rax",
682 "after 2: slot1 = rdx",
683 "before 4: rcx = slot1",
684 ]
685 );
686 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
687 }
688
689 #[test]
694 fn an_instruction_reading_out_of_two_files_takes_the_first_scratch_register_of_each() {
695 let mut names = Interner::new();
696 let mut func = Func::new(names.intern("f"));
697 let opcode = Opcode::new(names.intern("x64.nop"));
698 let block = func.create_block();
699 let integer = func.new_vreg(GPR);
700 let number = func.new_vreg(XMM);
701 let spare = func.new_vreg(GPR);
702 let other = func.new_vreg(XMM);
703 func.build(block, opcode).def(integer, GPR).finish();
704 func.build(block, opcode).def(number, XMM).finish();
705 func.build(block, opcode).def(spare, GPR).finish();
706 func.build(block, opcode).def(other, XMM).finish();
707 func.build(block, opcode).uses(integer, GPR).uses(number, XMM).finish();
708 let read = func.build(block, opcode).uses(spare, GPR).uses(other, XMM).finish();
709
710 let env = Env::new().with(GPR, &SYSV.int_order[..1], &SYSV.int_order[1..3]).with(
713 XMM,
714 &SYSV.sse_order[..1],
715 &SYSV.sse_order[1..3],
716 );
717 assert_eq!(
718 run(&mut func, &env),
719 [
720 "after 2: slot0 = rcx",
721 "after 3: slot1 = xmm1",
722 "before 5: rcx = slot0",
723 "before 5: xmm1 = slot1",
724 ]
725 );
726 assert_eq!(operands(&func, read), ["rcx", "xmm1"]);
727 }
728
729 #[test]
730 fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
731 let mut names = Interner::new();
732 let mut func = Func::new(names.intern("f"));
733 let opcode = Opcode::new(names.intern("x64.nop"));
734 let head = func.create_block();
735 let tail = func.create_block();
736 let held = func.new_vreg(GPR);
737 let carried = func.new_vreg(GPR);
738 func.build(head, opcode).def(held, GPR).finish();
739 func.build(head, opcode).def(carried, GPR).finish();
740 func.build(head, opcode).uses(held, GPR).finish();
741 let param = func.append_param(tail, GPR);
742 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
743 let read = func.build(tail, opcode).uses(param, GPR).finish();
744
745 assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
749 assert_eq!(operands(&func, read), ["rax"]);
750 assert!(func[tail].params.is_empty());
753 assert!(func[head].succs[0].args.is_empty());
754 }
755
756 #[test]
757 fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
758 let mut names = Interner::new();
759 let mut func = Func::new(names.intern("f"));
760 let opcode = Opcode::new(names.intern("x64.nop"));
761 let head = func.create_block();
762 let left = func.create_block();
763 let right = func.create_block();
764 let held = func.new_vreg(GPR);
765 let carried = func.new_vreg(GPR);
766 func.build(head, opcode).def(held, GPR).finish();
767 func.build(head, opcode).def(carried, GPR).finish();
768 func.build(head, opcode).uses(held, GPR).finish();
769 let taken = func.append_param(left, GPR);
770 *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
771 func.build(left, opcode).uses(taken, GPR).finish();
772
773 assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
777 }
778
779 #[test]
780 fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
781 let mut names = Interner::new();
782 let mut func = Func::new(names.intern("f"));
783 let opcode = Opcode::new(names.intern("x64.nop"));
784 let head = func.create_block();
785 let body = func.create_block();
786 let first = func.new_vreg(GPR);
787 let second = func.new_vreg(GPR);
788 func.build(head, opcode).def(first, GPR).finish();
789 func.build(head, opcode).def(second, GPR).finish();
790 let left = func.append_param(body, GPR);
791 let right = func.append_param(body, GPR);
792 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
793 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
794 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
795
796 assert_eq!(
800 run(&mut func, &env()),
801 ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
802 );
803 }
804
805 #[test]
806 fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
807 let mut names = Interner::new();
808 let mut func = Func::new(names.intern("f"));
809 let opcode = Opcode::new(names.intern("x64.nop"));
810 let head = func.create_block();
811 let body = func.create_block();
812 let first = func.new_vreg(GPR);
813 let second = func.new_vreg(GPR);
814 func.build(head, opcode).def(first, GPR).finish();
815 func.build(head, opcode).def(second, GPR).finish();
816 let left = func.append_param(body, GPR);
817 let right = func.append_param(body, GPR);
818 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
819 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
820
821 assert_eq!(
826 run(&mut func, &narrow(1)),
827 [
828 "after 1: slot0 = rcx",
829 "before 2: rcx = slot1",
830 "end of 0: rdx = slot0",
831 "end of 0: slot1 = rdx",
832 ]
833 );
834 }
835
836 #[test]
850 fn an_instruction_reading_three_spilled_values_borrows_a_register_for_the_third() {
851 let mut names = Interner::new();
852 let mut func = Func::new(names.intern("f"));
853 let opcode = Opcode::new(names.intern("x64.nop"));
854 let block = func.create_block();
855 let keeper = func.new_vreg(GPR);
856 let base = func.new_vreg(GPR);
857 let index = func.new_vreg(GPR);
858 let value = func.new_vreg(GPR);
859 func.build(block, opcode).def(keeper, GPR).finish();
860 for reg in [base, index, value] {
861 func.build(block, opcode)
862 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
863 .finish();
864 }
865 let store =
866 func.build(block, opcode).uses(base, GPR).uses(index, GPR).uses(value, GPR).finish();
867 func.build(block, opcode).uses(keeper, GPR).finish();
868
869 assert_eq!(
870 run(&mut func, &narrow(2)),
871 [
872 "after 1: slot0 = rdx",
873 "after 2: slot1 = rdx",
874 "after 3: slot2 = rdx",
875 "before 4: slot3 = rax",
876 "before 4: rdx = slot0",
877 "before 4: rsi = slot1",
878 "before 4: rax = slot2",
879 "after 4: rax = slot3",
880 ]
881 );
882 assert_eq!(operands(&func, store), ["rdx", "rsi", "rax"]);
883 }
884
885 #[test]
892 fn a_register_the_instruction_only_writes_still_carries_a_value_in() {
893 let mut names = Interner::new();
894 let mut func = Func::new(names.intern("f"));
895 let opcode = Opcode::new(names.intern("x64.nop"));
896 let block = func.create_block();
897 let target = func.new_vreg(GPR);
898 func.build(block, opcode)
899 .operand(Operand::write(target, GPR).with(Constraint::Stack))
900 .finish();
901 let call = func
902 .build(block, opcode)
903 .def(Reg::physical(RDX), GPR)
904 .def(Reg::physical(RSI), GPR)
905 .uses(target, GPR)
906 .finish();
907
908 assert_eq!(run(&mut func, &narrow(2)), ["after 0: slot0 = rdx", "before 1: rdx = slot0"]);
909 assert_eq!(operands(&func, call), ["rdx", "rsi", "rdx"]);
910 }
911
912 #[test]
919 fn a_register_the_instruction_already_named_is_not_handed_out_as_scratch() {
920 let mut names = Interner::new();
921 let mut func = Func::new(names.intern("f"));
922 let opcode = Opcode::new(names.intern("x64.nop"));
923 let block = func.create_block();
924 let wanted = func.new_vreg(GPR);
925 let other = func.new_vreg(GPR);
926 for reg in [wanted, other] {
927 func.build(block, opcode)
928 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
929 .finish();
930 }
931 let read = func
932 .build(block, opcode)
933 .operand(Operand::read(wanted, GPR).with(Constraint::Fixed(RCX)))
934 .uses(other, GPR)
935 .finish();
936
937 assert_eq!(
940 run(&mut func, &narrow(1)),
941 [
942 "after 0: slot0 = rcx",
943 "after 1: slot1 = rcx",
944 "before 2: rcx = slot0",
945 "before 2: rdx = slot1"
946 ]
947 );
948 assert_eq!(operands(&func, read), ["rcx", "rdx"]);
949 }
950
951 #[test]
952 #[should_panic(expected = "a critical edge has nowhere to put its moves")]
953 fn a_critical_edge_is_refused() {
954 let mut names = Interner::new();
955 let mut func = Func::new(names.intern("f"));
956 let opcode = Opcode::new(names.intern("x64.nop"));
957 let head = func.create_block();
958 let other = func.create_block();
959 let join = func.create_block();
960 let value = func.new_vreg(GPR);
961 func.build(head, opcode).def(value, GPR).finish();
962 let param = func.append_param(join, GPR);
963 *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
964 *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
965 func.build(join, opcode).uses(param, GPR).finish();
966
967 let _ = run(&mut func, &env());
968 }
969
970 #[test]
971 #[should_panic(expected = "what arrives in a function is not a block parameter")]
972 fn a_parameter_on_the_entry_block_is_refused() {
973 let mut names = Interner::new();
974 let mut func = Func::new(names.intern("f"));
975 let block = func.create_block();
976 let param = func.append_param(block, GPR);
977 let opcode = Opcode::new(names.intern("x64.nop"));
978 func.build(block, opcode).uses(param, GPR).finish();
979
980 let _ = run(&mut func, &env());
981 }
982
983 #[test]
984 fn a_value_already_in_a_register_is_left_where_it_is() {
985 let mut names = Interner::new();
986 let mut func = Func::new(names.intern("f"));
987 let opcode = Opcode::new(names.intern("x64.nop"));
988 let block = func.create_block();
989 let inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
990
991 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
992 assert_eq!(operands(&func, inst), ["rdx"]);
993 }
994}