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 Crowded(mir::Inst),
155 Read(mir::Inst),
157 Class(mir::Inst),
159 Edge(mir::Block, usize),
161 Args(mir::Block, usize),
163}
164
165#[derive(Debug, Clone, Default)]
175pub struct Reads {
176 virtuals: Vec<usize>,
177 physical: HashMap<mir::Reg, usize>,
178}
179
180impl Reads {
181 #[must_use]
184 pub fn of(func: &mir::Func) -> Self {
185 let mut reads = Self { virtuals: vec![0; func.vregs()], physical: HashMap::new() };
186 for block in func.blocks() {
187 for inst in func.insts(block) {
188 for operand in &func[func[inst].operands] {
189 if operand.role == Role::Use {
190 reads.gained(operand.reg);
191 }
192 }
193 }
194 for call in &func[block].succs {
195 for &arg in &call.args {
196 reads.gained(arg);
197 }
198 }
199 }
200 reads
201 }
202
203 #[must_use]
205 pub fn count(&self, reg: mir::Reg) -> usize {
206 match reg.number() {
207 Some(number) => self.virtuals.get(number as usize).copied().unwrap_or(0),
208 None => self.physical.get(®).copied().unwrap_or(0),
209 }
210 }
211
212 fn gained(&mut self, reg: mir::Reg) {
214 match reg.number() {
215 Some(number) => {
216 let number = number as usize;
217 if number >= self.virtuals.len() {
218 self.virtuals.resize(number + 1, 0);
219 }
220 self.virtuals[number] += 1;
221 }
222 None => *self.physical.entry(reg).or_insert(0) += 1,
223 }
224 }
225
226 fn lost(&mut self, reg: mir::Reg) {
228 let count = match reg.number() {
229 Some(number) => self.virtuals.get_mut(number as usize),
230 None => self.physical.get_mut(®),
231 };
232 if let Some(count) = count {
233 *count = count.saturating_sub(1);
234 }
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
240enum What {
241 Rewrite(Plan),
243 Rename { from: mir::Reg, into: mir::Reg },
246 Remove,
248}
249
250#[derive(Debug, Clone, PartialEq, Eq)]
252struct Carried {
253 from: mir::Block,
255 at: usize,
257 args: Vec<mir::Reg>,
259}
260
261#[derive(Debug, Clone, Default)]
267pub struct Changes {
268 changes: Vec<(mir::Inst, What)>,
269 carries: Vec<Carried>,
270}
271
272impl Changes {
273 #[must_use]
275 pub fn new() -> Self {
276 Self::default()
277 }
278
279 #[must_use]
281 pub fn len(&self) -> usize {
282 self.changes.len() + self.carries.len()
283 }
284
285 #[must_use]
287 pub fn is_empty(&self) -> bool {
288 self.changes.is_empty() && self.carries.is_empty()
289 }
290
291 pub fn rewrite(&mut self, inst: mir::Inst, plan: Plan) {
293 self.changes.push((inst, What::Rewrite(plan)));
294 }
295
296 pub fn rename(&mut self, inst: mir::Inst, from: mir::Reg, into: mir::Reg) {
301 self.changes.push((inst, What::Rename { from, into }));
302 }
303
304 pub fn carry(&mut self, from: mir::Block, at: usize, args: Vec<mir::Reg>) {
309 self.carries.push(Carried { from, at, args });
310 }
311
312 pub fn remove(&mut self, inst: mir::Inst) {
314 self.changes.push((inst, What::Remove));
315 }
316
317 #[must_use]
323 pub fn refused(
324 &self,
325 func: &mir::Func,
326 reads: &Reads,
327 names: &Interner,
328 machine: &MachineInsts,
329 ) -> Option<Refusal> {
330 for (at, &(inst, _)) in self.changes.iter().enumerate() {
331 if self.changes[..at].iter().any(|&(other, _)| other == inst) {
332 return Some(Refusal::Twice(inst));
333 }
334 if func.block_of(inst).is_none() {
335 return Some(Refusal::Gone(inst));
336 }
337 }
338 for (at, carried) in self.carries.iter().enumerate() {
339 let edge = (carried.from, carried.at);
340 if self.carries[..at].iter().any(|other| (other.from, other.at) == edge) {
341 return Some(Refusal::Edge(carried.from, carried.at));
342 }
343 let Some(call) = func[carried.from].succs.get(carried.at) else {
344 return Some(Refusal::Edge(carried.from, carried.at));
345 };
346 if func[call.block].params.len() != carried.args.len() {
347 return Some(Refusal::Args(carried.from, carried.at));
348 }
349 }
350 for &(inst, ref what) in &self.changes {
351 match what {
352 What::Rewrite(plan) => {
353 if let Some(refusal) = shaped(inst, plan, names, machine) {
354 return Some(refusal);
355 }
356 }
357 What::Rename { from, into } => {
358 if let Some(refusal) = renamed(func, inst, *from, *into) {
359 return Some(refusal);
360 }
361 }
362 What::Remove => {
363 if self.read_after(func, reads, inst) {
364 return Some(Refusal::Read(inst));
365 }
366 }
367 }
368 }
369 None
370 }
371
372 pub fn commit(
380 self,
381 func: &mut mir::Func,
382 reads: &mut Reads,
383 names: &Interner,
384 machine: &MachineInsts,
385 ) -> Result<usize, Refusal> {
386 if let Some(refusal) = self.refused(func, reads, names, machine) {
387 return Err(refusal);
388 }
389 let touched = self.len();
390 for (inst, what) in self.changes {
391 let (lost, gained) = moved(func, inst, &what);
392 for reg in lost {
393 reads.lost(reg);
394 }
395 for reg in gained {
396 reads.gained(reg);
397 }
398 match what {
399 What::Rewrite(plan) => {
400 let operands = func.push_operands(&plan.operands);
401 let imm = plan.imm.map(|value| func.add_imm(value));
402 let mem = plan.amode.map(|amode| func.add_amode(amode));
403 let data = &mut func[inst];
404 data.opcode = plan.opcode;
405 data.operands = operands;
406 data.imm = imm;
407 data.mem = mem;
408 data.symbol = plan.symbol;
409 }
410 What::Rename { from, into } => {
411 let operands = func[inst].operands;
412 for operand in &mut func[operands] {
413 if operand.role == Role::Use && operand.reg == from {
414 operand.reg = into;
415 }
416 }
417 }
418 What::Remove => func.remove_inst(inst),
419 }
420 }
421 for carried in self.carries {
422 for &arg in &func[carried.from].succs[carried.at].args {
423 reads.lost(arg);
424 }
425 for &arg in &carried.args {
426 reads.gained(arg);
427 }
428 func.succs_mut(carried.from)[carried.at].args = carried.args;
429 }
430 Ok(touched)
431 }
432
433 fn read_after(&self, func: &mir::Func, reads: &Reads, inst: mir::Inst) -> bool {
440 func[func[inst].operands].iter().filter(|operand| operand.role.is_def()).any(|operand| {
441 let mut left = reads.count(operand.reg);
442 let mut settle = |lost: &[mir::Reg], gained: &[mir::Reg]| {
443 let goes = lost.iter().filter(|&®| reg == operand.reg).count();
444 left = left.saturating_sub(goes);
445 left += gained.iter().filter(|&®| reg == operand.reg).count();
446 };
447 for &(other, ref what) in &self.changes {
448 let (lost, gained) = moved(func, other, what);
449 settle(&lost, &gained);
450 }
451 for carried in &self.carries {
452 settle(&func[carried.from].succs[carried.at].args, &carried.args);
453 }
454 left != 0
455 })
456 }
457}
458
459fn moved(func: &mir::Func, inst: mir::Inst, what: &What) -> (Vec<mir::Reg>, Vec<mir::Reg>) {
466 let held: Vec<mir::Reg> = func[func[inst].operands]
467 .iter()
468 .filter(|operand| operand.role == Role::Use)
469 .map(|operand| operand.reg)
470 .collect();
471 match what {
472 What::Rewrite(plan) => (held, plan.reads().collect()),
473 What::Rename { from, into } => {
474 let gone: Vec<mir::Reg> = held.into_iter().filter(|reg| reg == from).collect();
475 let back = vec![*into; gone.len()];
476 (gone, back)
477 }
478 What::Remove => (held, Vec::new()),
479 }
480}
481
482fn renamed(func: &mir::Func, inst: mir::Inst, from: mir::Reg, into: mir::Reg) -> Option<Refusal> {
489 let class = func.class_of(into)?;
490 func[func[inst].operands]
491 .iter()
492 .any(|operand| operand.role == Role::Use && operand.reg == from && operand.class != class)
493 .then_some(Refusal::Class(inst))
494}
495
496fn shaped(
511 inst: mir::Inst,
512 plan: &Plan,
513 names: &Interner,
514 machine: &MachineInsts,
515) -> Option<Refusal> {
516 let name = names.resolve(plan.opcode.name());
517 let bare = machine.bare(name);
518 let Some(desc) = (machine.operands)(bare) else { return Some(Refusal::Unknown(inst)) };
519 if plan.operands.len() < desc.len() {
520 return Some(Refusal::Operands(inst));
521 }
522 let (described, addressed) = plan.operands.split_at(desc.len());
523 for (operand, want) in described.iter().zip(desc) {
524 let shape = (operand.class, operand.role, operand.constraint);
525 if shape != (want.class, want.role, want.constraint) {
526 return Some(Refusal::Operands(inst));
527 }
528 }
529 if plan.imm.is_some() != (machine.takes_imm)(bare) {
530 return Some(Refusal::Imm(inst));
531 }
532 let Some(amode) = plan.amode else {
533 return ((machine.takes_mem)(bare) || !addressed.is_empty()).then_some(Refusal::Mem(inst));
534 };
535 if !(machine.takes_mem)(bare) {
536 return Some(Refusal::Mem(inst));
537 }
538 let named = [amode.base, amode.index].into_iter().flatten();
539 let mut wanted = 0;
540 for at in named {
541 let Some(operand) = plan.operands.get(usize::from(at)) else {
542 return Some(Refusal::Operands(inst));
543 };
544 if usize::from(at) < desc.len() || operand.role != Role::Use {
545 return Some(Refusal::Operands(inst));
546 }
547 wanted += 1;
548 }
549 if addressed.len() != wanted {
550 return Some(Refusal::Operands(inst));
551 }
552 let scaled = if amode.index.is_some() { amode.scale } else { 1 };
553 if !machine.scales(scaled) || (amode.index.is_none() && amode.scale != 1) {
554 return Some(Refusal::Scale(inst));
555 }
556 if amode.index.is_some() && amode.disp != 0 && !machine.index_and_disp {
557 return Some(Refusal::Crowded(inst));
558 }
559 None
560}
561
562#[cfg(test)]
563mod tests {
564 use rucc_mir::Constraint;
565 use rucc_target::x86_64::{GPR, MACHINE, XMM};
566
567 use super::*;
568
569 fn empty() -> (Interner, mir::Func, mir::Block) {
571 let mut names = Interner::new();
572 let mut func = mir::Func::new(names.intern("f"));
573 let block = func.create_block();
574 (names, func, block)
575 }
576
577 fn op(names: &mut Interner, name: &str) -> mir::Opcode {
579 mir::Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
580 }
581
582 fn refused(func: &mir::Func, names: &Interner, set: &Changes) -> Option<Refusal> {
584 set.refused(func, &Reads::of(func), names, &MACHINE)
585 }
586
587 fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
589 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
590 }
591
592 fn copy(
594 func: &mut mir::Func,
595 names: &mut Interner,
596 block: mir::Block,
597 ) -> (mir::Inst, mir::Reg) {
598 let from = func.new_vreg(GPR);
599 let into = func.new_vreg(GPR);
600 let mov = op(names, "mov_rr_64");
601 (func.build(block, mov).def(into, GPR).uses(from, GPR).finish(), into)
602 }
603
604 #[test]
607 fn a_rewrite_the_target_has_is_taken() {
608 let (mut names, mut func, block) = empty();
609 let (mov, into) = copy(&mut func, &mut names, block);
610 let base = func.new_vreg(GPR);
611 let load = op(&mut names, "mov_rm_64");
612 let mut set = Changes::new();
613 set.rewrite(
614 mov,
615 Plan {
616 opcode: load,
617 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
618 imm: None,
619 amode: Some(mir::Amode {
620 base: Some(1),
621 index: None,
622 scale: 1,
623 disp: 8,
624 symbol: None,
625 block: None,
626 table: None,
627 reach: mir::Reach::Itself,
628 segment: None,
629 }),
630 symbol: None,
631 },
632 );
633
634 let mut reads = Reads::of(&func);
635 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
636
637 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
638 assert_eq!(func[func[mov].mem.expect("the load has an address")].disp, 8);
639 assert_eq!(reads.count(base), 1, "the address register is read now");
640 assert_eq!(reads.count(into), 0, "the register the copy read is read by nothing");
641 }
642
643 #[test]
646 fn an_opcode_this_target_does_not_have_is_refused() {
647 let (mut names, mut func, block) = empty();
648 let (mov, into) = copy(&mut func, &mut names, block);
649 let made_up = op(&mut names, "mov_rr_65");
650 let mut set = Changes::new();
651 set.rewrite(
652 mov,
653 Plan {
654 opcode: made_up,
655 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
656 imm: None,
657 amode: None,
658 symbol: None,
659 },
660 );
661
662 assert_eq!(refused(&func, &names, &set), Some(Refusal::Unknown(mov)));
663 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"], "the function was written to");
664 }
665
666 #[test]
670 fn an_operand_of_the_wrong_class_is_refused() {
671 let (mut names, mut func, block) = empty();
672 let (mov, into) = copy(&mut func, &mut names, block);
673 let float = func.new_vreg(XMM);
674 let same = func[mov].opcode;
675 let mut set = Changes::new();
676 set.rewrite(
677 mov,
678 Plan {
679 opcode: same,
680 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(float, XMM)],
681 imm: None,
682 amode: None,
683 symbol: None,
684 },
685 );
686
687 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
688 }
689
690 #[test]
694 fn an_add_whose_answer_is_not_tied_to_its_source_is_refused() {
695 let (mut names, mut func, block) = empty();
696 let (mov, into) = copy(&mut func, &mut names, block);
697 let other = func.new_vreg(GPR);
698 let add = op(&mut names, "add_rr_64");
699 let loose = vec![
700 mir::Operand::write(into, GPR),
701 mir::Operand::read(into, GPR),
702 mir::Operand::read(other, GPR),
703 ];
704 let mut set = Changes::new();
705 set.rewrite(
706 mov,
707 Plan { opcode: add, operands: loose.clone(), imm: None, amode: None, symbol: None },
708 );
709 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
710
711 let mut tied = loose;
712 tied[0] = tied[0].with(Constraint::Reuse(1));
713 let mut set = Changes::new();
714 set.rewrite(
715 mov,
716 Plan { opcode: add, operands: tied, imm: None, amode: None, symbol: None },
717 );
718 assert_eq!(refused(&func, &names, &set), None, "the same instruction written properly");
719 }
720
721 #[test]
724 fn an_immediate_has_to_be_there_exactly_when_the_instruction_carries_one() {
725 let (mut names, mut func, block) = empty();
726 let (mov, into) = copy(&mut func, &mut names, block);
727 let same = func[mov].opcode;
728 let add = op(&mut names, "add_ri_64");
729 let mut set = Changes::new();
730 set.rewrite(
731 mov,
732 Plan {
733 opcode: same,
734 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
735 imm: Some(7),
736 amode: None,
737 symbol: None,
738 },
739 );
740 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "a copy of seven");
741
742 let tied = vec![
743 mir::Operand::write(into, GPR).with(Constraint::Reuse(1)),
744 mir::Operand::read(into, GPR),
745 ];
746 let mut set = Changes::new();
747 set.rewrite(
748 mov,
749 Plan { opcode: add, operands: tied.clone(), imm: None, amode: None, symbol: None },
750 );
751 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "an add of nothing");
752
753 let mut set = Changes::new();
754 set.rewrite(
755 mov,
756 Plan { opcode: add, operands: tied, imm: Some(7), amode: None, symbol: None },
757 );
758 assert_eq!(refused(&func, &names, &set), None);
759 }
760
761 #[test]
764 fn an_address_on_an_instruction_that_has_none_is_refused() {
765 let (mut names, mut func, block) = empty();
766 let (mov, into) = copy(&mut func, &mut names, block);
767 let base = func.new_vreg(GPR);
768 let same = func[mov].opcode;
769 let mut set = Changes::new();
770 set.rewrite(
771 mov,
772 Plan {
773 opcode: same,
774 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
775 imm: None,
776 amode: Some(mir::Amode {
777 base: Some(1),
778 index: None,
779 scale: 1,
780 disp: 0,
781 symbol: None,
782 block: None,
783 table: None,
784 reach: mir::Reach::Itself,
785 segment: None,
786 }),
787 symbol: None,
788 },
789 );
790
791 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
792 }
793
794 #[test]
796 fn an_instruction_that_wants_an_address_and_has_none_is_refused() {
797 let (mut names, mut func, block) = empty();
798 let (mov, into) = copy(&mut func, &mut names, block);
799 let load = op(&mut names, "mov_rm_64");
800 let mut set = Changes::new();
801 set.rewrite(
802 mov,
803 Plan {
804 opcode: load,
805 operands: vec![mir::Operand::write(into, GPR)],
806 imm: None,
807 amode: None,
808 symbol: None,
809 },
810 );
811
812 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
813 }
814
815 #[test]
819 fn an_index_scaled_by_something_this_machine_cannot_write_is_refused() {
820 let (mut names, mut func, block) = empty();
821 let (mov, into) = copy(&mut func, &mut names, block);
822 let base = func.new_vreg(GPR);
823 let index = func.new_vreg(GPR);
824 let load = op(&mut names, "mov_rm_64");
825 let scaled = |scale| Plan {
826 opcode: load,
827 operands: vec![
828 mir::Operand::write(into, GPR),
829 mir::Operand::read(base, GPR),
830 mir::Operand::read(index, GPR),
831 ],
832 imm: None,
833 amode: Some(mir::Amode {
834 base: Some(1),
835 index: Some(2),
836 scale,
837 disp: 0,
838 symbol: None,
839 block: None,
840 table: None,
841 reach: mir::Reach::Itself,
842 segment: None,
843 }),
844 symbol: None,
845 };
846 let mut set = Changes::new();
847 set.rewrite(mov, scaled(3));
848 assert_eq!(refused(&func, &names, &set), Some(Refusal::Scale(mov)));
849
850 let mut set = Changes::new();
851 set.rewrite(mov, scaled(4));
852 assert_eq!(refused(&func, &names, &set), None);
853 }
854
855 #[test]
858 fn an_index_beside_a_displacement_is_refused_where_the_machine_has_no_such_mode() {
859 use rucc_target::aarch64;
860
861 let (mut names, mut func, block) = empty();
862 let into = func.new_vreg(aarch64::GPR);
863 let base = func.new_vreg(aarch64::GPR);
864 let index = func.new_vreg(aarch64::GPR);
865 let load = mir::Opcode::new(names.intern("a64.ldr_64"));
866 let mov = func.build(block, load).def(into, aarch64::GPR).uses(base, aarch64::GPR).finish();
867 let plan = |disp| Plan {
868 opcode: load,
869 operands: vec![
870 mir::Operand::write(into, aarch64::GPR),
871 mir::Operand::read(base, aarch64::GPR),
872 mir::Operand::read(index, aarch64::GPR),
873 ],
874 imm: None,
875 amode: Some(mir::Amode { base: Some(1), index: Some(2), disp, ..mir::Amode::NOTHING }),
876 symbol: None,
877 };
878 let asked = |set: &Changes, func: &mir::Func| {
879 set.refused(func, &Reads::of(func), &names, &aarch64::MACHINE)
880 };
881 let mut set = Changes::new();
882 set.rewrite(mov, plan(16));
883 assert_eq!(asked(&set, &func), Some(Refusal::Crowded(mov)));
884
885 let mut set = Changes::new();
886 set.rewrite(mov, plan(0));
887 assert_eq!(asked(&set, &func), None);
888 }
889
890 #[test]
894 fn an_address_pointing_at_an_operand_of_its_own_instruction_is_refused() {
895 let (mut names, mut func, block) = empty();
896 let (mov, into) = copy(&mut func, &mut names, block);
897 let load = op(&mut names, "mov_rm_64");
898 let mut set = Changes::new();
899 set.rewrite(
900 mov,
901 Plan {
902 opcode: load,
903 operands: vec![mir::Operand::write(into, GPR)],
904 imm: None,
905 amode: Some(mir::Amode {
906 base: Some(0),
907 index: None,
908 scale: 1,
909 disp: 0,
910 symbol: None,
911 block: None,
912 table: None,
913 reach: mir::Reach::Itself,
914 segment: None,
915 }),
916 symbol: None,
917 },
918 );
919
920 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
921 }
922
923 #[test]
927 fn removing_an_instruction_whose_answer_is_still_read_is_refused() {
928 let (mut names, mut func, block) = empty();
929 let (mov, into) = copy(&mut func, &mut names, block);
930 let out = func.new_vreg(GPR);
931 let second = func[mov].opcode;
932 func.build(block, second).def(out, GPR).uses(into, GPR).finish();
933 let mut set = Changes::new();
934 set.remove(mov);
935
936 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
937 assert_eq!(shape(&func, &names, block).len(), 2);
938 }
939
940 #[test]
944 fn removing_it_in_a_set_that_takes_away_the_reader_is_taken() {
945 let (mut names, mut func, block) = empty();
946 let base = func.new_vreg(GPR);
947 let address = func.new_vreg(GPR);
948 let out = func.new_vreg(GPR);
949 let lea = op(&mut names, "lea_64");
950 let load = op(&mut names, "mov_rm_64");
951 let at = |reg| mir::Mem { disp: 16, ..mir::Mem::at(mir::Operand::read(reg, GPR)) };
952 let made = func.build(block, lea).def(address, GPR).mem(at(base)).finish();
953 let read = func
954 .build(block, load)
955 .def(out, GPR)
956 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
957 .finish();
958
959 let mut set = Changes::new();
960 set.rewrite(
961 read,
962 Plan {
963 opcode: load,
964 operands: vec![mir::Operand::write(out, GPR), mir::Operand::read(base, GPR)],
965 imm: None,
966 amode: Some(mir::Amode { disp: 16, ..func[func[read].mem.expect("a load")] }),
967 symbol: None,
968 },
969 );
970 set.remove(made);
971
972 let mut reads = Reads::of(&func);
973 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
974
975 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
976 assert_eq!(reads.count(address), 0, "nothing reads the address the lea wrote");
977 assert_eq!(reads.count(base), 1, "the load reads what the lea read");
978 }
979
980 #[test]
983 fn a_rename_that_takes_the_last_reader_off_an_instruction_lets_it_go() {
984 let (mut names, mut func, block) = empty();
985 let (first, into) = copy(&mut func, &mut names, block);
986 let source = func[func[first].operands][1].reg;
987 let out = func.new_vreg(GPR);
988 let mov = func[first].opcode;
989 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
990
991 let mut set = Changes::new();
992 set.rename(second, into, source);
993 set.remove(first);
994 let mut reads = Reads::of(&func);
995 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
996
997 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
998 assert_eq!(func[func[second].operands][1].reg, source, "the reader was not sent on");
999 assert_eq!(reads.count(into), 0, "nothing reads what the copy wrote");
1000 assert_eq!(reads.count(source), 1, "the reader reads what the copy read");
1001 }
1002
1003 #[test]
1005 fn a_removal_whose_reader_is_not_renamed_is_refused() {
1006 let (mut names, mut func, block) = empty();
1007 let (first, into) = copy(&mut func, &mut names, block);
1008 let out = func.new_vreg(GPR);
1009 let mov = func[first].opcode;
1010 func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1011
1012 let mut set = Changes::new();
1013 set.remove(first);
1014 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(first)));
1015 }
1016
1017 #[test]
1021 fn a_rename_into_a_register_of_another_class_is_refused() {
1022 let (mut names, mut func, block) = empty();
1023 let (mov, _) = copy(&mut func, &mut names, block);
1024 let source = func[func[mov].operands][1].reg;
1025 let float = func.new_vreg(XMM);
1026
1027 let mut set = Changes::new();
1028 set.rename(mov, source, float);
1029 assert_eq!(refused(&func, &names, &set), Some(Refusal::Class(mov)));
1030 }
1031
1032 #[test]
1035 fn an_edge_carries_what_the_set_says_and_the_instruction_it_read_goes() {
1036 let (mut names, mut func, block) = empty();
1037 let next = func.create_block();
1038 let (mov, into) = copy(&mut func, &mut names, block);
1039 let source = func[func[mov].operands][1].reg;
1040 let arrived = func.new_vreg(GPR);
1041 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1042 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1043
1044 let mut set = Changes::new();
1045 set.carry(block, 0, vec![source]);
1046 set.remove(mov);
1047 let mut reads = Reads::of(&func);
1048 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
1049
1050 assert!(shape(&func, &names, block).is_empty(), "the copy is still there");
1051 assert_eq!(func[block].succs[0].args, vec![source]);
1052 assert_eq!(reads.count(into), 0);
1053 assert_eq!(reads.count(source), 1, "the edge reads what the copy read");
1054 }
1055
1056 #[test]
1059 fn an_edge_that_is_not_there_is_refused() {
1060 let (mut names, mut func, block) = empty();
1061 copy(&mut func, &mut names, block);
1062
1063 let mut set = Changes::new();
1064 set.carry(block, 0, Vec::new());
1065 assert_eq!(refused(&func, &names, &set), Some(Refusal::Edge(block, 0)));
1066 }
1067
1068 #[test]
1072 fn an_edge_carrying_the_wrong_number_of_values_is_refused() {
1073 let (mut names, mut func, block) = empty();
1074 let next = func.create_block();
1075 let (_, into) = copy(&mut func, &mut names, block);
1076 let arrived = func.new_vreg(GPR);
1077 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1078 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1079
1080 let mut set = Changes::new();
1081 set.carry(block, 0, vec![into, into]);
1082 assert_eq!(refused(&func, &names, &set), Some(Refusal::Args(block, 0)));
1083 }
1084
1085 #[test]
1088 fn an_answer_an_edge_carries_keeps_its_instruction() {
1089 let (mut names, mut func, block) = empty();
1090 let next = func.create_block();
1091 let (mov, into) = copy(&mut func, &mut names, block);
1092 let arrived = func.new_vreg(GPR);
1093 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1094 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1095 let mut set = Changes::new();
1096 set.remove(mov);
1097
1098 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
1099 }
1100
1101 #[test]
1105 fn an_instruction_named_twice_is_refused() {
1106 let (mut names, mut func, block) = empty();
1107 let (mov, _) = copy(&mut func, &mut names, block);
1108 let mut set = Changes::new();
1109 set.rewrite(mov, Plan::of(&func, mov));
1110 set.remove(mov);
1111
1112 assert_eq!(refused(&func, &names, &set), Some(Refusal::Twice(mov)));
1113 }
1114
1115 #[test]
1118 fn an_instruction_that_has_already_gone_is_refused() {
1119 let (mut names, mut func, block) = empty();
1120 let (mov, _) = copy(&mut func, &mut names, block);
1121 let plan = Plan::of(&func, mov);
1122 func.remove_inst(mov);
1123 let mut set = Changes::new();
1124 set.rewrite(mov, plan);
1125
1126 assert_eq!(refused(&func, &names, &set), Some(Refusal::Gone(mov)));
1127 }
1128
1129 #[test]
1132 fn the_instruction_as_it_stands_is_a_proposal_the_target_takes() {
1133 let (mut names, mut func, block) = empty();
1134 let base = func.new_vreg(GPR);
1135 let out = func.new_vreg(GPR);
1136 let load = op(&mut names, "mov_rm_64");
1137 let read = func
1138 .build(block, load)
1139 .def(out, GPR)
1140 .mem(mir::Mem { disp: 24, ..mir::Mem::at(mir::Operand::read(base, GPR)) })
1141 .finish();
1142
1143 let plan = Plan::of(&func, read);
1144 let mut set = Changes::new();
1145 set.rewrite(read, plan.clone());
1146 let mut reads = Reads::of(&func);
1147 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1148
1149 assert_eq!(Plan::of(&func, read), plan);
1150 assert_eq!(reads.count(base), 1, "the one read it had before");
1151 }
1152
1153 #[test]
1156 fn a_set_with_one_bad_change_in_it_leaves_the_others_alone() {
1157 let (mut names, mut func, block) = empty();
1158 let (first, into) = copy(&mut func, &mut names, block);
1159 let (second, _) = copy(&mut func, &mut names, block);
1160 let load = op(&mut names, "mov_rm_64");
1161 let mut set = Changes::new();
1162 set.rewrite(
1163 first,
1164 Plan {
1165 opcode: load,
1166 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
1167 imm: None,
1168 amode: Some(mir::Amode {
1169 base: Some(1),
1170 index: None,
1171 scale: 1,
1172 disp: 0,
1173 symbol: None,
1174 block: None,
1175 table: None,
1176 reach: mir::Reach::Itself,
1177 segment: None,
1178 }),
1179 symbol: None,
1180 },
1181 );
1182 set.rewrite(second, Plan { imm: Some(3), ..Plan::of(&func, second) });
1183
1184 let mut reads = Reads::of(&func);
1185 assert_eq!(
1186 set.commit(&mut func, &mut reads, &names, &MACHINE),
1187 Err(Refusal::Imm(second)),
1188 "the second change is the one the target turns down"
1189 );
1190 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.mov_rr_64"]);
1191 }
1192
1193 #[test]
1195 fn a_set_with_nothing_in_it_commits() {
1196 let (mut names, mut func, block) = empty();
1197 copy(&mut func, &mut names, block);
1198 let set = Changes::new();
1199 assert!(set.is_empty());
1200
1201 let mut reads = Reads::of(&func);
1202 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(0));
1203 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1204 }
1205
1206 #[test]
1210 fn the_counts_are_still_right_after_a_commit() {
1211 let (mut names, mut func, block) = empty();
1212 let (first, into) = copy(&mut func, &mut names, block);
1213 let out = func.new_vreg(GPR);
1214 let mov = func[first].opcode;
1215 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1216
1217 let mut reads = Reads::of(&func);
1218 assert_eq!(reads.count(into), 1);
1219 let mut set = Changes::new();
1220 set.remove(second);
1221 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1222 assert_eq!(reads.count(into), 0, "the reader went and the count went with it");
1223
1224 let mut set = Changes::new();
1225 set.remove(first);
1226 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1227 assert!(shape(&func, &names, block).is_empty());
1228 }
1229}