1use rucc_base::Interner;
220use rucc_mir::{Amode, Func, Inst, Opcode, Operand, Reg};
221use rucc_target::MachineInsts;
222
223use crate::changes::{Changes, Plan, Reads};
224use crate::fold::Pending;
225
226pub const WINDOW: usize = 16;
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq)]
252pub struct Fold {
253 pub from: &'static str,
255 pub into: &'static str,
257 pub load: &'static str,
259 pub commutes: bool,
261}
262
263pub static FOLDS: &[Fold] = &[
269 Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", commutes: true },
270 Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", commutes: true },
271 Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", commutes: true },
272 Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", commutes: true },
273 Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", commutes: false },
274 Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", commutes: false },
275 Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", commutes: false },
276 Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", commutes: false },
277 Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", commutes: true },
278 Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", commutes: true },
279 Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", commutes: true },
280 Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", commutes: true },
281 Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", commutes: true },
282 Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", commutes: true },
283 Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", commutes: true },
284 Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", commutes: true },
285 Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", commutes: true },
286 Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", commutes: true },
287 Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", commutes: true },
288 Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", commutes: true },
289 Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", commutes: true },
290 Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", commutes: true },
291 Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", commutes: true },
292];
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq)]
301pub struct Update {
302 pub from: &'static str,
304 pub into: &'static str,
306 pub load: &'static str,
308 pub store: &'static str,
310 pub commutes: bool,
312}
313
314pub static UPDATES: &[Update] = &[
325 Update {
326 from: "add_rr_8",
327 into: "add_mr_8",
328 load: "mov_rm_8",
329 store: "mov_mr_8",
330 commutes: true,
331 },
332 Update {
333 from: "add_rr_16",
334 into: "add_mr_16",
335 load: "mov_rm_16",
336 store: "mov_mr_16",
337 commutes: true,
338 },
339 Update {
340 from: "add_rr_32",
341 into: "add_mr_32",
342 load: "mov_rm_32",
343 store: "mov_mr_32",
344 commutes: true,
345 },
346 Update {
347 from: "add_rr_64",
348 into: "add_mr_64",
349 load: "mov_rm_64",
350 store: "mov_mr_64",
351 commutes: true,
352 },
353 Update {
354 from: "sub_rr_8",
355 into: "sub_mr_8",
356 load: "mov_rm_8",
357 store: "mov_mr_8",
358 commutes: false,
359 },
360 Update {
361 from: "sub_rr_16",
362 into: "sub_mr_16",
363 load: "mov_rm_16",
364 store: "mov_mr_16",
365 commutes: false,
366 },
367 Update {
368 from: "sub_rr_32",
369 into: "sub_mr_32",
370 load: "mov_rm_32",
371 store: "mov_mr_32",
372 commutes: false,
373 },
374 Update {
375 from: "sub_rr_64",
376 into: "sub_mr_64",
377 load: "mov_rm_64",
378 store: "mov_mr_64",
379 commutes: false,
380 },
381 Update {
382 from: "and_rr_8",
383 into: "and_mr_8",
384 load: "mov_rm_8",
385 store: "mov_mr_8",
386 commutes: true,
387 },
388 Update {
389 from: "and_rr_16",
390 into: "and_mr_16",
391 load: "mov_rm_16",
392 store: "mov_mr_16",
393 commutes: true,
394 },
395 Update {
396 from: "and_rr_32",
397 into: "and_mr_32",
398 load: "mov_rm_32",
399 store: "mov_mr_32",
400 commutes: true,
401 },
402 Update {
403 from: "and_rr_64",
404 into: "and_mr_64",
405 load: "mov_rm_64",
406 store: "mov_mr_64",
407 commutes: true,
408 },
409 Update {
410 from: "or_rr_8",
411 into: "or_mr_8",
412 load: "mov_rm_8",
413 store: "mov_mr_8",
414 commutes: true,
415 },
416 Update {
417 from: "or_rr_16",
418 into: "or_mr_16",
419 load: "mov_rm_16",
420 store: "mov_mr_16",
421 commutes: true,
422 },
423 Update {
424 from: "or_rr_32",
425 into: "or_mr_32",
426 load: "mov_rm_32",
427 store: "mov_mr_32",
428 commutes: true,
429 },
430 Update {
431 from: "or_rr_64",
432 into: "or_mr_64",
433 load: "mov_rm_64",
434 store: "mov_mr_64",
435 commutes: true,
436 },
437 Update {
438 from: "xor_rr_8",
439 into: "xor_mr_8",
440 load: "mov_rm_8",
441 store: "mov_mr_8",
442 commutes: true,
443 },
444 Update {
445 from: "xor_rr_16",
446 into: "xor_mr_16",
447 load: "mov_rm_16",
448 store: "mov_mr_16",
449 commutes: true,
450 },
451 Update {
452 from: "xor_rr_32",
453 into: "xor_mr_32",
454 load: "mov_rm_32",
455 store: "mov_mr_32",
456 commutes: true,
457 },
458 Update {
459 from: "xor_rr_64",
460 into: "xor_mr_64",
461 load: "mov_rm_64",
462 store: "mov_mr_64",
463 commutes: true,
464 },
465];
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub struct Bump {
477 pub from: &'static str,
479 pub into: &'static str,
481 pub load: &'static str,
483 pub store: &'static str,
485}
486
487pub static BUMPS: &[Bump] = &[
502 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
503 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
504 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
505 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
506 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
507 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
508 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
509 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
510 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
511 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
512 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
513 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
514 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
515 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
516 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
517 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
518 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
519 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
520 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
521 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
522];
523
524#[derive(Debug, Clone, Copy)]
529struct Waiting {
530 inst: Inst,
532 reg: Reg,
534 load: &'static str,
536 at: usize,
538}
539
540pub fn loads(
551 func: &mut Func,
552 machine: &MachineInsts,
553 names: &mut Interner,
554 pending: &mut Pending<'_>,
555) -> usize {
556 let mut reads = Reads::of(func);
557 let mut done = 0;
558 for block in func.blocks().collect::<Vec<_>>() {
559 let mut waiting: Option<Waiting> = None;
560 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
561 let name = names.resolve(func[inst].opcode.name()).to_owned();
562 let bare = machine.bare(&name).to_owned();
563 let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
569 if let Some(carried) = waiting {
570 if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
571 let mut set = Changes::new();
572 set.rewrite(inst, plan);
573 set.remove(carried.inst);
574 if set.commit(func, &mut reads, names, machine).is_ok() {
575 pending.moved(carried.inst, &[inst]);
576 waiting = None;
577 done += 1;
578 }
579 }
580 }
581 if barrier {
582 waiting = None;
583 }
584 if let Some(carried) = waiting {
585 if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
586 waiting = None;
587 }
588 }
589 if let Some(load) = FOLDS.iter().find(|fold| fold.load == bare).map(|fold| fold.load) {
590 let operands = &func[func[inst].operands];
591 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
592 waiting = Some(Waiting { inst, reg: first.reg, load, at });
593 }
594 }
595 }
596 }
597 done
598}
599
600#[derive(Debug, Clone, Copy)]
602struct Run {
603 load: Inst,
605 alu: Inst,
607 store: Inst,
609 update: &'static Update,
611 kept: Operand,
613}
614
615#[derive(Debug, Clone, Copy)]
621struct Bumped {
622 load: Inst,
624 alu: Inst,
626 store: Inst,
628 bump: &'static Bump,
630 imm: i64,
632}
633
634pub fn stores(
651 func: &mut Func,
652 machine: &MachineInsts,
653 names: &mut Interner,
654 pending: &mut Pending<'_>,
655) -> usize {
656 let mut reads = Reads::of(func);
657 let mut done = 0;
658 for block in func.blocks().collect::<Vec<_>>() {
659 let insts: Vec<Inst> = func.insts(block).collect();
660 for at in 0..insts.len() {
661 let found = match run(func, &reads, machine, names, &insts, at) {
662 Some(found) => Some((
663 found.load,
664 found.alu,
665 found.store,
666 updated(func, machine, names, &found),
667 )),
668 None => constant(func, &reads, machine, names, &insts, at).map(|found| {
669 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
670 }),
671 };
672 let Some((load, alu, store, plan)) = found else { continue };
673 if !pending.alike(load, store) {
674 continue;
675 }
676 let mut set = Changes::new();
677 set.rewrite(store, plan);
678 set.remove(alu);
679 set.remove(load);
680 if set.commit(func, &mut reads, names, machine).is_ok() {
681 pending.moved(load, &[]);
682 done += 1;
683 }
684 }
685 }
686 done
687}
688
689fn run(
701 func: &Func,
702 reads: &Reads,
703 machine: &MachineInsts,
704 names: &Interner,
705 insts: &[Inst],
706 at: usize,
707) -> Option<Run> {
708 let store = insts[at];
709 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
710 let value = *func[func[store].operands].first()?;
711 if value.role.is_def() || reads.count(value.reg) != 1 {
712 return None;
713 }
714 let earliest = at.saturating_sub(WINDOW);
717 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
718 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
719 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
720 let operands = func[func[insts[alu]].operands].to_vec();
721 let [_, first, second] = operands[..] else { return None };
722 let both = [(first, second), (second, first)];
727 let tried = if update.commutes { &both[..] } else { &both[..1] };
728 for &(source, kept) in tried {
729 if reads.count(source.reg) != 1 {
730 continue;
731 }
732 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
733 continue;
734 };
735 let load = insts[from];
736 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
737 continue;
738 }
739 if !same_place(func, load, store) {
740 continue;
741 }
742 let mut wanted: Vec<Reg> =
747 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
748 wanted.push(kept.reg);
749 if !clear(func, machine, names, insts, (from, at), &wanted) {
750 continue;
751 }
752 return Some(Run { load, alu: insts[alu], store, update, kept });
753 }
754 None
755}
756
757fn constant(
771 func: &Func,
772 reads: &Reads,
773 machine: &MachineInsts,
774 names: &Interner,
775 insts: &[Inst],
776 at: usize,
777) -> Option<Bumped> {
778 let store = insts[at];
779 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
780 let value = *func[func[store].operands].first()?;
781 if value.role.is_def() || reads.count(value.reg) != 1 {
782 return None;
783 }
784 let mem = func[func[store].mem?];
785 if mem.base == Some(0) || mem.index == Some(0) {
786 return None;
787 }
788 let earliest = at.saturating_sub(WINDOW);
789 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
790 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
791 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
792 let operands = func[func[insts[alu]].operands].to_vec();
793 let [_, source] = operands[..] else { return None };
794 let imm = func[func[insts[alu]].imm?].0;
795 if reads.count(source.reg) != 1 {
796 return None;
797 }
798 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
799 let load = insts[from];
800 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
801 return None;
802 }
803 if !same_place(func, load, store) {
804 return None;
805 }
806 let wanted: Vec<Reg> =
809 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
810 if !clear(func, machine, names, insts, (from, at), &wanted) {
811 return None;
812 }
813 Some(Bumped { load, alu: insts[alu], store, bump, imm })
814}
815
816fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
818 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
819}
820
821fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
828 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
829 let (here, there) = (func[here], func[there]);
830 if func[one].symbol != func[other].symbol {
831 return false;
832 }
833 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
834 if bare(here) != bare(there) {
835 return false;
836 }
837 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
838 (None, None) => true,
839 (Some(left), Some(right)) => {
840 func[func[one].operands][usize::from(left)].reg
841 == func[func[other].operands][usize::from(right)].reg
842 }
843 _ => false,
844 };
845 same(here.base, there.base) && same(here.index, there.index)
846}
847
848fn clear(
855 func: &Func,
856 machine: &MachineInsts,
857 names: &Interner,
858 insts: &[Inst],
859 span: (usize, usize),
860 wanted: &[Reg],
861) -> bool {
862 let (from, to) = span;
863 insts[from + 1..to].iter().all(|&inst| {
864 let name = names.resolve(func[inst].opcode.name());
865 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
866 return false;
867 }
868 !func[func[inst].operands]
869 .iter()
870 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
871 })
872}
873
874fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
880 let operands = func[func[run.store].operands].to_vec();
881 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
882 Plan {
883 opcode: Opcode::new(into),
884 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
885 imm: None,
886 amode: func[run.store].mem.map(|mem| func[mem]),
887 symbol: func[run.store].symbol,
888 }
889}
890
891fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
899 let operands = func[func[run.store].operands][1..].to_vec();
900 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
901 let back = |at: Option<u8>| at.map(|at| at - 1);
902 Plan {
903 opcode: Opcode::new(into),
904 operands,
905 imm: Some(run.imm),
906 amode: func[run.store].mem.map(|mem| {
907 let mem = func[mem];
908 Amode { base: back(mem.base), index: back(mem.index), ..mem }
909 }),
910 symbol: func[run.store].symbol,
911 }
912}
913
914fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
920 let written: Vec<Reg> = func[func[inst].operands]
921 .iter()
922 .filter(|operand| operand.role.is_def())
923 .map(|operand| operand.reg)
924 .collect();
925 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
926}
927
928fn joined(
933 func: &Func,
934 reads: &Reads,
935 carried: Waiting,
936 machine: &MachineInsts,
937 names: &mut Interner,
938 inst: Inst,
939 bare: &str,
940) -> Option<Plan> {
941 let fold = FOLDS.iter().find(|fold| fold.from == bare)?;
942 if carried.load != fold.load || reads.count(carried.reg) != 1 {
943 return None;
944 }
945 let operands = func[func[inst].operands].to_vec();
946 let [answer, first, second] = operands[..] else { return None };
947 let kept = if second.reg == carried.reg {
951 first
952 } else if fold.commutes && first.reg == carried.reg {
953 second
954 } else {
955 return None;
956 };
957 let load = carried.inst;
958 let address = func[func[load].operands][1..].to_vec();
959 let mut amode = func[func[load].mem?];
960 amode.base = amode.base.map(|at| at + 1);
964 amode.index = amode.index.map(|at| at + 1);
965 let into = names.intern(&format!("{}{}", machine.prefix, fold.into));
966 Some(Plan {
967 opcode: Opcode::new(into),
968 operands: [answer, kept].into_iter().chain(address).collect(),
969 imm: None,
970 amode: Some(amode),
971 symbol: func[load].symbol,
972 })
973}
974
975#[cfg(test)]
976mod tests {
977 use rucc_mir::{self as mir, Constraint, Mem, Operand};
978 use rucc_target::x86_64::{GPR, MACHINE};
979
980 use super::*;
981
982 fn empty() -> (Interner, Func, mir::Block) {
984 let mut names = Interner::new();
985 let mut func = Func::new(names.intern("f"));
986 let block = func.create_block();
987 (names, func, block)
988 }
989
990 fn op(names: &mut Interner, name: &str) -> Opcode {
992 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
993 }
994
995 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
997 let into = func.new_vreg(GPR);
998 let mov = op(names, "mov_rm_64");
999 func.build(block, mov)
1000 .def(into, GPR)
1001 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1002 .finish();
1003 into
1004 }
1005
1006 fn alu(
1008 func: &mut Func,
1009 names: &mut Interner,
1010 block: mir::Block,
1011 name: &str,
1012 first: Reg,
1013 second: Reg,
1014 ) -> Reg {
1015 let answer = func.new_vreg(GPR);
1016 let opcode = op(names, name);
1017 func.build(block, opcode)
1018 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1019 .uses(first, GPR)
1020 .uses(second, GPR)
1021 .finish();
1022 answer
1023 }
1024
1025 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1027 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1028 }
1029
1030 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1032 let mut addresses = Vec::new();
1033 let mut arguments = Vec::new();
1034 let mut dynamic = Vec::new();
1035 let mut pending =
1036 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1037 loads(func, &MACHINE, names, &mut pending)
1038 }
1039
1040 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1042 let mov = op(names, "mov_mr_64");
1043 func.build(block, mov)
1044 .uses(value, GPR)
1045 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1046 .finish();
1047 }
1048
1049 fn update(func: &mut Func, names: &mut Interner) -> usize {
1051 let mut addresses = Vec::new();
1052 let mut arguments = Vec::new();
1053 let mut dynamic = Vec::new();
1054 let mut pending =
1055 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1056 stores(func, &MACHINE, names, &mut pending)
1057 }
1058
1059 #[test]
1061 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1062 let (mut names, mut func, block) = empty();
1063 let base = func.new_vreg(GPR);
1064 let other = func.new_vreg(GPR);
1065 let word = load(&mut func, &mut names, block, base);
1066 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1067 store(&mut func, &mut names, block, base, sum);
1068
1069 assert_eq!(update(&mut func, &mut names), 1);
1070 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1071 let inst = func.insts(block).next().expect("the addition");
1072 let mem = func[inst].mem.expect("it writes memory");
1073 assert_eq!(func[mem].disp, 16, "the address came from the store");
1074 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1075 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1076 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1077 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1078 }
1079
1080 #[test]
1084 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1085 let (mut names, mut func, block) = empty();
1086 let base = func.new_vreg(GPR);
1087 let other = func.new_vreg(GPR);
1088 let word = load(&mut func, &mut names, block, base);
1089 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1090 store(&mut func, &mut names, block, base, sum);
1091
1092 assert_eq!(update(&mut func, &mut names), 1);
1093 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1094 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1095 }
1096
1097 #[test]
1100 fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1101 let (mut names, mut func, block) = empty();
1102 let base = func.new_vreg(GPR);
1103 let other = func.new_vreg(GPR);
1104 let word = load(&mut func, &mut names, block, base);
1105 let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1106 store(&mut func, &mut names, block, base, left);
1107
1108 assert_eq!(update(&mut func, &mut names), 1);
1109 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1110 }
1111
1112 #[test]
1115 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1116 let (mut names, mut func, block) = empty();
1117 let base = func.new_vreg(GPR);
1118 let other = func.new_vreg(GPR);
1119 let word = load(&mut func, &mut names, block, base);
1120 let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1121 store(&mut func, &mut names, block, base, left);
1122
1123 assert_eq!(update(&mut func, &mut names), 0);
1124 assert_eq!(
1125 shape(&func, &names, block),
1126 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1127 );
1128 }
1129
1130 #[test]
1133 fn a_store_to_another_address_stays_three_instructions() {
1134 let (mut names, mut func, block) = empty();
1135 let base = func.new_vreg(GPR);
1136 let elsewhere = func.new_vreg(GPR);
1137 let other = func.new_vreg(GPR);
1138 let word = load(&mut func, &mut names, block, base);
1139 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1140 store(&mut func, &mut names, block, elsewhere, sum);
1141
1142 assert_eq!(update(&mut func, &mut names), 0);
1143 }
1144
1145 #[test]
1148 fn a_store_at_another_displacement_stays_three_instructions() {
1149 let (mut names, mut func, block) = empty();
1150 let base = func.new_vreg(GPR);
1151 let other = func.new_vreg(GPR);
1152 let word = load(&mut func, &mut names, block, base);
1153 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1154 let mov = op(&mut names, "mov_mr_64");
1155 func.build(block, mov)
1156 .uses(sum, GPR)
1157 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1158 .finish();
1159
1160 assert_eq!(update(&mut func, &mut names), 0);
1161 }
1162
1163 #[test]
1166 fn a_word_two_instructions_read_stays_three_instructions() {
1167 let (mut names, mut func, block) = empty();
1168 let base = func.new_vreg(GPR);
1169 let other = func.new_vreg(GPR);
1170 let word = load(&mut func, &mut names, block, base);
1171 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1172 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1173 store(&mut func, &mut names, block, base, sum);
1174
1175 assert_eq!(update(&mut func, &mut names), 0);
1176 }
1177
1178 #[test]
1181 fn an_answer_something_else_reads_stays_three_instructions() {
1182 let (mut names, mut func, block) = empty();
1183 let base = func.new_vreg(GPR);
1184 let other = func.new_vreg(GPR);
1185 let word = load(&mut func, &mut names, block, base);
1186 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1187 store(&mut func, &mut names, block, base, sum);
1188 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1189
1190 assert_eq!(update(&mut func, &mut names), 0);
1191 }
1192
1193 #[test]
1196 fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1197 let (mut names, mut func, block) = empty();
1198 let base = func.new_vreg(GPR);
1199 let other = func.new_vreg(GPR);
1200 let word = load(&mut func, &mut names, block, base);
1201 load(&mut func, &mut names, block, other);
1202 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1203 store(&mut func, &mut names, block, base, sum);
1204
1205 assert_eq!(update(&mut func, &mut names), 0);
1206 }
1207
1208 #[test]
1211 fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1212 let (mut names, mut func, block) = empty();
1213 let base = Reg::physical(rucc_target::x86_64::RSP);
1214 let other = func.new_vreg(GPR);
1215 let word = load(&mut func, &mut names, block, base);
1216 let sub = op(&mut names, "sub_ri_64");
1217 func.build(block, sub)
1218 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1219 .uses(base, GPR)
1220 .imm(32)
1221 .finish();
1222 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1223 store(&mut func, &mut names, block, base, sum);
1224
1225 assert_eq!(update(&mut func, &mut names), 0);
1226 }
1227
1228 #[test]
1232 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1233 let (mut names, mut func, block) = empty();
1234 let base = Reg::physical(rucc_target::x86_64::RSP);
1235 let other = func.new_vreg(GPR);
1236 let mov = op(&mut names, "mov_rm_64");
1237 let word = func.new_vreg(GPR);
1238 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1239 let read = func.insts(block).next().expect("the load");
1240 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1241 let put = op(&mut names, "mov_mr_64");
1242 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1243 let written = func.insts(block).nth(2).expect("the store");
1244
1245 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1246 let mut arguments = Vec::new();
1247 let mut dynamic = Vec::new();
1248 let mut pending =
1249 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1250 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 0);
1251 }
1252
1253 #[test]
1257 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1258 let (mut names, mut func, block) = empty();
1259 let base = Reg::physical(rucc_target::x86_64::RSP);
1260 let other = func.new_vreg(GPR);
1261 let mov = op(&mut names, "mov_rm_64");
1262 let word = func.new_vreg(GPR);
1263 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1264 let read = func.insts(block).next().expect("the load");
1265 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1266 let put = op(&mut names, "mov_mr_64");
1267 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1268 let written = func.insts(block).nth(2).expect("the store");
1269
1270 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1271 let mut arguments = Vec::new();
1272 let mut dynamic = Vec::new();
1273 let mut pending =
1274 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1275 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 1);
1276
1277 let inst = func.insts(block).next().expect("the addition");
1278 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1279 }
1280
1281 #[test]
1283 fn a_run_whose_widths_disagree_stays_three_instructions() {
1284 let (mut names, mut func, block) = empty();
1285 let base = func.new_vreg(GPR);
1286 let other = func.new_vreg(GPR);
1287 let into = func.new_vreg(GPR);
1288 let narrow = op(&mut names, "mov_rm_32");
1289 func.build(block, narrow)
1290 .def(into, GPR)
1291 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1292 .finish();
1293 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1294 store(&mut func, &mut names, block, base, sum);
1295
1296 assert_eq!(update(&mut func, &mut names), 0);
1297 }
1298
1299 #[test]
1301 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1302 for update in UPDATES {
1303 for name in [update.from, update.into, update.load, update.store] {
1304 assert!(MACHINE.has(name), "{name} is not an instruction");
1305 }
1306 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1307 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1308 assert_eq!(
1309 width(update.from),
1310 width(update.load),
1311 "{} loads another width",
1312 update.from
1313 );
1314 assert_eq!(
1315 width(update.from),
1316 width(update.store),
1317 "{} stores another width",
1318 update.from
1319 );
1320 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1321 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1322 }
1323 }
1324
1325 #[test]
1328 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1329 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1330 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1331 assert_eq!(commuting, 16, "everything but the four subtractions");
1332 }
1333
1334 fn alu_imm(
1336 func: &mut Func,
1337 names: &mut Interner,
1338 block: mir::Block,
1339 name: &str,
1340 source: Reg,
1341 value: i64,
1342 ) -> Reg {
1343 let answer = func.new_vreg(GPR);
1344 let opcode = op(names, name);
1345 func.build(block, opcode)
1346 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1347 .uses(source, GPR)
1348 .imm(value)
1349 .finish();
1350 answer
1351 }
1352
1353 #[test]
1355 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1356 let (mut names, mut func, block) = empty();
1357 let base = func.new_vreg(GPR);
1358 let word = load(&mut func, &mut names, block, base);
1359 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1360 store(&mut func, &mut names, block, base, sum);
1361
1362 assert_eq!(update(&mut func, &mut names), 1);
1363 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1364 let inst = func.insts(block).next().expect("the addition");
1365 let mem = func[inst].mem.expect("it writes memory");
1366 assert_eq!(func[mem].disp, 16, "the address came from the store");
1367 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1368 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1369 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1370 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1371 }
1372
1373 #[test]
1376 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1377 let (mut names, mut func, block) = empty();
1378 let base = func.new_vreg(GPR);
1379 let word = load(&mut func, &mut names, block, base);
1380 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1381 store(&mut func, &mut names, block, base, left);
1382
1383 assert_eq!(update(&mut func, &mut names), 1);
1384 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1385 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1386 }
1387
1388 #[test]
1391 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1392 let (mut names, mut func, block) = empty();
1393 let base = func.new_vreg(GPR);
1394 let word = func.new_vreg(GPR);
1395 let mov = op(&mut names, "mov_rm_8");
1396 func.build(block, mov)
1397 .def(word, GPR)
1398 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1399 .finish();
1400 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1401 let put = op(&mut names, "mov_mr_8");
1402 func.build(block, put)
1403 .uses(sum, GPR)
1404 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1405 .finish();
1406
1407 assert_eq!(update(&mut func, &mut names), 1);
1408 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1409 }
1410
1411 #[test]
1414 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
1415 let (mut names, mut func, block) = empty();
1416 let base = func.new_vreg(GPR);
1417 let other = func.new_vreg(GPR);
1418 let word = load(&mut func, &mut names, block, base);
1419 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1420 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1421 store(&mut func, &mut names, block, base, sum);
1422
1423 assert_eq!(update(&mut func, &mut names), 0);
1424 }
1425
1426 #[test]
1429 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
1430 let (mut names, mut func, block) = empty();
1431 let base = func.new_vreg(GPR);
1432 let elsewhere = func.new_vreg(GPR);
1433 let word = load(&mut func, &mut names, block, base);
1434 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1435 load(&mut func, &mut names, block, elsewhere);
1436 store(&mut func, &mut names, block, base, sum);
1437
1438 assert_eq!(update(&mut func, &mut names), 0);
1439 }
1440
1441 #[test]
1444 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1445 let (mut names, mut func, block) = empty();
1446 let base = Reg::physical(rucc_target::x86_64::RAX);
1447 let word = load(&mut func, &mut names, block, base);
1448 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1449 let mov = op(&mut names, "mov_ri_64");
1450 func.build(block, mov).def(base, GPR).imm(0).finish();
1451 store(&mut func, &mut names, block, base, sum);
1452
1453 assert_eq!(update(&mut func, &mut names), 0);
1454 }
1455
1456 #[test]
1458 fn a_constant_written_to_another_address_stays_three_instructions() {
1459 let (mut names, mut func, block) = empty();
1460 let base = func.new_vreg(GPR);
1461 let elsewhere = func.new_vreg(GPR);
1462 let word = load(&mut func, &mut names, block, base);
1463 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1464 store(&mut func, &mut names, block, elsewhere, sum);
1465
1466 assert_eq!(update(&mut func, &mut names), 0);
1467 }
1468
1469 #[test]
1471 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
1472 let (mut names, mut func, block) = empty();
1473 let base = func.new_vreg(GPR);
1474 let into = func.new_vreg(GPR);
1475 let narrow = op(&mut names, "mov_rm_32");
1476 func.build(block, narrow)
1477 .def(into, GPR)
1478 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1479 .finish();
1480 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
1481 store(&mut func, &mut names, block, base, sum);
1482
1483 assert_eq!(update(&mut func, &mut names), 0);
1484 }
1485
1486 #[test]
1489 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
1490 let (mut names, mut func, block) = empty();
1491 let base = func.new_vreg(GPR);
1492 let word = load(&mut func, &mut names, block, base);
1493 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
1494 store(&mut func, &mut names, block, base, product);
1495
1496 assert_eq!(update(&mut func, &mut names), 0);
1497 }
1498
1499 #[test]
1502 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
1503 let (mut names, mut func, block) = empty();
1504 let base = Reg::physical(rucc_target::x86_64::RSP);
1505 let mov = op(&mut names, "mov_rm_64");
1506 let word = func.new_vreg(GPR);
1507 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1508 let read = func.insts(block).next().expect("the load");
1509 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1510 let put = op(&mut names, "mov_mr_64");
1511 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1512 let written = func.insts(block).nth(2).expect("the store");
1513
1514 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1515 let mut arguments = Vec::new();
1516 let mut dynamic = Vec::new();
1517 let mut pending =
1518 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1519 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 1);
1520
1521 let inst = func.insts(block).next().expect("the addition");
1522 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1523 }
1524
1525 #[test]
1528 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
1529 let (mut names, mut func, block) = empty();
1530 let base = Reg::physical(rucc_target::x86_64::RSP);
1531 let mov = op(&mut names, "mov_rm_64");
1532 let word = func.new_vreg(GPR);
1533 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1534 let read = func.insts(block).next().expect("the load");
1535 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1536 let put = op(&mut names, "mov_mr_64");
1537 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1538 let written = func.insts(block).nth(2).expect("the store");
1539
1540 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1541 let mut arguments = Vec::new();
1542 let mut dynamic = Vec::new();
1543 let mut pending =
1544 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1545 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 0);
1546 }
1547
1548 #[test]
1550 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
1551 for bump in BUMPS {
1552 for name in [bump.from, bump.into, bump.load, bump.store] {
1553 assert!(MACHINE.has(name), "{name} is not an instruction");
1554 }
1555 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1556 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
1557 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
1558 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
1559 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
1560 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
1561 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
1562 }
1563 }
1564
1565 #[test]
1568 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
1569 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
1570 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
1571 for bump in BUMPS {
1572 let same = bump.from.replace("_ri_", "_rr_");
1573 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
1574 }
1575 }
1576
1577 #[test]
1580 fn nothing_is_both_a_register_run_and_a_constant_run() {
1581 for bump in BUMPS {
1582 assert!(
1583 !UPDATES.iter().any(|update| update.from == bump.from),
1584 "{} starts both kinds of run",
1585 bump.from
1586 );
1587 }
1588 }
1589
1590 #[test]
1592 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
1593 let (mut names, mut func, block) = empty();
1594 let base = func.new_vreg(GPR);
1595 let other = func.new_vreg(GPR);
1596 let word = load(&mut func, &mut names, block, base);
1597 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1598
1599 assert_eq!(combine(&mut func, &mut names), 1);
1600 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
1601 let inst = func.insts(block).next().expect("the addition");
1602 let mem = func[inst].mem.expect("the addition reads memory now");
1603 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
1604 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
1605 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
1606 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
1607 }
1608
1609 #[test]
1612 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
1613 let (mut names, mut func, block) = empty();
1614 let base = func.new_vreg(GPR);
1615 let other = func.new_vreg(GPR);
1616 let word = load(&mut func, &mut names, block, base);
1617 alu(&mut func, &mut names, block, "add_rr_64", word, other);
1618
1619 assert_eq!(combine(&mut func, &mut names), 1);
1620 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
1621 let inst = func.insts(block).next().expect("the addition");
1622 assert_eq!(func[func[inst].operands][1].reg, other);
1623 }
1624
1625 #[test]
1628 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
1629 let (mut names, mut func, block) = empty();
1630 let base = func.new_vreg(GPR);
1631 let other = func.new_vreg(GPR);
1632 let word = load(&mut func, &mut names, block, base);
1633 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1634
1635 assert_eq!(combine(&mut func, &mut names), 0);
1636 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
1637 }
1638
1639 #[test]
1641 fn a_load_feeding_the_right_of_a_subtraction_folds() {
1642 let (mut names, mut func, block) = empty();
1643 let base = func.new_vreg(GPR);
1644 let other = func.new_vreg(GPR);
1645 let word = load(&mut func, &mut names, block, base);
1646 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1647
1648 assert_eq!(combine(&mut func, &mut names), 1);
1649 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
1650 }
1651
1652 #[test]
1655 fn a_load_two_instructions_read_stays_a_load() {
1656 let (mut names, mut func, block) = empty();
1657 let base = func.new_vreg(GPR);
1658 let other = func.new_vreg(GPR);
1659 let word = load(&mut func, &mut names, block, base);
1660 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1661 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
1662
1663 assert_eq!(combine(&mut func, &mut names), 0);
1664 assert_eq!(
1665 shape(&func, &names, block),
1666 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
1667 );
1668 }
1669
1670 #[test]
1673 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
1674 let (mut names, mut func, block) = empty();
1675 let base = func.new_vreg(GPR);
1676 let other = func.new_vreg(GPR);
1677 let word = load(&mut func, &mut names, block, base);
1678 let store = op(&mut names, "mov_mr_64");
1679 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1680 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1681
1682 assert_eq!(combine(&mut func, &mut names), 0);
1683 assert_eq!(
1684 shape(&func, &names, block),
1685 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
1686 );
1687 }
1688
1689 #[test]
1695 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
1696 let (mut names, mut func, block) = empty();
1697 let base = func.new_vreg(GPR);
1698 let other = func.new_vreg(GPR);
1699 let word = load(&mut func, &mut names, block, base);
1700 load(&mut func, &mut names, block, other);
1701 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1702
1703 assert_eq!(combine(&mut func, &mut names), 0);
1704 assert_eq!(
1705 shape(&func, &names, block),
1706 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
1707 );
1708 }
1709
1710 #[test]
1713 fn the_later_of_two_loads_is_the_one_that_folds() {
1714 let (mut names, mut func, block) = empty();
1715 let base = func.new_vreg(GPR);
1716 let other = func.new_vreg(GPR);
1717 let first = load(&mut func, &mut names, block, base);
1718 let second = load(&mut func, &mut names, block, other);
1719 alu(&mut func, &mut names, block, "add_rr_64", first, second);
1720
1721 assert_eq!(combine(&mut func, &mut names), 1);
1722 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
1723 let addition = func.insts(block).nth(1).expect("the addition");
1724 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
1725 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
1726 }
1727
1728 #[test]
1731 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
1732 let (mut names, mut func, block) = empty();
1733 let base = func.new_vreg(GPR);
1734 let other = func.new_vreg(GPR);
1735 let word = load(&mut func, &mut names, block, base);
1736 let call = op(&mut names, "call");
1737 func.build(block, call).finish();
1738 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1739
1740 assert_eq!(combine(&mut func, &mut names), 0);
1741 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
1742 }
1743
1744 #[test]
1747 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
1748 let (mut names, mut func, block) = empty();
1749 let base = Reg::physical(rucc_target::x86_64::RSP);
1750 let other = func.new_vreg(GPR);
1751 let word = load(&mut func, &mut names, block, base);
1752 let sub = op(&mut names, "sub_ri_64");
1753 func.build(block, sub)
1754 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1755 .uses(base, GPR)
1756 .imm(32)
1757 .finish();
1758 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1759
1760 assert_eq!(combine(&mut func, &mut names), 0);
1761 }
1762
1763 #[test]
1766 fn a_load_of_the_wrong_width_stays_a_load() {
1767 let (mut names, mut func, block) = empty();
1768 let base = func.new_vreg(GPR);
1769 let other = func.new_vreg(GPR);
1770 let into = func.new_vreg(GPR);
1771 let narrow = op(&mut names, "mov_rm_32");
1772 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1773 alu(&mut func, &mut names, block, "add_rr_64", other, into);
1774
1775 assert_eq!(combine(&mut func, &mut names), 0);
1776 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
1777 }
1778
1779 #[test]
1782 fn a_load_whose_value_an_edge_carries_stays_a_load() {
1783 let (mut names, mut func, block) = empty();
1784 let next = func.create_block();
1785 let base = func.new_vreg(GPR);
1786 let other = func.new_vreg(GPR);
1787 let word = load(&mut func, &mut names, block, base);
1788 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1789 let arrived = func.new_vreg(GPR);
1790 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1791 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
1792
1793 assert_eq!(combine(&mut func, &mut names), 0);
1794 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
1795 }
1796
1797 #[test]
1799 fn a_reader_in_another_block_stays_where_it_is() {
1800 let (mut names, mut func, block) = empty();
1801 let next = func.create_block();
1802 let base = func.new_vreg(GPR);
1803 let other = func.new_vreg(GPR);
1804 let word = load(&mut func, &mut names, block, base);
1805 alu(&mut func, &mut names, next, "add_rr_64", other, word);
1806
1807 assert_eq!(combine(&mut func, &mut names), 0);
1808 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
1809 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
1810 }
1811
1812 #[test]
1814 fn a_reader_past_the_window_stays_where_it_is() {
1815 let (mut names, mut func, block) = empty();
1816 let base = func.new_vreg(GPR);
1817 let other = func.new_vreg(GPR);
1818 let word = load(&mut func, &mut names, block, base);
1819 let nop = op(&mut names, "nop");
1820 for _ in 0..WINDOW {
1821 func.build(block, nop).finish();
1822 }
1823 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1824
1825 assert_eq!(combine(&mut func, &mut names), 0);
1826 }
1827
1828 #[test]
1830 fn a_reader_at_the_edge_of_the_window_folds() {
1831 let (mut names, mut func, block) = empty();
1832 let base = func.new_vreg(GPR);
1833 let other = func.new_vreg(GPR);
1834 let word = load(&mut func, &mut names, block, base);
1835 let nop = op(&mut names, "nop");
1836 for _ in 0..WINDOW - 1 {
1837 func.build(block, nop).finish();
1838 }
1839 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1840
1841 assert_eq!(combine(&mut func, &mut names), 1);
1842 }
1843
1844 #[test]
1847 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
1848 let (mut names, mut func, block) = empty();
1849 let base = Reg::physical(rucc_target::x86_64::RSP);
1850 let other = func.new_vreg(GPR);
1851 let word = load(&mut func, &mut names, block, base);
1852 let reader = func.insts(block).nth(1);
1853 assert!(reader.is_none(), "the block holds the load alone so far");
1854 alu(&mut func, &mut names, block, "add_rr_64", other, word);
1855 let held = func.insts(block).next().expect("the load");
1856
1857 let mut addresses = vec![(held, 3usize)];
1858 let mut arguments = Vec::new();
1859 let mut dynamic = Vec::new();
1860 let mut pending =
1861 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1862 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
1863
1864 let inst = func.insts(block).next().expect("the addition");
1865 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
1866 }
1867
1868 #[test]
1872 fn every_row_of_the_table_is_three_instructions_this_target_has() {
1873 for fold in FOLDS {
1874 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
1875 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
1876 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
1877 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1878 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
1879 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
1880 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
1881 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
1882 }
1883 }
1884
1885 #[test]
1889 fn the_table_covers_the_arithmetic_this_target_has() {
1890 assert_eq!(FOLDS.len(), 23, "six operations at four widths, less the eight bit multiply");
1891 let commuting = FOLDS.iter().filter(|fold| fold.commutes).count();
1892 assert_eq!(commuting, 19, "everything but the four subtractions");
1893 }
1894}