1use rucc_base::{Interner, Symbol};
83use rucc_mir::{self as mir, Role};
84use rucc_target::MachineInsts;
85
86#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct Plan {
93 pub opcode: mir::Opcode,
95 pub operands: Vec<mir::Operand>,
99 pub imm: Option<i64>,
101 pub amode: Option<mir::Amode>,
104 pub symbol: Option<Symbol>,
106}
107
108impl Plan {
109 #[must_use]
111 pub fn of(func: &mir::Func, inst: mir::Inst) -> Self {
112 let data = &func[inst];
113 Self {
114 opcode: data.opcode,
115 operands: func[data.operands].to_vec(),
116 imm: data.imm.map(|at| func[at].0),
117 amode: data.mem.map(|at| func[at]),
118 symbol: data.symbol,
119 }
120 }
121
122 fn reads(&self) -> impl Iterator<Item = mir::Reg> + use<'_> {
124 self.operands.iter().filter(|operand| operand.role == Role::Use).map(|operand| operand.reg)
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum Refusal {
135 Twice(mir::Inst),
137 Gone(mir::Inst),
140 Unknown(mir::Inst),
142 Operands(mir::Inst),
144 Imm(mir::Inst),
146 Mem(mir::Inst),
148 Scale(mir::Inst),
150 Crowded(mir::Inst),
153 Read(mir::Inst),
155 Class(mir::Inst),
157 Edge(mir::Block, usize),
159 Args(mir::Block, usize),
161}
162
163#[derive(Debug, Clone, Default)]
175pub struct Reads {
176 virtuals: Vec<usize>,
177 physical: Vec<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: Vec::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(physical(reg)).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 => {
223 let number = physical(reg);
224 if number >= self.physical.len() {
225 self.physical.resize(number + 1, 0);
226 }
227 self.physical[number] += 1;
228 }
229 }
230 }
231
232 fn lost(&mut self, reg: mir::Reg) {
234 let count = match reg.number() {
235 Some(number) => self.virtuals.get_mut(number as usize),
236 None => self.physical.get_mut(physical(reg)),
237 };
238 if let Some(count) = count {
239 *count = count.saturating_sub(1);
240 }
241 }
242}
243
244fn physical(reg: mir::Reg) -> usize {
246 reg.phys().map_or(0, |phys| usize::from(phys.number()))
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
251enum What {
252 Rewrite(Plan),
254 Rename { from: mir::Reg, into: mir::Reg },
257 Remove,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
263struct Carried {
264 from: mir::Block,
266 at: usize,
268 args: Vec<mir::Reg>,
270}
271
272#[derive(Debug, Clone, Default)]
278pub struct Changes {
279 changes: Vec<(mir::Inst, What)>,
280 carries: Vec<Carried>,
281}
282
283impl Changes {
284 #[must_use]
286 pub fn new() -> Self {
287 Self::default()
288 }
289
290 #[must_use]
292 pub fn len(&self) -> usize {
293 self.changes.len() + self.carries.len()
294 }
295
296 #[must_use]
298 pub fn is_empty(&self) -> bool {
299 self.changes.is_empty() && self.carries.is_empty()
300 }
301
302 pub fn rewrite(&mut self, inst: mir::Inst, plan: Plan) {
304 self.changes.push((inst, What::Rewrite(plan)));
305 }
306
307 pub fn rename(&mut self, inst: mir::Inst, from: mir::Reg, into: mir::Reg) {
312 self.changes.push((inst, What::Rename { from, into }));
313 }
314
315 pub fn carry(&mut self, from: mir::Block, at: usize, args: Vec<mir::Reg>) {
320 self.carries.push(Carried { from, at, args });
321 }
322
323 pub fn remove(&mut self, inst: mir::Inst) {
325 self.changes.push((inst, What::Remove));
326 }
327
328 #[must_use]
334 pub fn refused(
335 &self,
336 func: &mir::Func,
337 reads: &Reads,
338 names: &Interner,
339 machine: &MachineInsts,
340 ) -> Option<Refusal> {
341 for (at, &(inst, _)) in self.changes.iter().enumerate() {
342 if self.changes[..at].iter().any(|&(other, _)| other == inst) {
343 return Some(Refusal::Twice(inst));
344 }
345 if func.block_of(inst).is_none() {
346 return Some(Refusal::Gone(inst));
347 }
348 }
349 for (at, carried) in self.carries.iter().enumerate() {
350 let edge = (carried.from, carried.at);
351 if self.carries[..at].iter().any(|other| (other.from, other.at) == edge) {
352 return Some(Refusal::Edge(carried.from, carried.at));
353 }
354 let Some(call) = func[carried.from].succs.get(carried.at) else {
355 return Some(Refusal::Edge(carried.from, carried.at));
356 };
357 if func[call.block].params.len() != carried.args.len() {
358 return Some(Refusal::Args(carried.from, carried.at));
359 }
360 }
361 for &(inst, ref what) in &self.changes {
362 match what {
363 What::Rewrite(plan) => {
364 if let Some(refusal) = shaped(inst, plan, names, machine) {
365 return Some(refusal);
366 }
367 }
368 What::Rename { from, into } => {
369 if let Some(refusal) = renamed(func, inst, *from, *into) {
370 return Some(refusal);
371 }
372 }
373 What::Remove => {
374 if self.read_after(func, reads, inst) {
375 return Some(Refusal::Read(inst));
376 }
377 }
378 }
379 }
380 None
381 }
382
383 pub fn commit(
391 self,
392 func: &mut mir::Func,
393 reads: &mut Reads,
394 names: &Interner,
395 machine: &MachineInsts,
396 ) -> Result<usize, Refusal> {
397 if let Some(refusal) = self.refused(func, reads, names, machine) {
398 return Err(refusal);
399 }
400 let touched = self.len();
401 for (inst, what) in self.changes {
402 let (lost, gained) = moved(func, inst, &what);
403 for reg in lost {
404 reads.lost(reg);
405 }
406 for reg in gained {
407 reads.gained(reg);
408 }
409 match what {
410 What::Rewrite(plan) => {
411 let operands = func.push_operands(&plan.operands);
412 let imm = plan.imm.map(|value| func.add_imm(value));
413 let mem = plan.amode.map(|amode| func.add_amode(amode));
414 let data = &mut func[inst];
415 data.opcode = plan.opcode;
416 data.operands = operands;
417 data.imm = imm;
418 data.mem = mem;
419 data.symbol = plan.symbol;
420 }
421 What::Rename { from, into } => {
422 let operands = func[inst].operands;
423 for operand in &mut func[operands] {
424 if operand.role == Role::Use && operand.reg == from {
425 operand.reg = into;
426 }
427 }
428 }
429 What::Remove => func.remove_inst(inst),
430 }
431 }
432 for carried in self.carries {
433 for &arg in &func[carried.from].succs[carried.at].args {
434 reads.lost(arg);
435 }
436 for &arg in &carried.args {
437 reads.gained(arg);
438 }
439 func.succs_mut(carried.from)[carried.at].args = carried.args;
440 }
441 Ok(touched)
442 }
443
444 fn read_after(&self, func: &mir::Func, reads: &Reads, inst: mir::Inst) -> bool {
451 func[func[inst].operands].iter().filter(|operand| operand.role.is_def()).any(|operand| {
452 let mut left = reads.count(operand.reg);
453 let mut settle = |lost: &[mir::Reg], gained: &[mir::Reg]| {
454 let goes = lost.iter().filter(|&®| reg == operand.reg).count();
455 left = left.saturating_sub(goes);
456 left += gained.iter().filter(|&®| reg == operand.reg).count();
457 };
458 for &(other, ref what) in &self.changes {
459 let (lost, gained) = moved(func, other, what);
460 settle(&lost, &gained);
461 }
462 for carried in &self.carries {
463 settle(&func[carried.from].succs[carried.at].args, &carried.args);
464 }
465 left != 0
466 })
467 }
468}
469
470fn moved(func: &mir::Func, inst: mir::Inst, what: &What) -> (Vec<mir::Reg>, Vec<mir::Reg>) {
477 let held: Vec<mir::Reg> = func[func[inst].operands]
478 .iter()
479 .filter(|operand| operand.role == Role::Use)
480 .map(|operand| operand.reg)
481 .collect();
482 match what {
483 What::Rewrite(plan) => (held, plan.reads().collect()),
484 What::Rename { from, into } => {
485 let gone: Vec<mir::Reg> = held.into_iter().filter(|reg| reg == from).collect();
486 let back = vec![*into; gone.len()];
487 (gone, back)
488 }
489 What::Remove => (held, Vec::new()),
490 }
491}
492
493fn renamed(func: &mir::Func, inst: mir::Inst, from: mir::Reg, into: mir::Reg) -> Option<Refusal> {
500 let class = func.class_of(into)?;
501 func[func[inst].operands]
502 .iter()
503 .any(|operand| operand.role == Role::Use && operand.reg == from && operand.class != class)
504 .then_some(Refusal::Class(inst))
505}
506
507fn shaped(
522 inst: mir::Inst,
523 plan: &Plan,
524 names: &Interner,
525 machine: &MachineInsts,
526) -> Option<Refusal> {
527 let name = names.resolve(plan.opcode.name());
528 let bare = machine.bare(name);
529 let Some(desc) = (machine.operands)(bare) else { return Some(Refusal::Unknown(inst)) };
530 if plan.operands.len() < desc.len() {
531 return Some(Refusal::Operands(inst));
532 }
533 let (described, addressed) = plan.operands.split_at(desc.len());
534 for (operand, want) in described.iter().zip(desc) {
535 let shape = (operand.class, operand.role, operand.constraint);
536 if shape != (want.class, want.role, want.constraint) {
537 return Some(Refusal::Operands(inst));
538 }
539 }
540 if plan.imm.is_some() != (machine.takes_imm)(bare) {
541 return Some(Refusal::Imm(inst));
542 }
543 let Some(amode) = plan.amode else {
544 return ((machine.takes_mem)(bare) || !addressed.is_empty()).then_some(Refusal::Mem(inst));
545 };
546 if !(machine.takes_mem)(bare) {
547 return Some(Refusal::Mem(inst));
548 }
549 let named = [amode.base, amode.index].into_iter().flatten();
550 let mut wanted = 0;
551 for at in named {
552 let Some(operand) = plan.operands.get(usize::from(at)) else {
553 return Some(Refusal::Operands(inst));
554 };
555 if usize::from(at) < desc.len() || operand.role != Role::Use {
556 return Some(Refusal::Operands(inst));
557 }
558 wanted += 1;
559 }
560 if addressed.len() != wanted {
561 return Some(Refusal::Operands(inst));
562 }
563 let scaled = if amode.index.is_some() { amode.scale } else { 1 };
564 if !machine.scales(scaled) || (amode.index.is_none() && amode.scale != 1) {
565 return Some(Refusal::Scale(inst));
566 }
567 if amode.index.is_some() && amode.disp != 0 && !machine.index_and_disp {
568 return Some(Refusal::Crowded(inst));
569 }
570 None
571}
572
573#[cfg(test)]
574mod tests {
575 use rucc_mir::Constraint;
576 use rucc_target::x86_64::{GPR, MACHINE, XMM};
577
578 use super::*;
579
580 fn empty() -> (Interner, mir::Func, mir::Block) {
582 let mut names = Interner::new();
583 let mut func = mir::Func::new(names.intern("f"));
584 let block = func.create_block();
585 (names, func, block)
586 }
587
588 fn op(names: &mut Interner, name: &str) -> mir::Opcode {
590 mir::Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
591 }
592
593 fn refused(func: &mir::Func, names: &Interner, set: &Changes) -> Option<Refusal> {
595 set.refused(func, &Reads::of(func), names, &MACHINE)
596 }
597
598 fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
600 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
601 }
602
603 fn copy(
605 func: &mut mir::Func,
606 names: &mut Interner,
607 block: mir::Block,
608 ) -> (mir::Inst, mir::Reg) {
609 let from = func.new_vreg(GPR);
610 let into = func.new_vreg(GPR);
611 let mov = op(names, "mov_rr_64");
612 (func.build(block, mov).def(into, GPR).uses(from, GPR).finish(), into)
613 }
614
615 #[test]
618 fn a_rewrite_the_target_has_is_taken() {
619 let (mut names, mut func, block) = empty();
620 let (mov, into) = copy(&mut func, &mut names, block);
621 let base = func.new_vreg(GPR);
622 let load = op(&mut names, "mov_rm_64");
623 let mut set = Changes::new();
624 set.rewrite(
625 mov,
626 Plan {
627 opcode: load,
628 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
629 imm: None,
630 amode: Some(mir::Amode {
631 base: Some(1),
632 index: None,
633 scale: 1,
634 disp: 8,
635 symbol: None,
636 block: None,
637 table: None,
638 reach: mir::Reach::Itself,
639 segment: None,
640 }),
641 symbol: None,
642 },
643 );
644
645 let mut reads = Reads::of(&func);
646 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
647
648 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
649 assert_eq!(func[func[mov].mem.expect("the load has an address")].disp, 8);
650 assert_eq!(reads.count(base), 1, "the address register is read now");
651 assert_eq!(reads.count(into), 0, "the register the copy read is read by nothing");
652 }
653
654 #[test]
657 fn an_opcode_this_target_does_not_have_is_refused() {
658 let (mut names, mut func, block) = empty();
659 let (mov, into) = copy(&mut func, &mut names, block);
660 let made_up = op(&mut names, "mov_rr_65");
661 let mut set = Changes::new();
662 set.rewrite(
663 mov,
664 Plan {
665 opcode: made_up,
666 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
667 imm: None,
668 amode: None,
669 symbol: None,
670 },
671 );
672
673 assert_eq!(refused(&func, &names, &set), Some(Refusal::Unknown(mov)));
674 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"], "the function was written to");
675 }
676
677 #[test]
681 fn an_operand_of_the_wrong_class_is_refused() {
682 let (mut names, mut func, block) = empty();
683 let (mov, into) = copy(&mut func, &mut names, block);
684 let float = func.new_vreg(XMM);
685 let same = func[mov].opcode;
686 let mut set = Changes::new();
687 set.rewrite(
688 mov,
689 Plan {
690 opcode: same,
691 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(float, XMM)],
692 imm: None,
693 amode: None,
694 symbol: None,
695 },
696 );
697
698 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
699 }
700
701 #[test]
705 fn an_add_whose_answer_is_not_tied_to_its_source_is_refused() {
706 let (mut names, mut func, block) = empty();
707 let (mov, into) = copy(&mut func, &mut names, block);
708 let other = func.new_vreg(GPR);
709 let add = op(&mut names, "add_rr_64");
710 let loose = vec![
711 mir::Operand::write(into, GPR),
712 mir::Operand::read(into, GPR),
713 mir::Operand::read(other, GPR),
714 ];
715 let mut set = Changes::new();
716 set.rewrite(
717 mov,
718 Plan { opcode: add, operands: loose.clone(), imm: None, amode: None, symbol: None },
719 );
720 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
721
722 let mut tied = loose;
723 tied[0] = tied[0].with(Constraint::Reuse(1));
724 let mut set = Changes::new();
725 set.rewrite(
726 mov,
727 Plan { opcode: add, operands: tied, imm: None, amode: None, symbol: None },
728 );
729 assert_eq!(refused(&func, &names, &set), None, "the same instruction written properly");
730 }
731
732 #[test]
735 fn an_immediate_has_to_be_there_exactly_when_the_instruction_carries_one() {
736 let (mut names, mut func, block) = empty();
737 let (mov, into) = copy(&mut func, &mut names, block);
738 let same = func[mov].opcode;
739 let add = op(&mut names, "add_ri_64");
740 let mut set = Changes::new();
741 set.rewrite(
742 mov,
743 Plan {
744 opcode: same,
745 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
746 imm: Some(7),
747 amode: None,
748 symbol: None,
749 },
750 );
751 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "a copy of seven");
752
753 let tied = vec![
754 mir::Operand::write(into, GPR).with(Constraint::Reuse(1)),
755 mir::Operand::read(into, GPR),
756 ];
757 let mut set = Changes::new();
758 set.rewrite(
759 mov,
760 Plan { opcode: add, operands: tied.clone(), imm: None, amode: None, symbol: None },
761 );
762 assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "an add of nothing");
763
764 let mut set = Changes::new();
765 set.rewrite(
766 mov,
767 Plan { opcode: add, operands: tied, imm: Some(7), amode: None, symbol: None },
768 );
769 assert_eq!(refused(&func, &names, &set), None);
770 }
771
772 #[test]
775 fn an_address_on_an_instruction_that_has_none_is_refused() {
776 let (mut names, mut func, block) = empty();
777 let (mov, into) = copy(&mut func, &mut names, block);
778 let base = func.new_vreg(GPR);
779 let same = func[mov].opcode;
780 let mut set = Changes::new();
781 set.rewrite(
782 mov,
783 Plan {
784 opcode: same,
785 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
786 imm: None,
787 amode: Some(mir::Amode {
788 base: Some(1),
789 index: None,
790 scale: 1,
791 disp: 0,
792 symbol: None,
793 block: None,
794 table: None,
795 reach: mir::Reach::Itself,
796 segment: None,
797 }),
798 symbol: None,
799 },
800 );
801
802 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
803 }
804
805 #[test]
807 fn an_instruction_that_wants_an_address_and_has_none_is_refused() {
808 let (mut names, mut func, block) = empty();
809 let (mov, into) = copy(&mut func, &mut names, block);
810 let load = op(&mut names, "mov_rm_64");
811 let mut set = Changes::new();
812 set.rewrite(
813 mov,
814 Plan {
815 opcode: load,
816 operands: vec![mir::Operand::write(into, GPR)],
817 imm: None,
818 amode: None,
819 symbol: None,
820 },
821 );
822
823 assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
824 }
825
826 #[test]
830 fn an_index_scaled_by_something_this_machine_cannot_write_is_refused() {
831 let (mut names, mut func, block) = empty();
832 let (mov, into) = copy(&mut func, &mut names, block);
833 let base = func.new_vreg(GPR);
834 let index = func.new_vreg(GPR);
835 let load = op(&mut names, "mov_rm_64");
836 let scaled = |scale| Plan {
837 opcode: load,
838 operands: vec![
839 mir::Operand::write(into, GPR),
840 mir::Operand::read(base, GPR),
841 mir::Operand::read(index, GPR),
842 ],
843 imm: None,
844 amode: Some(mir::Amode {
845 base: Some(1),
846 index: Some(2),
847 scale,
848 disp: 0,
849 symbol: None,
850 block: None,
851 table: None,
852 reach: mir::Reach::Itself,
853 segment: None,
854 }),
855 symbol: None,
856 };
857 let mut set = Changes::new();
858 set.rewrite(mov, scaled(3));
859 assert_eq!(refused(&func, &names, &set), Some(Refusal::Scale(mov)));
860
861 let mut set = Changes::new();
862 set.rewrite(mov, scaled(4));
863 assert_eq!(refused(&func, &names, &set), None);
864 }
865
866 #[test]
869 fn an_index_beside_a_displacement_is_refused_where_the_machine_has_no_such_mode() {
870 use rucc_target::aarch64;
871
872 let (mut names, mut func, block) = empty();
873 let into = func.new_vreg(aarch64::GPR);
874 let base = func.new_vreg(aarch64::GPR);
875 let index = func.new_vreg(aarch64::GPR);
876 let load = mir::Opcode::new(names.intern("a64.ldr_64"));
877 let mov = func.build(block, load).def(into, aarch64::GPR).uses(base, aarch64::GPR).finish();
878 let plan = |disp| Plan {
879 opcode: load,
880 operands: vec![
881 mir::Operand::write(into, aarch64::GPR),
882 mir::Operand::read(base, aarch64::GPR),
883 mir::Operand::read(index, aarch64::GPR),
884 ],
885 imm: None,
886 amode: Some(mir::Amode { base: Some(1), index: Some(2), disp, ..mir::Amode::NOTHING }),
887 symbol: None,
888 };
889 let asked = |set: &Changes, func: &mir::Func| {
890 set.refused(func, &Reads::of(func), &names, &aarch64::MACHINE)
891 };
892 let mut set = Changes::new();
893 set.rewrite(mov, plan(16));
894 assert_eq!(asked(&set, &func), Some(Refusal::Crowded(mov)));
895
896 let mut set = Changes::new();
897 set.rewrite(mov, plan(0));
898 assert_eq!(asked(&set, &func), None);
899 }
900
901 #[test]
905 fn an_address_pointing_at_an_operand_of_its_own_instruction_is_refused() {
906 let (mut names, mut func, block) = empty();
907 let (mov, into) = copy(&mut func, &mut names, block);
908 let load = op(&mut names, "mov_rm_64");
909 let mut set = Changes::new();
910 set.rewrite(
911 mov,
912 Plan {
913 opcode: load,
914 operands: vec![mir::Operand::write(into, GPR)],
915 imm: None,
916 amode: Some(mir::Amode {
917 base: Some(0),
918 index: None,
919 scale: 1,
920 disp: 0,
921 symbol: None,
922 block: None,
923 table: None,
924 reach: mir::Reach::Itself,
925 segment: None,
926 }),
927 symbol: None,
928 },
929 );
930
931 assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
932 }
933
934 #[test]
938 fn removing_an_instruction_whose_answer_is_still_read_is_refused() {
939 let (mut names, mut func, block) = empty();
940 let (mov, into) = copy(&mut func, &mut names, block);
941 let out = func.new_vreg(GPR);
942 let second = func[mov].opcode;
943 func.build(block, second).def(out, GPR).uses(into, GPR).finish();
944 let mut set = Changes::new();
945 set.remove(mov);
946
947 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
948 assert_eq!(shape(&func, &names, block).len(), 2);
949 }
950
951 #[test]
955 fn removing_it_in_a_set_that_takes_away_the_reader_is_taken() {
956 let (mut names, mut func, block) = empty();
957 let base = func.new_vreg(GPR);
958 let address = func.new_vreg(GPR);
959 let out = func.new_vreg(GPR);
960 let lea = op(&mut names, "lea_64");
961 let load = op(&mut names, "mov_rm_64");
962 let at = |reg| mir::Mem { disp: 16, ..mir::Mem::at(mir::Operand::read(reg, GPR)) };
963 let made = func.build(block, lea).def(address, GPR).mem(at(base)).finish();
964 let read = func
965 .build(block, load)
966 .def(out, GPR)
967 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
968 .finish();
969
970 let mut set = Changes::new();
971 set.rewrite(
972 read,
973 Plan {
974 opcode: load,
975 operands: vec![mir::Operand::write(out, GPR), mir::Operand::read(base, GPR)],
976 imm: None,
977 amode: Some(mir::Amode { disp: 16, ..func[func[read].mem.expect("a load")] }),
978 symbol: None,
979 },
980 );
981 set.remove(made);
982
983 let mut reads = Reads::of(&func);
984 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
985
986 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
987 assert_eq!(reads.count(address), 0, "nothing reads the address the lea wrote");
988 assert_eq!(reads.count(base), 1, "the load reads what the lea read");
989 }
990
991 #[test]
994 fn a_rename_that_takes_the_last_reader_off_an_instruction_lets_it_go() {
995 let (mut names, mut func, block) = empty();
996 let (first, into) = copy(&mut func, &mut names, block);
997 let source = func[func[first].operands][1].reg;
998 let out = func.new_vreg(GPR);
999 let mov = func[first].opcode;
1000 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1001
1002 let mut set = Changes::new();
1003 set.rename(second, into, source);
1004 set.remove(first);
1005 let mut reads = Reads::of(&func);
1006 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
1007
1008 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1009 assert_eq!(func[func[second].operands][1].reg, source, "the reader was not sent on");
1010 assert_eq!(reads.count(into), 0, "nothing reads what the copy wrote");
1011 assert_eq!(reads.count(source), 1, "the reader reads what the copy read");
1012 }
1013
1014 #[test]
1016 fn a_removal_whose_reader_is_not_renamed_is_refused() {
1017 let (mut names, mut func, block) = empty();
1018 let (first, into) = copy(&mut func, &mut names, block);
1019 let out = func.new_vreg(GPR);
1020 let mov = func[first].opcode;
1021 func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1022
1023 let mut set = Changes::new();
1024 set.remove(first);
1025 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(first)));
1026 }
1027
1028 #[test]
1032 fn a_rename_into_a_register_of_another_class_is_refused() {
1033 let (mut names, mut func, block) = empty();
1034 let (mov, _) = copy(&mut func, &mut names, block);
1035 let source = func[func[mov].operands][1].reg;
1036 let float = func.new_vreg(XMM);
1037
1038 let mut set = Changes::new();
1039 set.rename(mov, source, float);
1040 assert_eq!(refused(&func, &names, &set), Some(Refusal::Class(mov)));
1041 }
1042
1043 #[test]
1046 fn an_edge_carries_what_the_set_says_and_the_instruction_it_read_goes() {
1047 let (mut names, mut func, block) = empty();
1048 let next = func.create_block();
1049 let (mov, into) = copy(&mut func, &mut names, block);
1050 let source = func[func[mov].operands][1].reg;
1051 let arrived = func.new_vreg(GPR);
1052 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1053 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1054
1055 let mut set = Changes::new();
1056 set.carry(block, 0, vec![source]);
1057 set.remove(mov);
1058 let mut reads = Reads::of(&func);
1059 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
1060
1061 assert!(shape(&func, &names, block).is_empty(), "the copy is still there");
1062 assert_eq!(func[block].succs[0].args, vec![source]);
1063 assert_eq!(reads.count(into), 0);
1064 assert_eq!(reads.count(source), 1, "the edge reads what the copy read");
1065 }
1066
1067 #[test]
1070 fn an_edge_that_is_not_there_is_refused() {
1071 let (mut names, mut func, block) = empty();
1072 copy(&mut func, &mut names, block);
1073
1074 let mut set = Changes::new();
1075 set.carry(block, 0, Vec::new());
1076 assert_eq!(refused(&func, &names, &set), Some(Refusal::Edge(block, 0)));
1077 }
1078
1079 #[test]
1083 fn an_edge_carrying_the_wrong_number_of_values_is_refused() {
1084 let (mut names, mut func, block) = empty();
1085 let next = func.create_block();
1086 let (_, into) = copy(&mut func, &mut names, block);
1087 let arrived = func.new_vreg(GPR);
1088 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1089 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1090
1091 let mut set = Changes::new();
1092 set.carry(block, 0, vec![into, into]);
1093 assert_eq!(refused(&func, &names, &set), Some(Refusal::Args(block, 0)));
1094 }
1095
1096 #[test]
1099 fn an_answer_an_edge_carries_keeps_its_instruction() {
1100 let (mut names, mut func, block) = empty();
1101 let next = func.create_block();
1102 let (mov, into) = copy(&mut func, &mut names, block);
1103 let arrived = func.new_vreg(GPR);
1104 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1105 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1106 let mut set = Changes::new();
1107 set.remove(mov);
1108
1109 assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
1110 }
1111
1112 #[test]
1116 fn an_instruction_named_twice_is_refused() {
1117 let (mut names, mut func, block) = empty();
1118 let (mov, _) = copy(&mut func, &mut names, block);
1119 let mut set = Changes::new();
1120 set.rewrite(mov, Plan::of(&func, mov));
1121 set.remove(mov);
1122
1123 assert_eq!(refused(&func, &names, &set), Some(Refusal::Twice(mov)));
1124 }
1125
1126 #[test]
1129 fn an_instruction_that_has_already_gone_is_refused() {
1130 let (mut names, mut func, block) = empty();
1131 let (mov, _) = copy(&mut func, &mut names, block);
1132 let plan = Plan::of(&func, mov);
1133 func.remove_inst(mov);
1134 let mut set = Changes::new();
1135 set.rewrite(mov, plan);
1136
1137 assert_eq!(refused(&func, &names, &set), Some(Refusal::Gone(mov)));
1138 }
1139
1140 #[test]
1143 fn the_instruction_as_it_stands_is_a_proposal_the_target_takes() {
1144 let (mut names, mut func, block) = empty();
1145 let base = func.new_vreg(GPR);
1146 let out = func.new_vreg(GPR);
1147 let load = op(&mut names, "mov_rm_64");
1148 let read = func
1149 .build(block, load)
1150 .def(out, GPR)
1151 .mem(mir::Mem { disp: 24, ..mir::Mem::at(mir::Operand::read(base, GPR)) })
1152 .finish();
1153
1154 let plan = Plan::of(&func, read);
1155 let mut set = Changes::new();
1156 set.rewrite(read, plan.clone());
1157 let mut reads = Reads::of(&func);
1158 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1159
1160 assert_eq!(Plan::of(&func, read), plan);
1161 assert_eq!(reads.count(base), 1, "the one read it had before");
1162 }
1163
1164 #[test]
1167 fn a_set_with_one_bad_change_in_it_leaves_the_others_alone() {
1168 let (mut names, mut func, block) = empty();
1169 let (first, into) = copy(&mut func, &mut names, block);
1170 let (second, _) = copy(&mut func, &mut names, block);
1171 let load = op(&mut names, "mov_rm_64");
1172 let mut set = Changes::new();
1173 set.rewrite(
1174 first,
1175 Plan {
1176 opcode: load,
1177 operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
1178 imm: None,
1179 amode: Some(mir::Amode {
1180 base: Some(1),
1181 index: None,
1182 scale: 1,
1183 disp: 0,
1184 symbol: None,
1185 block: None,
1186 table: None,
1187 reach: mir::Reach::Itself,
1188 segment: None,
1189 }),
1190 symbol: None,
1191 },
1192 );
1193 set.rewrite(second, Plan { imm: Some(3), ..Plan::of(&func, second) });
1194
1195 let mut reads = Reads::of(&func);
1196 assert_eq!(
1197 set.commit(&mut func, &mut reads, &names, &MACHINE),
1198 Err(Refusal::Imm(second)),
1199 "the second change is the one the target turns down"
1200 );
1201 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.mov_rr_64"]);
1202 }
1203
1204 #[test]
1206 fn a_set_with_nothing_in_it_commits() {
1207 let (mut names, mut func, block) = empty();
1208 copy(&mut func, &mut names, block);
1209 let set = Changes::new();
1210 assert!(set.is_empty());
1211
1212 let mut reads = Reads::of(&func);
1213 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(0));
1214 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1215 }
1216
1217 #[test]
1221 fn the_counts_are_still_right_after_a_commit() {
1222 let (mut names, mut func, block) = empty();
1223 let (first, into) = copy(&mut func, &mut names, block);
1224 let out = func.new_vreg(GPR);
1225 let mov = func[first].opcode;
1226 let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1227
1228 let mut reads = Reads::of(&func);
1229 assert_eq!(reads.count(into), 1);
1230 let mut set = Changes::new();
1231 set.remove(second);
1232 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1233 assert_eq!(reads.count(into), 0, "the reader went and the count went with it");
1234
1235 let mut set = Changes::new();
1236 set.remove(first);
1237 assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1238 assert!(shape(&func, &names, block).is_empty());
1239 }
1240}