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 table: None,
600 reach: mir::Reach::Itself,
601 segment: None,
602 }),
603 symbol: None,
604 },
605 );
606
607 let mut reads = Reads::of(&func);
608 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
609
610 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
611 assert_eq!(func[func[mov].mem.expect("the load has an address")].disp, 8);
612 assert_eq!(reads.count(base), 1, "the address register is read now");
613 assert_eq!(reads.count(into), 0, "the register the copy read is read by nothing");
614 }
615
616 #[test]
619 fn an_opcode_this_target_does_not_have_is_refused() {
620 let (mut names, mut func, block) = empty();
621 let (mov, into) = copy(&mut func, &mut names, block);
622 let made_up = op(&mut names, "mov_rr_65");
623 let mut set = Changes::new();
624 set.rewrite(
625 mov,
626 Plan {
627 opcode: made_up,
628 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
629 imm: None,
630 amode: None,
631 symbol: None,
632 },
633 );
634
635 assert_eq!(refused(&func, &names, &set), Some(Refusal::Unknown(mov)));
636 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"], "the function was written to");
637 }
638
639 #[test]
643 fn an_operand_of_the_wrong_class_is_refused() {
644 let (mut names, mut func, block) = empty();
645 let (mov, into) = copy(&mut func, &mut names, block);
646 let float = func.new_vreg(XMM);
647 let same = func[mov].opcode;
648 let mut set = Changes::new();
649 set.rewrite(
650 mov,
651 Plan {
652 opcode: same,
653 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(float, XMM)],
654 imm: None,
655 amode: None,
656 symbol: None,
657 },
658 );
659
660 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
661 }
662
663 #[test]
667 fn an_add_whose_answer_is_not_tied_to_its_source_is_refused() {
668 let (mut names, mut func, block) = empty();
669 let (mov, into) = copy(&mut func, &mut names, block);
670 let other = func.new_vreg(GPR);
671 let add = op(&mut names, "add_rr_64");
672 let loose = vec![
673 mir::Operand::write(into, GPR),
674 mir::Operand::read(into, GPR),
675 mir::Operand::read(other, GPR),
676 ];
677 let mut set = Changes::new();
678 set.rewrite(
679 mov,
680 Plan { opcode: add, operands: loose.clone(), imm: None, amode: None, symbol: None },
681 );
682 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
683
684 let mut tied = loose;
685 tied[0] = tied[0].with(Constraint::Reuse(1));
686 let mut set = Changes::new();
687 set.rewrite(
688 mov,
689 Plan { opcode: add, operands: tied, imm: None, amode: None, symbol: None },
690 );
691 assert_eq!(refused(&func, &names, &set), None, "the same instruction written properly");
692 }
693
694 #[test]
697 fn an_immediate_has_to_be_there_exactly_when_the_instruction_carries_one() {
698 let (mut names, mut func, block) = empty();
699 let (mov, into) = copy(&mut func, &mut names, block);
700 let same = func[mov].opcode;
701 let add = op(&mut names, "add_ri_64");
702 let mut set = Changes::new();
703 set.rewrite(
704 mov,
705 Plan {
706 opcode: same,
707 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
708 imm: Some(7),
709 amode: None,
710 symbol: None,
711 },
712 );
713 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "a copy of seven");
714
715 let tied = vec![
716 mir::Operand::write(into, GPR).with(Constraint::Reuse(1)),
717 mir::Operand::read(into, GPR),
718 ];
719 let mut set = Changes::new();
720 set.rewrite(
721 mov,
722 Plan { opcode: add, operands: tied.clone(), imm: None, amode: None, symbol: None },
723 );
724 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "an add of nothing");
725
726 let mut set = Changes::new();
727 set.rewrite(
728 mov,
729 Plan { opcode: add, operands: tied, imm: Some(7), amode: None, symbol: None },
730 );
731 assert_eq!(refused(&func, &names, &set), None);
732 }
733
734 #[test]
737 fn an_address_on_an_instruction_that_has_none_is_refused() {
738 let (mut names, mut func, block) = empty();
739 let (mov, into) = copy(&mut func, &mut names, block);
740 let base = func.new_vreg(GPR);
741 let same = func[mov].opcode;
742 let mut set = Changes::new();
743 set.rewrite(
744 mov,
745 Plan {
746 opcode: same,
747 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
748 imm: None,
749 amode: Some(mir::Amode {
750 base: Some(1),
751 index: None,
752 scale: 1,
753 disp: 0,
754 symbol: None,
755 block: None,
756 table: None,
757 reach: mir::Reach::Itself,
758 segment: None,
759 }),
760 symbol: None,
761 },
762 );
763
764 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
765 }
766
767 #[test]
769 fn an_instruction_that_wants_an_address_and_has_none_is_refused() {
770 let (mut names, mut func, block) = empty();
771 let (mov, into) = copy(&mut func, &mut names, block);
772 let load = op(&mut names, "mov_rm_64");
773 let mut set = Changes::new();
774 set.rewrite(
775 mov,
776 Plan {
777 opcode: load,
778 operands: vec![mir::Operand::write(into, GPR)],
779 imm: None,
780 amode: None,
781 symbol: None,
782 },
783 );
784
785 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
786 }
787
788 #[test]
792 fn an_index_scaled_by_something_this_machine_cannot_write_is_refused() {
793 let (mut names, mut func, block) = empty();
794 let (mov, into) = copy(&mut func, &mut names, block);
795 let base = func.new_vreg(GPR);
796 let index = func.new_vreg(GPR);
797 let load = op(&mut names, "mov_rm_64");
798 let scaled = |scale| Plan {
799 opcode: load,
800 operands: vec![
801 mir::Operand::write(into, GPR),
802 mir::Operand::read(base, GPR),
803 mir::Operand::read(index, GPR),
804 ],
805 imm: None,
806 amode: Some(mir::Amode {
807 base: Some(1),
808 index: Some(2),
809 scale,
810 disp: 0,
811 symbol: None,
812 block: None,
813 table: None,
814 reach: mir::Reach::Itself,
815 segment: None,
816 }),
817 symbol: None,
818 };
819 let mut set = Changes::new();
820 set.rewrite(mov, scaled(3));
821 assert_eq!(refused(&func, &names, &set), Some(Refusal::Scale(mov)));
822
823 let mut set = Changes::new();
824 set.rewrite(mov, scaled(4));
825 assert_eq!(refused(&func, &names, &set), None);
826 }
827
828 #[test]
832 fn an_address_pointing_at_an_operand_of_its_own_instruction_is_refused() {
833 let (mut names, mut func, block) = empty();
834 let (mov, into) = copy(&mut func, &mut names, block);
835 let load = op(&mut names, "mov_rm_64");
836 let mut set = Changes::new();
837 set.rewrite(
838 mov,
839 Plan {
840 opcode: load,
841 operands: vec![mir::Operand::write(into, GPR)],
842 imm: None,
843 amode: Some(mir::Amode {
844 base: Some(0),
845 index: None,
846 scale: 1,
847 disp: 0,
848 symbol: None,
849 block: None,
850 table: None,
851 reach: mir::Reach::Itself,
852 segment: None,
853 }),
854 symbol: None,
855 },
856 );
857
858 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
859 }
860
861 #[test]
865 fn removing_an_instruction_whose_answer_is_still_read_is_refused() {
866 let (mut names, mut func, block) = empty();
867 let (mov, into) = copy(&mut func, &mut names, block);
868 let out = func.new_vreg(GPR);
869 let second = func[mov].opcode;
870 func.build(block, second).def(out, GPR).uses(into, GPR).finish();
871 let mut set = Changes::new();
872 set.remove(mov);
873
874 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
875 assert_eq!(shape(&func, &names, block).len(), 2);
876 }
877
878 #[test]
882 fn removing_it_in_a_set_that_takes_away_the_reader_is_taken() {
883 let (mut names, mut func, block) = empty();
884 let base = func.new_vreg(GPR);
885 let address = func.new_vreg(GPR);
886 let out = func.new_vreg(GPR);
887 let lea = op(&mut names, "lea_64");
888 let load = op(&mut names, "mov_rm_64");
889 let at = |reg| mir::Mem { disp: 16, ..mir::Mem::at(mir::Operand::read(reg, GPR)) };
890 let made = func.build(block, lea).def(address, GPR).mem(at(base)).finish();
891 let read = func
892 .build(block, load)
893 .def(out, GPR)
894 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
895 .finish();
896
897 let mut set = Changes::new();
898 set.rewrite(
899 read,
900 Plan {
901 opcode: load,
902 operands: vec![mir::Operand::write(out, GPR), mir::Operand::read(base, GPR)],
903 imm: None,
904 amode: Some(mir::Amode { disp: 16, ..func[func[read].mem.expect("a load")] }),
905 symbol: None,
906 },
907 );
908 set.remove(made);
909
910 let mut reads = Reads::of(&func);
911 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
912
913 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
914 assert_eq!(reads.count(address), 0, "nothing reads the address the lea wrote");
915 assert_eq!(reads.count(base), 1, "the load reads what the lea read");
916 }
917
918 #[test]
921 fn a_rename_that_takes_the_last_reader_off_an_instruction_lets_it_go() {
922 let (mut names, mut func, block) = empty();
923 let (first, into) = copy(&mut func, &mut names, block);
924 let source = func[func[first].operands][1].reg;
925 let out = func.new_vreg(GPR);
926 let mov = func[first].opcode;
927 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
928
929 let mut set = Changes::new();
930 set.rename(second, into, source);
931 set.remove(first);
932 let mut reads = Reads::of(&func);
933 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
934
935 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
936 assert_eq!(func[func[second].operands][1].reg, source, "the reader was not sent on");
937 assert_eq!(reads.count(into), 0, "nothing reads what the copy wrote");
938 assert_eq!(reads.count(source), 1, "the reader reads what the copy read");
939 }
940
941 #[test]
943 fn a_removal_whose_reader_is_not_renamed_is_refused() {
944 let (mut names, mut func, block) = empty();
945 let (first, into) = copy(&mut func, &mut names, block);
946 let out = func.new_vreg(GPR);
947 let mov = func[first].opcode;
948 func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
949
950 let mut set = Changes::new();
951 set.remove(first);
952 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(first)));
953 }
954
955 #[test]
959 fn a_rename_into_a_register_of_another_class_is_refused() {
960 let (mut names, mut func, block) = empty();
961 let (mov, _) = copy(&mut func, &mut names, block);
962 let source = func[func[mov].operands][1].reg;
963 let float = func.new_vreg(XMM);
964
965 let mut set = Changes::new();
966 set.rename(mov, source, float);
967 assert_eq!(refused(&func, &names, &set), Some(Refusal::Class(mov)));
968 }
969
970 #[test]
973 fn an_edge_carries_what_the_set_says_and_the_instruction_it_read_goes() {
974 let (mut names, mut func, block) = empty();
975 let next = func.create_block();
976 let (mov, into) = copy(&mut func, &mut names, block);
977 let source = func[func[mov].operands][1].reg;
978 let arrived = func.new_vreg(GPR);
979 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
980 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
981
982 let mut set = Changes::new();
983 set.carry(block, 0, vec![source]);
984 set.remove(mov);
985 let mut reads = Reads::of(&func);
986 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
987
988 assert!(shape(&func, &names, block).is_empty(), "the copy is still there");
989 assert_eq!(func[block].succs[0].args, vec![source]);
990 assert_eq!(reads.count(into), 0);
991 assert_eq!(reads.count(source), 1, "the edge reads what the copy read");
992 }
993
994 #[test]
997 fn an_edge_that_is_not_there_is_refused() {
998 let (mut names, mut func, block) = empty();
999 copy(&mut func, &mut names, block);
1000
1001 let mut set = Changes::new();
1002 set.carry(block, 0, Vec::new());
1003 assert_eq!(refused(&func, &names, &set), Some(Refusal::Edge(block, 0)));
1004 }
1005
1006 #[test]
1010 fn an_edge_carrying_the_wrong_number_of_values_is_refused() {
1011 let (mut names, mut func, block) = empty();
1012 let next = func.create_block();
1013 let (_, into) = copy(&mut func, &mut names, block);
1014 let arrived = func.new_vreg(GPR);
1015 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1016 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1017
1018 let mut set = Changes::new();
1019 set.carry(block, 0, vec![into, into]);
1020 assert_eq!(refused(&func, &names, &set), Some(Refusal::Args(block, 0)));
1021 }
1022
1023 #[test]
1026 fn an_answer_an_edge_carries_keeps_its_instruction() {
1027 let (mut names, mut func, block) = empty();
1028 let next = func.create_block();
1029 let (mov, into) = copy(&mut func, &mut names, block);
1030 let arrived = func.new_vreg(GPR);
1031 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1032 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1033 let mut set = Changes::new();
1034 set.remove(mov);
1035
1036 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
1037 }
1038
1039 #[test]
1043 fn an_instruction_named_twice_is_refused() {
1044 let (mut names, mut func, block) = empty();
1045 let (mov, _) = copy(&mut func, &mut names, block);
1046 let mut set = Changes::new();
1047 set.rewrite(mov, Plan::of(&func, mov));
1048 set.remove(mov);
1049
1050 assert_eq!(refused(&func, &names, &set), Some(Refusal::Twice(mov)));
1051 }
1052
1053 #[test]
1056 fn an_instruction_that_has_already_gone_is_refused() {
1057 let (mut names, mut func, block) = empty();
1058 let (mov, _) = copy(&mut func, &mut names, block);
1059 let plan = Plan::of(&func, mov);
1060 func.remove_inst(mov);
1061 let mut set = Changes::new();
1062 set.rewrite(mov, plan);
1063
1064 assert_eq!(refused(&func, &names, &set), Some(Refusal::Gone(mov)));
1065 }
1066
1067 #[test]
1070 fn the_instruction_as_it_stands_is_a_proposal_the_target_takes() {
1071 let (mut names, mut func, block) = empty();
1072 let base = func.new_vreg(GPR);
1073 let out = func.new_vreg(GPR);
1074 let load = op(&mut names, "mov_rm_64");
1075 let read = func
1076 .build(block, load)
1077 .def(out, GPR)
1078 .mem(mir::Mem { disp: 24, ..mir::Mem::at(mir::Operand::read(base, GPR)) })
1079 .finish();
1080
1081 let plan = Plan::of(&func, read);
1082 let mut set = Changes::new();
1083 set.rewrite(read, plan.clone());
1084 let mut reads = Reads::of(&func);
1085 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1086
1087 assert_eq!(Plan::of(&func, read), plan);
1088 assert_eq!(reads.count(base), 1, "the one read it had before");
1089 }
1090
1091 #[test]
1094 fn a_set_with_one_bad_change_in_it_leaves_the_others_alone() {
1095 let (mut names, mut func, block) = empty();
1096 let (first, into) = copy(&mut func, &mut names, block);
1097 let (second, _) = copy(&mut func, &mut names, block);
1098 let load = op(&mut names, "mov_rm_64");
1099 let mut set = Changes::new();
1100 set.rewrite(
1101 first,
1102 Plan {
1103 opcode: load,
1104 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
1105 imm: None,
1106 amode: Some(mir::Amode {
1107 base: Some(1),
1108 index: None,
1109 scale: 1,
1110 disp: 0,
1111 symbol: None,
1112 block: None,
1113 table: None,
1114 reach: mir::Reach::Itself,
1115 segment: None,
1116 }),
1117 symbol: None,
1118 },
1119 );
1120 set.rewrite(second, Plan { imm: Some(3), ..Plan::of(&func, second) });
1121
1122 let mut reads = Reads::of(&func);
1123 assert_eq!(
1124 set.commit(&mut func, &mut reads, &names, &MACHINE),
1125 Err(Refusal::Imm(second)),
1126 "the second change is the one the target turns down"
1127 );
1128 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.mov_rr_64"]);
1129 }
1130
1131 #[test]
1133 fn a_set_with_nothing_in_it_commits() {
1134 let (mut names, mut func, block) = empty();
1135 copy(&mut func, &mut names, block);
1136 let set = Changes::new();
1137 assert!(set.is_empty());
1138
1139 let mut reads = Reads::of(&func);
1140 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(0));
1141 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1142 }
1143
1144 #[test]
1148 fn the_counts_are_still_right_after_a_commit() {
1149 let (mut names, mut func, block) = empty();
1150 let (first, into) = copy(&mut func, &mut names, block);
1151 let out = func.new_vreg(GPR);
1152 let mov = func[first].opcode;
1153 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1154
1155 let mut reads = Reads::of(&func);
1156 assert_eq!(reads.count(into), 1);
1157 let mut set = Changes::new();
1158 set.remove(second);
1159 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1160 assert_eq!(reads.count(into), 0, "the reader went and the count went with it");
1161
1162 let mut set = Changes::new();
1163 set.remove(first);
1164 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1165 assert!(shape(&func, &names, block).is_empty());
1166 }
1167}