1use rucc_mir::{Block, Func, Inst, Param, Reg};
46use rucc_target::RegClass;
47
48use crate::assign::{Assignment, Env, Place};
49use crate::legalize::{self, Spare, place};
50use crate::moves::{self, Move};
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct Edit {
55 pub at: At,
57 pub mov: Move<Place>,
59 pub class: RegClass,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub enum At {
66 Before(Inst),
68 After(Inst),
71 StartOf(Block),
73 EndOf(Block),
76}
77
78#[must_use]
93pub fn rewrite(func: &mut Func, assignment: &mut Assignment, env: &Env) -> Vec<Edit> {
94 let blocks: Vec<Block> = func.blocks().collect();
95 assert!(
96 func.entry().is_none_or(|entry| func[entry].params.is_empty()),
97 "what arrives in a function is not a block parameter"
98 );
99
100 let mut edits = Vec::new();
101 let mut spare = Spare::default();
102 let insts: Vec<Inst> = blocks.iter().flat_map(|&block| func.insts(block)).collect();
105 for inst in insts {
106 instruction(func, assignment, env, &mut spare, inst, &mut edits);
107 }
108
109 let preds = preds(func, &blocks);
110 for &block in &blocks {
111 behind_the_end(func, block, &preds, &mut edits);
112 }
113 for &block in &blocks {
114 edges(func, assignment, env, block, &preds, &mut edits);
115 }
116 for &block in &blocks {
117 func.params_mut(block).clear();
118 for call in func.succs_mut(block) {
119 call.args.clear();
120 }
121 }
122 edits
123}
124
125fn instruction(
127 func: &mut Func,
128 assignment: &mut Assignment,
129 env: &Env,
130 spare: &mut Spare,
131 inst: Inst,
132 edits: &mut Vec<Edit>,
133) {
134 let legal = legalize::instruction(func, assignment, env, spare, inst);
135 let list = func[inst].operands;
136 func[list].copy_from_slice(&legal.operands);
137 edits.extend(legal.before.into_iter().map(|(mov, class)| Edit {
138 at: At::Before(inst),
139 mov,
140 class,
141 }));
142 edits.extend(legal.after.into_iter().map(|(mov, class)| Edit {
143 at: At::After(inst),
144 mov,
145 class,
146 }));
147}
148
149fn behind_the_end(func: &Func, block: Block, preds: &[usize], edits: &mut Vec<Edit>) {
162 let succs = &func[block].succs;
163 if succs.len() < 2 {
164 return;
165 }
166 let Some(last) = func.insts(block).last() else { return };
167 if !func[func[last].operands].iter().any(|operand| operand.role.is_def()) {
168 return;
169 }
170 let behind: Vec<Edit> =
171 edits.iter().filter(|edit| edit.at == At::After(last)).copied().collect();
172 if behind.is_empty() {
173 return;
174 }
175 edits.retain(|edit| edit.at != At::After(last));
176 for call in succs {
177 assert!(
178 preds[call.block.index()] == 1,
179 "an edge out of an asm goto that writes something has to be split before allocation"
180 );
181 edits.extend(behind.iter().map(|&edit| Edit { at: At::StartOf(call.block), ..edit }));
182 }
183}
184
185fn edges(
187 func: &mut Func,
188 assignment: &Assignment,
189 env: &Env,
190 block: Block,
191 preds: &[usize],
192 edits: &mut Vec<Edit>,
193) {
194 let succs = func[block].succs.clone();
195 let single = succs.len() == 1;
196 for call in &succs {
197 let params = func[call.block].params.clone();
198 assert_eq!(
199 params.len(),
200 call.args.len(),
201 "an edge carries what the block it goes to asks for"
202 );
203 if params.is_empty() {
204 continue;
205 }
206 assert!(
207 single || preds[call.block.index()] == 1,
208 "a critical edge has nowhere to put its moves and has to be split before allocation"
209 );
210 let at = if single { At::EndOf(block) } else { At::StartOf(call.block) };
211 edits.extend(edge(assignment, env, ¶ms, &call.args, at));
212 }
213}
214
215fn edge(assignment: &Assignment, env: &Env, params: &[Param], args: &[Reg], at: At) -> Vec<Edit> {
217 let mut classes: Vec<RegClass> = params.iter().map(|param| param.class).collect();
218 classes.sort_unstable();
219 classes.dedup();
220
221 let mut edits = Vec::new();
222 for class in classes {
223 let parallel: Vec<Move<Place>> = params
226 .iter()
227 .zip(args)
228 .filter(|(param, _)| param.class == class)
229 .map(|(param, &arg)| Move::new(place(assignment, param.reg), place(assignment, arg)))
230 .collect();
231 let scratch = env.scratch(class);
232 let cycle = *scratch
233 .first()
234 .expect("a class whose values are passed on an edge and which has no scratch register");
235 for mov in moves::sequence(¶llel, Place::Reg(cycle)) {
236 match (mov.to, mov.from) {
237 (Place::Slot(_), Place::Slot(_)) => {
241 let through = Place::Reg(*scratch.get(1).expect(
242 "a class passing a spilled value to a spilled parameter and having only \
243 one scratch register",
244 ));
245 edits.push(Edit { at, mov: Move::new(through, mov.from), class });
246 edits.push(Edit { at, mov: Move::new(mov.to, through), class });
247 }
248 _ => edits.push(Edit { at, mov, class }),
249 }
250 }
251 }
252 edits
253}
254
255fn preds(func: &Func, blocks: &[Block]) -> Vec<usize> {
257 let mut preds = vec![0; func.block_count()];
258 for &block in blocks {
259 for call in &func[block].succs {
260 preds[call.block.index()] += 1;
261 }
262 }
263 preds
264}
265
266#[cfg(test)]
267mod tests {
268 use rucc_base::Interner;
269 use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
270 use rucc_target::x86_64::{GPR, RAX, RCX, RDX, REGS, RSI, SYSV, XMM};
271
272 use super::*;
273 use crate::assign::assign;
274 use crate::legalize::phys;
275 use crate::live::Live;
276 use crate::order::Order;
277
278 fn env() -> Env {
280 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
281 Env::new().with(GPR, order, scratch)
282 }
283
284 fn narrow(count: usize) -> Env {
286 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 2])
287 }
288
289 fn named(class: RegClass, place: Place) -> String {
294 match place {
295 Place::Reg(reg) => REGS.name(class, reg).expect("a register").to_string(),
296 Place::Slot(slot) => format!("slot{slot}"),
297 }
298 }
299
300 fn run(func: &mut Func, env: &Env) -> Vec<String> {
302 let order = Order::of(func);
303 let live = Live::of(func, &order);
304 let mut assignment = assign(func, &order, &live, env);
305 rewrite(func, &mut assignment, env)
306 .into_iter()
307 .map(|edit| {
308 let at = match edit.at {
309 At::Before(inst) => format!("before {}", inst.index()),
310 At::After(inst) => format!("after {}", inst.index()),
311 At::StartOf(block) => format!("start of {}", block.index()),
312 At::EndOf(block) => format!("end of {}", block.index()),
313 };
314 format!(
315 "{at}: {} = {}",
316 named(edit.class, edit.mov.to),
317 named(edit.class, edit.mov.from)
318 )
319 })
320 .collect()
321 }
322
323 fn operands(func: &Func, inst: Inst) -> Vec<String> {
325 func[func[inst].operands]
326 .iter()
327 .map(|operand| named(operand.class, Place::Reg(phys(operand.reg))))
328 .collect()
329 }
330
331 #[test]
332 fn every_operand_ends_up_naming_the_register_its_value_was_given() {
333 let mut names = Interner::new();
334 let mut func = Func::new(names.intern("f"));
335 let opcode = Opcode::new(names.intern("x64.nop"));
336 let block = func.create_block();
337 let first = func.new_vreg(GPR);
338 let second = func.new_vreg(GPR);
339 func.build(block, opcode).def(first, GPR).finish();
340 func.build(block, opcode).def(second, GPR).finish();
341 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
342
343 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
344 assert_eq!(operands(&func, read), ["rax", "rcx"]);
345 }
346
347 #[test]
348 fn a_register_an_instruction_insists_on_costs_nothing_when_the_values_can_have_it() {
349 let mut names = Interner::new();
350 let mut func = Func::new(names.intern("f"));
351 let opcode = Opcode::new(names.intern("x64.nop"));
352 let block = func.create_block();
353 let dividend = func.new_vreg(GPR);
354 let quotient = func.new_vreg(GPR);
355 func.build(block, opcode).def(dividend, GPR).finish();
356 let divide = func
357 .build(block, opcode)
358 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
359 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
360 .finish();
361 func.build(block, opcode).uses(quotient, GPR).finish();
362
363 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
367 assert_eq!(operands(&func, divide), ["rax", "rax"]);
368 }
369
370 #[test]
371 fn a_register_an_instruction_insists_on_is_moved_into_when_the_value_cannot_have_it() {
372 let mut names = Interner::new();
373 let mut func = Func::new(names.intern("f"));
374 let opcode = Opcode::new(names.intern("x64.nop"));
375 let block = func.create_block();
376 let dividend = func.new_vreg(GPR);
377 let quotient = func.new_vreg(GPR);
378 func.build(block, opcode).def(dividend, GPR).finish();
379 let divide = func
380 .build(block, opcode)
381 .operand(Operand::write(quotient, GPR).with(Constraint::Fixed(RAX)))
382 .operand(Operand::read(dividend, GPR).with(Constraint::Fixed(RAX)))
383 .finish();
384 func.build(block, opcode).uses(quotient, GPR).finish();
385 func.build(block, opcode).uses(dividend, GPR).finish();
386
387 assert_eq!(run(&mut func, &env()), ["before 1: rax = rcx"]);
391 assert_eq!(operands(&func, divide), ["rax", "rax"]);
392 }
393
394 #[test]
395 fn a_two_address_instruction_that_did_not_get_its_register_copies_first() {
396 let mut names = Interner::new();
397 let mut func = Func::new(names.intern("f"));
398 let opcode = Opcode::new(names.intern("x64.nop"));
399 let block = func.create_block();
400 let left = func.new_vreg(GPR);
401 let right = func.new_vreg(GPR);
402 let sum = func.new_vreg(GPR);
403 func.build(block, opcode).def(left, GPR).finish();
404 func.build(block, opcode).def(right, GPR).finish();
405 let add = func
406 .build(block, opcode)
407 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
408 .uses(left, GPR)
409 .uses(right, GPR)
410 .finish();
411 func.build(block, opcode).uses(left, GPR).finish();
412
413 assert_eq!(run(&mut func, &env()), ["before 2: rdx = rax"]);
416 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
417 }
418
419 #[test]
420 fn a_two_address_instruction_that_did_get_its_register_copies_nothing() {
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 left = func.new_vreg(GPR);
426 let right = func.new_vreg(GPR);
427 let sum = func.new_vreg(GPR);
428 func.build(block, opcode).def(left, GPR).finish();
429 func.build(block, opcode).def(right, GPR).finish();
430 let add = func
431 .build(block, opcode)
432 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
433 .uses(left, GPR)
434 .uses(right, GPR)
435 .finish();
436 func.build(block, opcode).uses(right, GPR).finish();
437
438 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
439 assert_eq!(operands(&func, add), ["rax", "rax", "rcx"]);
440 }
441
442 #[test]
443 fn a_spilled_value_is_read_into_a_scratch_register_at_each_instruction_that_wants_it() {
444 let mut names = Interner::new();
445 let mut func = Func::new(names.intern("f"));
446 let opcode = Opcode::new(names.intern("x64.nop"));
447 let block = func.create_block();
448 let first = func.new_vreg(GPR);
449 let second = func.new_vreg(GPR);
450 func.build(block, opcode).def(first, GPR).finish();
451 func.build(block, opcode).def(second, GPR).finish();
452 let read = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
453
454 assert_eq!(run(&mut func, &narrow(1)), ["after 1: slot0 = rcx", "before 2: rcx = slot0"]);
458 assert_eq!(operands(&func, read), ["rax", "rcx"]);
459 }
460
461 #[test]
468 fn a_two_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
469 let mut names = Interner::new();
470 let mut func = Func::new(names.intern("f"));
471 let opcode = Opcode::new(names.intern("x64.nop"));
472 let block = func.create_block();
473 let keeper = func.new_vreg(GPR);
474 let left = func.new_vreg(GPR);
475 let right = func.new_vreg(GPR);
476 let sum = func.new_vreg(GPR);
477 func.build(block, opcode).def(keeper, GPR).finish();
478 func.build(block, opcode)
479 .operand(Operand::write(left, GPR).with(Constraint::Stack))
480 .finish();
481 func.build(block, opcode)
482 .operand(Operand::write(right, GPR).with(Constraint::Stack))
483 .finish();
484 let add = func
485 .build(block, opcode)
486 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
487 .uses(left, GPR)
488 .uses(right, GPR)
489 .finish();
490 func.build(block, opcode).uses(keeper, GPR).finish();
491 func.build(block, opcode).uses(sum, GPR).finish();
492
493 assert_eq!(
497 run(&mut func, &narrow(1)),
498 [
499 "after 1: slot0 = rcx",
500 "after 2: slot1 = rcx",
501 "before 3: rcx = slot0",
502 "before 3: rdx = slot1",
503 "after 3: slot2 = rcx",
504 "before 5: rcx = slot2",
505 ]
506 );
507 assert_eq!(operands(&func, add), ["rcx", "rcx", "rdx"]);
508 }
509
510 #[test]
521 fn a_three_address_instruction_whose_answer_and_operands_are_all_spilled_wants_two_registers() {
522 let mut names = Interner::new();
523 let mut func = Func::new(names.intern("f"));
524 let opcode = Opcode::new(names.intern("x64.nop"));
525 let block = func.create_block();
526 let keeper = func.new_vreg(GPR);
527 let base = func.new_vreg(GPR);
528 let index = func.new_vreg(GPR);
529 let address = func.new_vreg(GPR);
530 func.build(block, opcode).def(keeper, GPR).finish();
531 func.build(block, opcode)
532 .operand(Operand::write(base, GPR).with(Constraint::Stack))
533 .finish();
534 func.build(block, opcode)
535 .operand(Operand::write(index, GPR).with(Constraint::Stack))
536 .finish();
537 let lea =
538 func.build(block, opcode).def(address, GPR).uses(base, GPR).uses(index, GPR).finish();
539 func.build(block, opcode).uses(keeper, GPR).finish();
540 func.build(block, opcode).uses(address, GPR).finish();
541
542 assert_eq!(
545 run(&mut func, &narrow(1)),
546 [
547 "after 1: slot0 = rcx",
548 "after 2: slot1 = rcx",
549 "before 3: rcx = slot0",
550 "before 3: rdx = slot1",
551 "after 3: slot2 = rcx",
552 "before 5: rcx = slot2",
553 ]
554 );
555 assert_eq!(operands(&func, lea), ["rcx", "rcx", "rdx"]);
556 }
557
558 #[test]
565 fn a_spilled_answer_does_not_write_over_a_register_the_assignment_gave_to_something_else() {
566 let mut names = Interner::new();
567 let mut func = Func::new(names.intern("f"));
568 let opcode = Opcode::new(names.intern("x64.nop"));
569 let block = func.create_block();
570 let left = func.new_vreg(GPR);
571 let right = func.new_vreg(GPR);
572 let sum = func.new_vreg(GPR);
573 func.build(block, opcode).def(left, GPR).finish();
574 func.build(block, opcode)
575 .operand(Operand::write(right, GPR).with(Constraint::Stack))
576 .finish();
577 let add = func
578 .build(block, opcode)
579 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
580 .uses(left, GPR)
581 .uses(right, GPR)
582 .finish();
583 func.build(block, opcode).uses(left, GPR).finish();
584 func.build(block, opcode).uses(sum, GPR).finish();
585
586 assert_eq!(
589 run(&mut func, &narrow(1)),
590 [
591 "after 1: slot0 = rcx",
592 "before 2: rcx = slot0",
593 "before 2: rdx = rax",
594 "after 2: slot1 = rdx",
595 "before 4: rcx = slot1",
596 ]
597 );
598 assert_eq!(operands(&func, add), ["rdx", "rax", "rcx"]);
599 }
600
601 #[test]
606 fn an_instruction_reading_out_of_two_files_takes_the_first_scratch_register_of_each() {
607 let mut names = Interner::new();
608 let mut func = Func::new(names.intern("f"));
609 let opcode = Opcode::new(names.intern("x64.nop"));
610 let block = func.create_block();
611 let integer = func.new_vreg(GPR);
612 let number = func.new_vreg(XMM);
613 let spare = func.new_vreg(GPR);
614 let other = func.new_vreg(XMM);
615 func.build(block, opcode).def(integer, GPR).finish();
616 func.build(block, opcode).def(number, XMM).finish();
617 func.build(block, opcode).def(spare, GPR).finish();
618 func.build(block, opcode).def(other, XMM).finish();
619 func.build(block, opcode).uses(integer, GPR).uses(number, XMM).finish();
620 let read = func.build(block, opcode).uses(spare, GPR).uses(other, XMM).finish();
621
622 let env = Env::new().with(GPR, &SYSV.int_order[..1], &SYSV.int_order[1..3]).with(
625 XMM,
626 &SYSV.sse_order[..1],
627 &SYSV.sse_order[1..3],
628 );
629 assert_eq!(
630 run(&mut func, &env),
631 [
632 "after 2: slot0 = rcx",
633 "after 3: slot1 = xmm1",
634 "before 5: rcx = slot0",
635 "before 5: xmm1 = slot1",
636 ]
637 );
638 assert_eq!(operands(&func, read), ["rcx", "xmm1"]);
639 }
640
641 #[test]
642 fn an_edge_out_of_a_block_with_one_way_to_go_moves_at_the_end_of_it() {
643 let mut names = Interner::new();
644 let mut func = Func::new(names.intern("f"));
645 let opcode = Opcode::new(names.intern("x64.nop"));
646 let head = func.create_block();
647 let tail = func.create_block();
648 let held = func.new_vreg(GPR);
649 let carried = func.new_vreg(GPR);
650 func.build(head, opcode).def(held, GPR).finish();
651 func.build(head, opcode).def(carried, GPR).finish();
652 func.build(head, opcode).uses(held, GPR).finish();
653 let param = func.append_param(tail, GPR);
654 *func.succs_mut(head) = vec![BlockCall::with(tail, vec![carried])];
655 let read = func.build(tail, opcode).uses(param, GPR).finish();
656
657 assert_eq!(run(&mut func, &env()), ["end of 0: rax = rcx"]);
661 assert_eq!(operands(&func, read), ["rax"]);
662 assert!(func[tail].params.is_empty());
665 assert!(func[head].succs[0].args.is_empty());
666 }
667
668 #[test]
669 fn an_edge_out_of_a_block_with_a_choice_moves_at_the_start_of_where_it_goes() {
670 let mut names = Interner::new();
671 let mut func = Func::new(names.intern("f"));
672 let opcode = Opcode::new(names.intern("x64.nop"));
673 let head = func.create_block();
674 let left = func.create_block();
675 let right = func.create_block();
676 let held = func.new_vreg(GPR);
677 let carried = func.new_vreg(GPR);
678 func.build(head, opcode).def(held, GPR).finish();
679 func.build(head, opcode).def(carried, GPR).finish();
680 func.build(head, opcode).uses(held, GPR).finish();
681 let taken = func.append_param(left, GPR);
682 *func.succs_mut(head) = vec![BlockCall::with(left, vec![carried]), BlockCall::to(right)];
683 func.build(left, opcode).uses(taken, GPR).finish();
684
685 assert_eq!(run(&mut func, &env()), ["start of 1: rax = rcx"]);
689 }
690
691 #[test]
692 fn two_values_that_swap_on_an_edge_get_an_order_and_a_scratch_register() {
693 let mut names = Interner::new();
694 let mut func = Func::new(names.intern("f"));
695 let opcode = Opcode::new(names.intern("x64.nop"));
696 let head = func.create_block();
697 let body = func.create_block();
698 let first = func.new_vreg(GPR);
699 let second = func.new_vreg(GPR);
700 func.build(head, opcode).def(first, GPR).finish();
701 func.build(head, opcode).def(second, GPR).finish();
702 let left = func.append_param(body, GPR);
703 let right = func.append_param(body, GPR);
704 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
705 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
706 *func.succs_mut(body) = vec![BlockCall::with(body, vec![right, left])];
707
708 assert_eq!(
712 run(&mut func, &env()),
713 ["end of 1: r13 = rcx", "end of 1: rcx = rax", "end of 1: rax = r13"]
714 );
715 }
716
717 #[test]
718 fn a_spilled_value_handed_to_a_spilled_parameter_goes_through_a_register() {
719 let mut names = Interner::new();
720 let mut func = Func::new(names.intern("f"));
721 let opcode = Opcode::new(names.intern("x64.nop"));
722 let head = func.create_block();
723 let body = func.create_block();
724 let first = func.new_vreg(GPR);
725 let second = func.new_vreg(GPR);
726 func.build(head, opcode).def(first, GPR).finish();
727 func.build(head, opcode).def(second, GPR).finish();
728 let left = func.append_param(body, GPR);
729 let right = func.append_param(body, GPR);
730 *func.succs_mut(head) = vec![BlockCall::with(body, vec![first, second])];
731 func.build(body, opcode).uses(left, GPR).uses(right, GPR).finish();
732
733 assert_eq!(
738 run(&mut func, &narrow(1)),
739 [
740 "after 1: slot0 = rcx",
741 "before 2: rcx = slot1",
742 "end of 0: rdx = slot0",
743 "end of 0: slot1 = rdx",
744 ]
745 );
746 }
747
748 #[test]
762 fn an_instruction_reading_three_spilled_values_borrows_a_register_for_the_third() {
763 let mut names = Interner::new();
764 let mut func = Func::new(names.intern("f"));
765 let opcode = Opcode::new(names.intern("x64.nop"));
766 let block = func.create_block();
767 let keeper = func.new_vreg(GPR);
768 let base = func.new_vreg(GPR);
769 let index = func.new_vreg(GPR);
770 let value = func.new_vreg(GPR);
771 func.build(block, opcode).def(keeper, GPR).finish();
772 for reg in [base, index, value] {
773 func.build(block, opcode)
774 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
775 .finish();
776 }
777 let store =
778 func.build(block, opcode).uses(base, GPR).uses(index, GPR).uses(value, GPR).finish();
779 func.build(block, opcode).uses(keeper, GPR).finish();
780
781 assert_eq!(
782 run(&mut func, &narrow(2)),
783 [
784 "after 1: slot0 = rdx",
785 "after 2: slot1 = rdx",
786 "after 3: slot2 = rdx",
787 "before 4: slot3 = rax",
788 "before 4: rdx = slot0",
789 "before 4: rsi = slot1",
790 "before 4: rax = slot2",
791 "after 4: rax = slot3",
792 ]
793 );
794 assert_eq!(operands(&func, store), ["rdx", "rsi", "rax"]);
795 }
796
797 #[test]
804 fn a_register_the_instruction_only_writes_still_carries_a_value_in() {
805 let mut names = Interner::new();
806 let mut func = Func::new(names.intern("f"));
807 let opcode = Opcode::new(names.intern("x64.nop"));
808 let block = func.create_block();
809 let target = func.new_vreg(GPR);
810 func.build(block, opcode)
811 .operand(Operand::write(target, GPR).with(Constraint::Stack))
812 .finish();
813 let call = func
814 .build(block, opcode)
815 .def(Reg::physical(RDX), GPR)
816 .def(Reg::physical(RSI), GPR)
817 .uses(target, GPR)
818 .finish();
819
820 assert_eq!(run(&mut func, &narrow(2)), ["after 0: slot0 = rdx", "before 1: rdx = slot0"]);
821 assert_eq!(operands(&func, call), ["rdx", "rsi", "rdx"]);
822 }
823
824 #[test]
831 fn a_register_the_instruction_already_named_is_not_handed_out_as_scratch() {
832 let mut names = Interner::new();
833 let mut func = Func::new(names.intern("f"));
834 let opcode = Opcode::new(names.intern("x64.nop"));
835 let block = func.create_block();
836 let wanted = func.new_vreg(GPR);
837 let other = func.new_vreg(GPR);
838 for reg in [wanted, other] {
839 func.build(block, opcode)
840 .operand(Operand::write(reg, GPR).with(Constraint::Stack))
841 .finish();
842 }
843 let read = func
844 .build(block, opcode)
845 .operand(Operand::read(wanted, GPR).with(Constraint::Fixed(RCX)))
846 .uses(other, GPR)
847 .finish();
848
849 assert_eq!(
852 run(&mut func, &narrow(1)),
853 [
854 "after 0: slot0 = rcx",
855 "after 1: slot1 = rcx",
856 "before 2: rcx = slot0",
857 "before 2: rdx = slot1"
858 ]
859 );
860 assert_eq!(operands(&func, read), ["rcx", "rdx"]);
861 }
862
863 #[test]
864 #[should_panic(expected = "a critical edge has nowhere to put its moves")]
865 fn a_critical_edge_is_refused() {
866 let mut names = Interner::new();
867 let mut func = Func::new(names.intern("f"));
868 let opcode = Opcode::new(names.intern("x64.nop"));
869 let head = func.create_block();
870 let other = func.create_block();
871 let join = func.create_block();
872 let value = func.new_vreg(GPR);
873 func.build(head, opcode).def(value, GPR).finish();
874 let param = func.append_param(join, GPR);
875 *func.succs_mut(head) = vec![BlockCall::with(join, vec![value]), BlockCall::to(other)];
876 *func.succs_mut(other) = vec![BlockCall::with(join, vec![value])];
877 func.build(join, opcode).uses(param, GPR).finish();
878
879 let _ = run(&mut func, &env());
880 }
881
882 #[test]
883 #[should_panic(expected = "what arrives in a function is not a block parameter")]
884 fn a_parameter_on_the_entry_block_is_refused() {
885 let mut names = Interner::new();
886 let mut func = Func::new(names.intern("f"));
887 let block = func.create_block();
888 let param = func.append_param(block, GPR);
889 let opcode = Opcode::new(names.intern("x64.nop"));
890 func.build(block, opcode).uses(param, GPR).finish();
891
892 let _ = run(&mut func, &env());
893 }
894
895 #[test]
896 fn a_value_already_in_a_register_is_left_where_it_is() {
897 let mut names = Interner::new();
898 let mut func = Func::new(names.intern("f"));
899 let opcode = Opcode::new(names.intern("x64.nop"));
900 let block = func.create_block();
901 let inst = func.build(block, opcode).uses(Reg::physical(RDX), GPR).finish();
902
903 assert_eq!(run(&mut func, &env()), Vec::<String>::new());
904 assert_eq!(operands(&func, inst), ["rdx"]);
905 }
906}