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