1use std::collections::HashMap;
83
84use rucc_base::{Interner, Symbol};
85use rucc_mir::{self as mir, Role};
86use rucc_target::MachineInsts;
87
88#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct Plan {
95 pub opcode: mir::Opcode,
97 pub operands: Vec<mir::Operand>,
101 pub imm: Option<i64>,
103 pub amode: Option<mir::Amode>,
106 pub symbol: Option<Symbol>,
108}
109
110impl Plan {
111 #[must_use]
113 pub fn of(func: &mir::Func, inst: mir::Inst) -> Self {
114 let data = &func[inst];
115 Self {
116 opcode: data.opcode,
117 operands: func[data.operands].to_vec(),
118 imm: data.imm.map(|at| func[at].0),
119 amode: data.mem.map(|at| func[at]),
120 symbol: data.symbol,
121 }
122 }
123
124 fn reads(&self) -> impl Iterator<Item = mir::Reg> + use<'_> {
126 self.operands.iter().filter(|operand| operand.role == Role::Use).map(|operand| operand.reg)
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum Refusal {
137 Twice(mir::Inst),
139 Gone(mir::Inst),
142 Unknown(mir::Inst),
144 Operands(mir::Inst),
146 Imm(mir::Inst),
148 Mem(mir::Inst),
150 Scale(mir::Inst),
152 Read(mir::Inst),
154 Class(mir::Inst),
156 Edge(mir::Block, usize),
158 Args(mir::Block, usize),
160}
161
162#[derive(Debug, Clone, Default)]
168pub struct Reads {
169 counts: HashMap<mir::Reg, usize>,
170}
171
172impl Reads {
173 #[must_use]
176 pub fn of(func: &mir::Func) -> Self {
177 let mut counts: HashMap<mir::Reg, usize> = HashMap::new();
178 for block in func.blocks() {
179 for inst in func.insts(block) {
180 for operand in &func[func[inst].operands] {
181 if operand.role == Role::Use {
182 *counts.entry(operand.reg).or_insert(0) += 1;
183 }
184 }
185 }
186 for call in &func[block].succs {
187 for &arg in &call.args {
188 *counts.entry(arg).or_insert(0) += 1;
189 }
190 }
191 }
192 Self { counts }
193 }
194
195 #[must_use]
197 pub fn count(&self, reg: mir::Reg) -> usize {
198 self.counts.get(®).copied().unwrap_or(0)
199 }
200
201 fn gained(&mut self, reg: mir::Reg) {
203 *self.counts.entry(reg).or_insert(0) += 1;
204 }
205
206 fn lost(&mut self, reg: mir::Reg) {
208 if let Some(count) = self.counts.get_mut(®) {
209 *count = count.saturating_sub(1);
210 }
211 }
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
216enum What {
217 Rewrite(Plan),
219 Rename { from: mir::Reg, into: mir::Reg },
222 Remove,
224}
225
226#[derive(Debug, Clone, PartialEq, Eq)]
228struct Carried {
229 from: mir::Block,
231 at: usize,
233 args: Vec<mir::Reg>,
235}
236
237#[derive(Debug, Clone, Default)]
243pub struct Changes {
244 changes: Vec<(mir::Inst, What)>,
245 carries: Vec<Carried>,
246}
247
248impl Changes {
249 #[must_use]
251 pub fn new() -> Self {
252 Self::default()
253 }
254
255 #[must_use]
257 pub fn len(&self) -> usize {
258 self.changes.len() + self.carries.len()
259 }
260
261 #[must_use]
263 pub fn is_empty(&self) -> bool {
264 self.changes.is_empty() && self.carries.is_empty()
265 }
266
267 pub fn rewrite(&mut self, inst: mir::Inst, plan: Plan) {
269 self.changes.push((inst, What::Rewrite(plan)));
270 }
271
272 pub fn rename(&mut self, inst: mir::Inst, from: mir::Reg, into: mir::Reg) {
277 self.changes.push((inst, What::Rename { from, into }));
278 }
279
280 pub fn carry(&mut self, from: mir::Block, at: usize, args: Vec<mir::Reg>) {
285 self.carries.push(Carried { from, at, args });
286 }
287
288 pub fn remove(&mut self, inst: mir::Inst) {
290 self.changes.push((inst, What::Remove));
291 }
292
293 #[must_use]
299 pub fn refused(
300 &self,
301 func: &mir::Func,
302 reads: &Reads,
303 names: &Interner,
304 machine: &MachineInsts,
305 ) -> Option<Refusal> {
306 for (at, &(inst, _)) in self.changes.iter().enumerate() {
307 if self.changes[..at].iter().any(|&(other, _)| other == inst) {
308 return Some(Refusal::Twice(inst));
309 }
310 if func.block_of(inst).is_none() {
311 return Some(Refusal::Gone(inst));
312 }
313 }
314 for (at, carried) in self.carries.iter().enumerate() {
315 let edge = (carried.from, carried.at);
316 if self.carries[..at].iter().any(|other| (other.from, other.at) == edge) {
317 return Some(Refusal::Edge(carried.from, carried.at));
318 }
319 let Some(call) = func[carried.from].succs.get(carried.at) else {
320 return Some(Refusal::Edge(carried.from, carried.at));
321 };
322 if func[call.block].params.len() != carried.args.len() {
323 return Some(Refusal::Args(carried.from, carried.at));
324 }
325 }
326 for &(inst, ref what) in &self.changes {
327 match what {
328 What::Rewrite(plan) => {
329 if let Some(refusal) = shaped(inst, plan, names, machine) {
330 return Some(refusal);
331 }
332 }
333 What::Rename { from, into } => {
334 if let Some(refusal) = renamed(func, inst, *from, *into) {
335 return Some(refusal);
336 }
337 }
338 What::Remove => {
339 if self.read_after(func, reads, inst) {
340 return Some(Refusal::Read(inst));
341 }
342 }
343 }
344 }
345 None
346 }
347
348 pub fn commit(
356 self,
357 func: &mut mir::Func,
358 reads: &mut Reads,
359 names: &Interner,
360 machine: &MachineInsts,
361 ) -> Result<usize, Refusal> {
362 if let Some(refusal) = self.refused(func, reads, names, machine) {
363 return Err(refusal);
364 }
365 let touched = self.len();
366 for (inst, what) in self.changes {
367 let (lost, gained) = moved(func, inst, &what);
368 for reg in lost {
369 reads.lost(reg);
370 }
371 for reg in gained {
372 reads.gained(reg);
373 }
374 match what {
375 What::Rewrite(plan) => {
376 let operands = func.push_operands(&plan.operands);
377 let imm = plan.imm.map(|value| func.add_imm(value));
378 let mem = plan.amode.map(|amode| func.add_amode(amode));
379 let data = &mut func[inst];
380 data.opcode = plan.opcode;
381 data.operands = operands;
382 data.imm = imm;
383 data.mem = mem;
384 data.symbol = plan.symbol;
385 }
386 What::Rename { from, into } => {
387 let operands = func[inst].operands;
388 for operand in &mut func[operands] {
389 if operand.role == Role::Use && operand.reg == from {
390 operand.reg = into;
391 }
392 }
393 }
394 What::Remove => func.remove_inst(inst),
395 }
396 }
397 for carried in self.carries {
398 for &arg in &func[carried.from].succs[carried.at].args {
399 reads.lost(arg);
400 }
401 for &arg in &carried.args {
402 reads.gained(arg);
403 }
404 func.succs_mut(carried.from)[carried.at].args = carried.args;
405 }
406 Ok(touched)
407 }
408
409 fn read_after(&self, func: &mir::Func, reads: &Reads, inst: mir::Inst) -> bool {
416 func[func[inst].operands].iter().filter(|operand| operand.role.is_def()).any(|operand| {
417 let mut left = reads.count(operand.reg);
418 let mut settle = |lost: &[mir::Reg], gained: &[mir::Reg]| {
419 let goes = lost.iter().filter(|&®| reg == operand.reg).count();
420 left = left.saturating_sub(goes);
421 left += gained.iter().filter(|&®| reg == operand.reg).count();
422 };
423 for &(other, ref what) in &self.changes {
424 let (lost, gained) = moved(func, other, what);
425 settle(&lost, &gained);
426 }
427 for carried in &self.carries {
428 settle(&func[carried.from].succs[carried.at].args, &carried.args);
429 }
430 left != 0
431 })
432 }
433}
434
435fn moved(func: &mir::Func, inst: mir::Inst, what: &What) -> (Vec<mir::Reg>, Vec<mir::Reg>) {
442 let held: Vec<mir::Reg> = func[func[inst].operands]
443 .iter()
444 .filter(|operand| operand.role == Role::Use)
445 .map(|operand| operand.reg)
446 .collect();
447 match what {
448 What::Rewrite(plan) => (held, plan.reads().collect()),
449 What::Rename { from, into } => {
450 let gone: Vec<mir::Reg> = held.into_iter().filter(|reg| reg == from).collect();
451 let back = vec![*into; gone.len()];
452 (gone, back)
453 }
454 What::Remove => (held, Vec::new()),
455 }
456}
457
458fn renamed(func: &mir::Func, inst: mir::Inst, from: mir::Reg, into: mir::Reg) -> Option<Refusal> {
465 let class = func.class_of(into)?;
466 func[func[inst].operands]
467 .iter()
468 .any(|operand| operand.role == Role::Use && operand.reg == from && operand.class != class)
469 .then_some(Refusal::Class(inst))
470}
471
472fn shaped(
487 inst: mir::Inst,
488 plan: &Plan,
489 names: &Interner,
490 machine: &MachineInsts,
491) -> Option<Refusal> {
492 let name = names.resolve(plan.opcode.name());
493 let bare = machine.bare(name);
494 let Some(desc) = (machine.operands)(bare) else { return Some(Refusal::Unknown(inst)) };
495 if plan.operands.len() < desc.len() {
496 return Some(Refusal::Operands(inst));
497 }
498 let (described, addressed) = plan.operands.split_at(desc.len());
499 for (operand, want) in described.iter().zip(desc) {
500 let shape = (operand.class, operand.role, operand.constraint);
501 if shape != (want.class, want.role, want.constraint) {
502 return Some(Refusal::Operands(inst));
503 }
504 }
505 if plan.imm.is_some() != (machine.takes_imm)(bare) {
506 return Some(Refusal::Imm(inst));
507 }
508 let Some(amode) = plan.amode else {
509 return ((machine.takes_mem)(bare) || !addressed.is_empty()).then_some(Refusal::Mem(inst));
510 };
511 if !(machine.takes_mem)(bare) {
512 return Some(Refusal::Mem(inst));
513 }
514 let named = [amode.base, amode.index].into_iter().flatten();
515 let mut wanted = 0;
516 for at in named {
517 let Some(operand) = plan.operands.get(usize::from(at)) else {
518 return Some(Refusal::Operands(inst));
519 };
520 if usize::from(at) < desc.len() || operand.role != Role::Use {
521 return Some(Refusal::Operands(inst));
522 }
523 wanted += 1;
524 }
525 if addressed.len() != wanted {
526 return Some(Refusal::Operands(inst));
527 }
528 let scaled = if amode.index.is_some() { amode.scale } else { 1 };
529 if !machine.scales(scaled) || (amode.index.is_none() && amode.scale != 1) {
530 return Some(Refusal::Scale(inst));
531 }
532 None
533}
534
535#[cfg(test)]
536mod tests {
537 use rucc_mir::Constraint;
538 use rucc_target::x86_64::{GPR, MACHINE, XMM};
539
540 use super::*;
541
542 fn empty() -> (Interner, mir::Func, mir::Block) {
544 let mut names = Interner::new();
545 let mut func = mir::Func::new(names.intern("f"));
546 let block = func.create_block();
547 (names, func, block)
548 }
549
550 fn op(names: &mut Interner, name: &str) -> mir::Opcode {
552 mir::Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
553 }
554
555 fn refused(func: &mir::Func, names: &Interner, set: &Changes) -> Option<Refusal> {
557 set.refused(func, &Reads::of(func), names, &MACHINE)
558 }
559
560 fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
562 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
563 }
564
565 fn copy(
567 func: &mut mir::Func,
568 names: &mut Interner,
569 block: mir::Block,
570 ) -> (mir::Inst, mir::Reg) {
571 let from = func.new_vreg(GPR);
572 let into = func.new_vreg(GPR);
573 let mov = op(names, "mov_rr_64");
574 (func.build(block, mov).def(into, GPR).uses(from, GPR).finish(), into)
575 }
576
577 #[test]
580 fn a_rewrite_the_target_has_is_taken() {
581 let (mut names, mut func, block) = empty();
582 let (mov, into) = copy(&mut func, &mut names, block);
583 let base = func.new_vreg(GPR);
584 let load = op(&mut names, "mov_rm_64");
585 let mut set = Changes::new();
586 set.rewrite(
587 mov,
588 Plan {
589 opcode: load,
590 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
591 imm: None,
592 amode: Some(mir::Amode {
593 base: Some(1),
594 index: None,
595 scale: 1,
596 disp: 8,
597 symbol: None,
598 block: None,
599 reach: mir::Reach::Itself,
600 segment: None,
601 }),
602 symbol: None,
603 },
604 );
605
606 let mut reads = Reads::of(&func);
607 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
608
609 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
610 assert_eq!(func[func[mov].mem.expect("the load has an address")].disp, 8);
611 assert_eq!(reads.count(base), 1, "the address register is read now");
612 assert_eq!(reads.count(into), 0, "the register the copy read is read by nothing");
613 }
614
615 #[test]
618 fn an_opcode_this_target_does_not_have_is_refused() {
619 let (mut names, mut func, block) = empty();
620 let (mov, into) = copy(&mut func, &mut names, block);
621 let made_up = op(&mut names, "mov_rr_65");
622 let mut set = Changes::new();
623 set.rewrite(
624 mov,
625 Plan {
626 opcode: made_up,
627 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
628 imm: None,
629 amode: None,
630 symbol: None,
631 },
632 );
633
634 assert_eq!(refused(&func, &names, &set), Some(Refusal::Unknown(mov)));
635 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"], "the function was written to");
636 }
637
638 #[test]
642 fn an_operand_of_the_wrong_class_is_refused() {
643 let (mut names, mut func, block) = empty();
644 let (mov, into) = copy(&mut func, &mut names, block);
645 let float = func.new_vreg(XMM);
646 let same = func[mov].opcode;
647 let mut set = Changes::new();
648 set.rewrite(
649 mov,
650 Plan {
651 opcode: same,
652 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(float, XMM)],
653 imm: None,
654 amode: None,
655 symbol: None,
656 },
657 );
658
659 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
660 }
661
662 #[test]
666 fn an_add_whose_answer_is_not_tied_to_its_source_is_refused() {
667 let (mut names, mut func, block) = empty();
668 let (mov, into) = copy(&mut func, &mut names, block);
669 let other = func.new_vreg(GPR);
670 let add = op(&mut names, "add_rr_64");
671 let loose = vec![
672 mir::Operand::write(into, GPR),
673 mir::Operand::read(into, GPR),
674 mir::Operand::read(other, GPR),
675 ];
676 let mut set = Changes::new();
677 set.rewrite(
678 mov,
679 Plan { opcode: add, operands: loose.clone(), imm: None, amode: None, symbol: None },
680 );
681 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
682
683 let mut tied = loose;
684 tied[0] = tied[0].with(Constraint::Reuse(1));
685 let mut set = Changes::new();
686 set.rewrite(
687 mov,
688 Plan { opcode: add, operands: tied, imm: None, amode: None, symbol: None },
689 );
690 assert_eq!(refused(&func, &names, &set), None, "the same instruction written properly");
691 }
692
693 #[test]
696 fn an_immediate_has_to_be_there_exactly_when_the_instruction_carries_one() {
697 let (mut names, mut func, block) = empty();
698 let (mov, into) = copy(&mut func, &mut names, block);
699 let same = func[mov].opcode;
700 let add = op(&mut names, "add_ri_64");
701 let mut set = Changes::new();
702 set.rewrite(
703 mov,
704 Plan {
705 opcode: same,
706 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
707 imm: Some(7),
708 amode: None,
709 symbol: None,
710 },
711 );
712 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "a copy of seven");
713
714 let tied = vec![
715 mir::Operand::write(into, GPR).with(Constraint::Reuse(1)),
716 mir::Operand::read(into, GPR),
717 ];
718 let mut set = Changes::new();
719 set.rewrite(
720 mov,
721 Plan { opcode: add, operands: tied.clone(), imm: None, amode: None, symbol: None },
722 );
723 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "an add of nothing");
724
725 let mut set = Changes::new();
726 set.rewrite(
727 mov,
728 Plan { opcode: add, operands: tied, imm: Some(7), amode: None, symbol: None },
729 );
730 assert_eq!(refused(&func, &names, &set), None);
731 }
732
733 #[test]
736 fn an_address_on_an_instruction_that_has_none_is_refused() {
737 let (mut names, mut func, block) = empty();
738 let (mov, into) = copy(&mut func, &mut names, block);
739 let base = func.new_vreg(GPR);
740 let same = func[mov].opcode;
741 let mut set = Changes::new();
742 set.rewrite(
743 mov,
744 Plan {
745 opcode: same,
746 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
747 imm: None,
748 amode: Some(mir::Amode {
749 base: Some(1),
750 index: None,
751 scale: 1,
752 disp: 0,
753 symbol: None,
754 block: None,
755 reach: mir::Reach::Itself,
756 segment: None,
757 }),
758 symbol: None,
759 },
760 );
761
762 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
763 }
764
765 #[test]
767 fn an_instruction_that_wants_an_address_and_has_none_is_refused() {
768 let (mut names, mut func, block) = empty();
769 let (mov, into) = copy(&mut func, &mut names, block);
770 let load = op(&mut names, "mov_rm_64");
771 let mut set = Changes::new();
772 set.rewrite(
773 mov,
774 Plan {
775 opcode: load,
776 operands: vec![mir::Operand::write(into, GPR)],
777 imm: None,
778 amode: None,
779 symbol: None,
780 },
781 );
782
783 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
784 }
785
786 #[test]
790 fn an_index_scaled_by_something_this_machine_cannot_write_is_refused() {
791 let (mut names, mut func, block) = empty();
792 let (mov, into) = copy(&mut func, &mut names, block);
793 let base = func.new_vreg(GPR);
794 let index = func.new_vreg(GPR);
795 let load = op(&mut names, "mov_rm_64");
796 let scaled = |scale| Plan {
797 opcode: load,
798 operands: vec![
799 mir::Operand::write(into, GPR),
800 mir::Operand::read(base, GPR),
801 mir::Operand::read(index, GPR),
802 ],
803 imm: None,
804 amode: Some(mir::Amode {
805 base: Some(1),
806 index: Some(2),
807 scale,
808 disp: 0,
809 symbol: None,
810 block: None,
811 reach: mir::Reach::Itself,
812 segment: None,
813 }),
814 symbol: None,
815 };
816 let mut set = Changes::new();
817 set.rewrite(mov, scaled(3));
818 assert_eq!(refused(&func, &names, &set), Some(Refusal::Scale(mov)));
819
820 let mut set = Changes::new();
821 set.rewrite(mov, scaled(4));
822 assert_eq!(refused(&func, &names, &set), None);
823 }
824
825 #[test]
829 fn an_address_pointing_at_an_operand_of_its_own_instruction_is_refused() {
830 let (mut names, mut func, block) = empty();
831 let (mov, into) = copy(&mut func, &mut names, block);
832 let load = op(&mut names, "mov_rm_64");
833 let mut set = Changes::new();
834 set.rewrite(
835 mov,
836 Plan {
837 opcode: load,
838 operands: vec![mir::Operand::write(into, GPR)],
839 imm: None,
840 amode: Some(mir::Amode {
841 base: Some(0),
842 index: None,
843 scale: 1,
844 disp: 0,
845 symbol: None,
846 block: None,
847 reach: mir::Reach::Itself,
848 segment: None,
849 }),
850 symbol: None,
851 },
852 );
853
854 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
855 }
856
857 #[test]
861 fn removing_an_instruction_whose_answer_is_still_read_is_refused() {
862 let (mut names, mut func, block) = empty();
863 let (mov, into) = copy(&mut func, &mut names, block);
864 let out = func.new_vreg(GPR);
865 let second = func[mov].opcode;
866 func.build(block, second).def(out, GPR).uses(into, GPR).finish();
867 let mut set = Changes::new();
868 set.remove(mov);
869
870 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
871 assert_eq!(shape(&func, &names, block).len(), 2);
872 }
873
874 #[test]
878 fn removing_it_in_a_set_that_takes_away_the_reader_is_taken() {
879 let (mut names, mut func, block) = empty();
880 let base = func.new_vreg(GPR);
881 let address = func.new_vreg(GPR);
882 let out = func.new_vreg(GPR);
883 let lea = op(&mut names, "lea_64");
884 let load = op(&mut names, "mov_rm_64");
885 let at = |reg| mir::Mem { disp: 16, ..mir::Mem::at(mir::Operand::read(reg, GPR)) };
886 let made = func.build(block, lea).def(address, GPR).mem(at(base)).finish();
887 let read = func
888 .build(block, load)
889 .def(out, GPR)
890 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
891 .finish();
892
893 let mut set = Changes::new();
894 set.rewrite(
895 read,
896 Plan {
897 opcode: load,
898 operands: vec![mir::Operand::write(out, GPR), mir::Operand::read(base, GPR)],
899 imm: None,
900 amode: Some(mir::Amode { disp: 16, ..func[func[read].mem.expect("a load")] }),
901 symbol: None,
902 },
903 );
904 set.remove(made);
905
906 let mut reads = Reads::of(&func);
907 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
908
909 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
910 assert_eq!(reads.count(address), 0, "nothing reads the address the lea wrote");
911 assert_eq!(reads.count(base), 1, "the load reads what the lea read");
912 }
913
914 #[test]
917 fn a_rename_that_takes_the_last_reader_off_an_instruction_lets_it_go() {
918 let (mut names, mut func, block) = empty();
919 let (first, into) = copy(&mut func, &mut names, block);
920 let source = func[func[first].operands][1].reg;
921 let out = func.new_vreg(GPR);
922 let mov = func[first].opcode;
923 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
924
925 let mut set = Changes::new();
926 set.rename(second, into, source);
927 set.remove(first);
928 let mut reads = Reads::of(&func);
929 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
930
931 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
932 assert_eq!(func[func[second].operands][1].reg, source, "the reader was not sent on");
933 assert_eq!(reads.count(into), 0, "nothing reads what the copy wrote");
934 assert_eq!(reads.count(source), 1, "the reader reads what the copy read");
935 }
936
937 #[test]
939 fn a_removal_whose_reader_is_not_renamed_is_refused() {
940 let (mut names, mut func, block) = empty();
941 let (first, into) = copy(&mut func, &mut names, block);
942 let out = func.new_vreg(GPR);
943 let mov = func[first].opcode;
944 func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
945
946 let mut set = Changes::new();
947 set.remove(first);
948 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(first)));
949 }
950
951 #[test]
955 fn a_rename_into_a_register_of_another_class_is_refused() {
956 let (mut names, mut func, block) = empty();
957 let (mov, _) = copy(&mut func, &mut names, block);
958 let source = func[func[mov].operands][1].reg;
959 let float = func.new_vreg(XMM);
960
961 let mut set = Changes::new();
962 set.rename(mov, source, float);
963 assert_eq!(refused(&func, &names, &set), Some(Refusal::Class(mov)));
964 }
965
966 #[test]
969 fn an_edge_carries_what_the_set_says_and_the_instruction_it_read_goes() {
970 let (mut names, mut func, block) = empty();
971 let next = func.create_block();
972 let (mov, into) = copy(&mut func, &mut names, block);
973 let source = func[func[mov].operands][1].reg;
974 let arrived = func.new_vreg(GPR);
975 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
976 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
977
978 let mut set = Changes::new();
979 set.carry(block, 0, vec![source]);
980 set.remove(mov);
981 let mut reads = Reads::of(&func);
982 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
983
984 assert!(shape(&func, &names, block).is_empty(), "the copy is still there");
985 assert_eq!(func[block].succs[0].args, vec![source]);
986 assert_eq!(reads.count(into), 0);
987 assert_eq!(reads.count(source), 1, "the edge reads what the copy read");
988 }
989
990 #[test]
993 fn an_edge_that_is_not_there_is_refused() {
994 let (mut names, mut func, block) = empty();
995 copy(&mut func, &mut names, block);
996
997 let mut set = Changes::new();
998 set.carry(block, 0, Vec::new());
999 assert_eq!(refused(&func, &names, &set), Some(Refusal::Edge(block, 0)));
1000 }
1001
1002 #[test]
1006 fn an_edge_carrying_the_wrong_number_of_values_is_refused() {
1007 let (mut names, mut func, block) = empty();
1008 let next = func.create_block();
1009 let (_, into) = copy(&mut func, &mut names, block);
1010 let arrived = func.new_vreg(GPR);
1011 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1012 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1013
1014 let mut set = Changes::new();
1015 set.carry(block, 0, vec![into, into]);
1016 assert_eq!(refused(&func, &names, &set), Some(Refusal::Args(block, 0)));
1017 }
1018
1019 #[test]
1022 fn an_answer_an_edge_carries_keeps_its_instruction() {
1023 let (mut names, mut func, block) = empty();
1024 let next = func.create_block();
1025 let (mov, into) = copy(&mut func, &mut names, block);
1026 let arrived = func.new_vreg(GPR);
1027 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1028 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1029 let mut set = Changes::new();
1030 set.remove(mov);
1031
1032 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
1033 }
1034
1035 #[test]
1039 fn an_instruction_named_twice_is_refused() {
1040 let (mut names, mut func, block) = empty();
1041 let (mov, _) = copy(&mut func, &mut names, block);
1042 let mut set = Changes::new();
1043 set.rewrite(mov, Plan::of(&func, mov));
1044 set.remove(mov);
1045
1046 assert_eq!(refused(&func, &names, &set), Some(Refusal::Twice(mov)));
1047 }
1048
1049 #[test]
1052 fn an_instruction_that_has_already_gone_is_refused() {
1053 let (mut names, mut func, block) = empty();
1054 let (mov, _) = copy(&mut func, &mut names, block);
1055 let plan = Plan::of(&func, mov);
1056 func.remove_inst(mov);
1057 let mut set = Changes::new();
1058 set.rewrite(mov, plan);
1059
1060 assert_eq!(refused(&func, &names, &set), Some(Refusal::Gone(mov)));
1061 }
1062
1063 #[test]
1066 fn the_instruction_as_it_stands_is_a_proposal_the_target_takes() {
1067 let (mut names, mut func, block) = empty();
1068 let base = func.new_vreg(GPR);
1069 let out = func.new_vreg(GPR);
1070 let load = op(&mut names, "mov_rm_64");
1071 let read = func
1072 .build(block, load)
1073 .def(out, GPR)
1074 .mem(mir::Mem { disp: 24, ..mir::Mem::at(mir::Operand::read(base, GPR)) })
1075 .finish();
1076
1077 let plan = Plan::of(&func, read);
1078 let mut set = Changes::new();
1079 set.rewrite(read, plan.clone());
1080 let mut reads = Reads::of(&func);
1081 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1082
1083 assert_eq!(Plan::of(&func, read), plan);
1084 assert_eq!(reads.count(base), 1, "the one read it had before");
1085 }
1086
1087 #[test]
1090 fn a_set_with_one_bad_change_in_it_leaves_the_others_alone() {
1091 let (mut names, mut func, block) = empty();
1092 let (first, into) = copy(&mut func, &mut names, block);
1093 let (second, _) = copy(&mut func, &mut names, block);
1094 let load = op(&mut names, "mov_rm_64");
1095 let mut set = Changes::new();
1096 set.rewrite(
1097 first,
1098 Plan {
1099 opcode: load,
1100 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
1101 imm: None,
1102 amode: Some(mir::Amode {
1103 base: Some(1),
1104 index: None,
1105 scale: 1,
1106 disp: 0,
1107 symbol: None,
1108 block: None,
1109 reach: mir::Reach::Itself,
1110 segment: None,
1111 }),
1112 symbol: None,
1113 },
1114 );
1115 set.rewrite(second, Plan { imm: Some(3), ..Plan::of(&func, second) });
1116
1117 let mut reads = Reads::of(&func);
1118 assert_eq!(
1119 set.commit(&mut func, &mut reads, &names, &MACHINE),
1120 Err(Refusal::Imm(second)),
1121 "the second change is the one the target turns down"
1122 );
1123 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.mov_rr_64"]);
1124 }
1125
1126 #[test]
1128 fn a_set_with_nothing_in_it_commits() {
1129 let (mut names, mut func, block) = empty();
1130 copy(&mut func, &mut names, block);
1131 let set = Changes::new();
1132 assert!(set.is_empty());
1133
1134 let mut reads = Reads::of(&func);
1135 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(0));
1136 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1137 }
1138
1139 #[test]
1143 fn the_counts_are_still_right_after_a_commit() {
1144 let (mut names, mut func, block) = empty();
1145 let (first, into) = copy(&mut func, &mut names, block);
1146 let out = func.new_vreg(GPR);
1147 let mov = func[first].opcode;
1148 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1149
1150 let mut reads = Reads::of(&func);
1151 assert_eq!(reads.count(into), 1);
1152 let mut set = Changes::new();
1153 set.remove(second);
1154 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1155 assert_eq!(reads.count(into), 0, "the reader went and the count went with it");
1156
1157 let mut set = Changes::new();
1158 set.remove(first);
1159 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1160 assert!(shape(&func, &names, block).is_empty());
1161 }
1162}