1use rucc_base::Interner;
301use rucc_base::hash::Map;
302use rucc_mir::{Amode, Flags, Func, Inst, Opcode, Operand, Reg};
303use rucc_target::{FlagInsts, MachineInsts};
304
305use crate::changes::{Changes, Plan, Reads};
306use crate::fold::Pending;
307
308pub const WINDOW: usize = 16;
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub struct Fold {
335 pub from: &'static str,
337 pub into: &'static str,
339 pub load: &'static str,
341 pub swapped: Option<&'static str>,
357}
358
359pub static FOLDS: &[Fold] = &[
374 Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", swapped: Some("add_rm_8") },
375 Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", swapped: Some("add_rm_16") },
376 Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", swapped: Some("add_rm_32") },
377 Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", swapped: Some("add_rm_64") },
378 Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", swapped: None },
379 Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", swapped: None },
380 Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", swapped: None },
381 Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", swapped: None },
382 Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", swapped: Some("and_rm_8") },
383 Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", swapped: Some("and_rm_16") },
384 Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", swapped: Some("and_rm_32") },
385 Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", swapped: Some("and_rm_64") },
386 Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", swapped: Some("or_rm_8") },
387 Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", swapped: Some("or_rm_16") },
388 Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", swapped: Some("or_rm_32") },
389 Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", swapped: Some("or_rm_64") },
390 Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", swapped: Some("xor_rm_8") },
391 Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", swapped: Some("xor_rm_16") },
392 Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", swapped: Some("xor_rm_32") },
393 Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", swapped: Some("xor_rm_64") },
394 Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", swapped: Some("imul_rm_16") },
395 Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", swapped: Some("imul_rm_32") },
396 Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", swapped: Some("imul_rm_64") },
397 Fold {
398 from: "cmp_set_e_8",
399 into: "cmp_set_e_rm_8",
400 load: "mov_rm_8",
401 swapped: Some("cmp_set_e_rm_8"),
402 },
403 Fold {
404 from: "cmp_set_e_16",
405 into: "cmp_set_e_rm_16",
406 load: "mov_rm_16",
407 swapped: Some("cmp_set_e_rm_16"),
408 },
409 Fold {
410 from: "cmp_set_e_32",
411 into: "cmp_set_e_rm_32",
412 load: "mov_rm_32",
413 swapped: Some("cmp_set_e_rm_32"),
414 },
415 Fold {
416 from: "cmp_set_e_64",
417 into: "cmp_set_e_rm_64",
418 load: "mov_rm_64",
419 swapped: Some("cmp_set_e_rm_64"),
420 },
421 Fold {
422 from: "cmp_set_ne_8",
423 into: "cmp_set_ne_rm_8",
424 load: "mov_rm_8",
425 swapped: Some("cmp_set_ne_rm_8"),
426 },
427 Fold {
428 from: "cmp_set_ne_16",
429 into: "cmp_set_ne_rm_16",
430 load: "mov_rm_16",
431 swapped: Some("cmp_set_ne_rm_16"),
432 },
433 Fold {
434 from: "cmp_set_ne_32",
435 into: "cmp_set_ne_rm_32",
436 load: "mov_rm_32",
437 swapped: Some("cmp_set_ne_rm_32"),
438 },
439 Fold {
440 from: "cmp_set_ne_64",
441 into: "cmp_set_ne_rm_64",
442 load: "mov_rm_64",
443 swapped: Some("cmp_set_ne_rm_64"),
444 },
445 Fold {
446 from: "cmp_set_l_8",
447 into: "cmp_set_l_rm_8",
448 load: "mov_rm_8",
449 swapped: Some("cmp_set_g_rm_8"),
450 },
451 Fold {
452 from: "cmp_set_l_16",
453 into: "cmp_set_l_rm_16",
454 load: "mov_rm_16",
455 swapped: Some("cmp_set_g_rm_16"),
456 },
457 Fold {
458 from: "cmp_set_l_32",
459 into: "cmp_set_l_rm_32",
460 load: "mov_rm_32",
461 swapped: Some("cmp_set_g_rm_32"),
462 },
463 Fold {
464 from: "cmp_set_l_64",
465 into: "cmp_set_l_rm_64",
466 load: "mov_rm_64",
467 swapped: Some("cmp_set_g_rm_64"),
468 },
469 Fold {
470 from: "cmp_set_le_8",
471 into: "cmp_set_le_rm_8",
472 load: "mov_rm_8",
473 swapped: Some("cmp_set_ge_rm_8"),
474 },
475 Fold {
476 from: "cmp_set_le_16",
477 into: "cmp_set_le_rm_16",
478 load: "mov_rm_16",
479 swapped: Some("cmp_set_ge_rm_16"),
480 },
481 Fold {
482 from: "cmp_set_le_32",
483 into: "cmp_set_le_rm_32",
484 load: "mov_rm_32",
485 swapped: Some("cmp_set_ge_rm_32"),
486 },
487 Fold {
488 from: "cmp_set_le_64",
489 into: "cmp_set_le_rm_64",
490 load: "mov_rm_64",
491 swapped: Some("cmp_set_ge_rm_64"),
492 },
493 Fold {
494 from: "cmp_set_g_8",
495 into: "cmp_set_g_rm_8",
496 load: "mov_rm_8",
497 swapped: Some("cmp_set_l_rm_8"),
498 },
499 Fold {
500 from: "cmp_set_g_16",
501 into: "cmp_set_g_rm_16",
502 load: "mov_rm_16",
503 swapped: Some("cmp_set_l_rm_16"),
504 },
505 Fold {
506 from: "cmp_set_g_32",
507 into: "cmp_set_g_rm_32",
508 load: "mov_rm_32",
509 swapped: Some("cmp_set_l_rm_32"),
510 },
511 Fold {
512 from: "cmp_set_g_64",
513 into: "cmp_set_g_rm_64",
514 load: "mov_rm_64",
515 swapped: Some("cmp_set_l_rm_64"),
516 },
517 Fold {
518 from: "cmp_set_ge_8",
519 into: "cmp_set_ge_rm_8",
520 load: "mov_rm_8",
521 swapped: Some("cmp_set_le_rm_8"),
522 },
523 Fold {
524 from: "cmp_set_ge_16",
525 into: "cmp_set_ge_rm_16",
526 load: "mov_rm_16",
527 swapped: Some("cmp_set_le_rm_16"),
528 },
529 Fold {
530 from: "cmp_set_ge_32",
531 into: "cmp_set_ge_rm_32",
532 load: "mov_rm_32",
533 swapped: Some("cmp_set_le_rm_32"),
534 },
535 Fold {
536 from: "cmp_set_ge_64",
537 into: "cmp_set_ge_rm_64",
538 load: "mov_rm_64",
539 swapped: Some("cmp_set_le_rm_64"),
540 },
541 Fold {
542 from: "cmp_set_b_8",
543 into: "cmp_set_b_rm_8",
544 load: "mov_rm_8",
545 swapped: Some("cmp_set_a_rm_8"),
546 },
547 Fold {
548 from: "cmp_set_b_16",
549 into: "cmp_set_b_rm_16",
550 load: "mov_rm_16",
551 swapped: Some("cmp_set_a_rm_16"),
552 },
553 Fold {
554 from: "cmp_set_b_32",
555 into: "cmp_set_b_rm_32",
556 load: "mov_rm_32",
557 swapped: Some("cmp_set_a_rm_32"),
558 },
559 Fold {
560 from: "cmp_set_b_64",
561 into: "cmp_set_b_rm_64",
562 load: "mov_rm_64",
563 swapped: Some("cmp_set_a_rm_64"),
564 },
565 Fold {
566 from: "cmp_set_be_8",
567 into: "cmp_set_be_rm_8",
568 load: "mov_rm_8",
569 swapped: Some("cmp_set_ae_rm_8"),
570 },
571 Fold {
572 from: "cmp_set_be_16",
573 into: "cmp_set_be_rm_16",
574 load: "mov_rm_16",
575 swapped: Some("cmp_set_ae_rm_16"),
576 },
577 Fold {
578 from: "cmp_set_be_32",
579 into: "cmp_set_be_rm_32",
580 load: "mov_rm_32",
581 swapped: Some("cmp_set_ae_rm_32"),
582 },
583 Fold {
584 from: "cmp_set_be_64",
585 into: "cmp_set_be_rm_64",
586 load: "mov_rm_64",
587 swapped: Some("cmp_set_ae_rm_64"),
588 },
589 Fold {
590 from: "cmp_set_a_8",
591 into: "cmp_set_a_rm_8",
592 load: "mov_rm_8",
593 swapped: Some("cmp_set_b_rm_8"),
594 },
595 Fold {
596 from: "cmp_set_a_16",
597 into: "cmp_set_a_rm_16",
598 load: "mov_rm_16",
599 swapped: Some("cmp_set_b_rm_16"),
600 },
601 Fold {
602 from: "cmp_set_a_32",
603 into: "cmp_set_a_rm_32",
604 load: "mov_rm_32",
605 swapped: Some("cmp_set_b_rm_32"),
606 },
607 Fold {
608 from: "cmp_set_a_64",
609 into: "cmp_set_a_rm_64",
610 load: "mov_rm_64",
611 swapped: Some("cmp_set_b_rm_64"),
612 },
613 Fold {
614 from: "cmp_set_ae_8",
615 into: "cmp_set_ae_rm_8",
616 load: "mov_rm_8",
617 swapped: Some("cmp_set_be_rm_8"),
618 },
619 Fold {
620 from: "cmp_set_ae_16",
621 into: "cmp_set_ae_rm_16",
622 load: "mov_rm_16",
623 swapped: Some("cmp_set_be_rm_16"),
624 },
625 Fold {
626 from: "cmp_set_ae_32",
627 into: "cmp_set_ae_rm_32",
628 load: "mov_rm_32",
629 swapped: Some("cmp_set_be_rm_32"),
630 },
631 Fold {
632 from: "cmp_set_ae_64",
633 into: "cmp_set_ae_rm_64",
634 load: "mov_rm_64",
635 swapped: Some("cmp_set_be_rm_64"),
636 },
637 Fold { from: "cmp_set_e_ri_8", into: "cmp_set_e_mi_8", load: "mov_rm_8", swapped: None },
638 Fold { from: "cmp_set_e_ri_16", into: "cmp_set_e_mi_16", load: "mov_rm_16", swapped: None },
639 Fold { from: "cmp_set_e_ri_32", into: "cmp_set_e_mi_32", load: "mov_rm_32", swapped: None },
640 Fold { from: "cmp_set_e_ri_64", into: "cmp_set_e_mi_64", load: "mov_rm_64", swapped: None },
641 Fold { from: "cmp_set_ne_ri_8", into: "cmp_set_ne_mi_8", load: "mov_rm_8", swapped: None },
642 Fold { from: "cmp_set_ne_ri_16", into: "cmp_set_ne_mi_16", load: "mov_rm_16", swapped: None },
643 Fold { from: "cmp_set_ne_ri_32", into: "cmp_set_ne_mi_32", load: "mov_rm_32", swapped: None },
644 Fold { from: "cmp_set_ne_ri_64", into: "cmp_set_ne_mi_64", load: "mov_rm_64", swapped: None },
645 Fold { from: "cmp_set_l_ri_8", into: "cmp_set_l_mi_8", load: "mov_rm_8", swapped: None },
646 Fold { from: "cmp_set_l_ri_16", into: "cmp_set_l_mi_16", load: "mov_rm_16", swapped: None },
647 Fold { from: "cmp_set_l_ri_32", into: "cmp_set_l_mi_32", load: "mov_rm_32", swapped: None },
648 Fold { from: "cmp_set_l_ri_64", into: "cmp_set_l_mi_64", load: "mov_rm_64", swapped: None },
649 Fold { from: "cmp_set_le_ri_8", into: "cmp_set_le_mi_8", load: "mov_rm_8", swapped: None },
650 Fold { from: "cmp_set_le_ri_16", into: "cmp_set_le_mi_16", load: "mov_rm_16", swapped: None },
651 Fold { from: "cmp_set_le_ri_32", into: "cmp_set_le_mi_32", load: "mov_rm_32", swapped: None },
652 Fold { from: "cmp_set_le_ri_64", into: "cmp_set_le_mi_64", load: "mov_rm_64", swapped: None },
653 Fold { from: "cmp_set_g_ri_8", into: "cmp_set_g_mi_8", load: "mov_rm_8", swapped: None },
654 Fold { from: "cmp_set_g_ri_16", into: "cmp_set_g_mi_16", load: "mov_rm_16", swapped: None },
655 Fold { from: "cmp_set_g_ri_32", into: "cmp_set_g_mi_32", load: "mov_rm_32", swapped: None },
656 Fold { from: "cmp_set_g_ri_64", into: "cmp_set_g_mi_64", load: "mov_rm_64", swapped: None },
657 Fold { from: "cmp_set_ge_ri_8", into: "cmp_set_ge_mi_8", load: "mov_rm_8", swapped: None },
658 Fold { from: "cmp_set_ge_ri_16", into: "cmp_set_ge_mi_16", load: "mov_rm_16", swapped: None },
659 Fold { from: "cmp_set_ge_ri_32", into: "cmp_set_ge_mi_32", load: "mov_rm_32", swapped: None },
660 Fold { from: "cmp_set_ge_ri_64", into: "cmp_set_ge_mi_64", load: "mov_rm_64", swapped: None },
661 Fold { from: "cmp_set_b_ri_8", into: "cmp_set_b_mi_8", load: "mov_rm_8", swapped: None },
662 Fold { from: "cmp_set_b_ri_16", into: "cmp_set_b_mi_16", load: "mov_rm_16", swapped: None },
663 Fold { from: "cmp_set_b_ri_32", into: "cmp_set_b_mi_32", load: "mov_rm_32", swapped: None },
664 Fold { from: "cmp_set_b_ri_64", into: "cmp_set_b_mi_64", load: "mov_rm_64", swapped: None },
665 Fold { from: "cmp_set_be_ri_8", into: "cmp_set_be_mi_8", load: "mov_rm_8", swapped: None },
666 Fold { from: "cmp_set_be_ri_16", into: "cmp_set_be_mi_16", load: "mov_rm_16", swapped: None },
667 Fold { from: "cmp_set_be_ri_32", into: "cmp_set_be_mi_32", load: "mov_rm_32", swapped: None },
668 Fold { from: "cmp_set_be_ri_64", into: "cmp_set_be_mi_64", load: "mov_rm_64", swapped: None },
669 Fold { from: "cmp_set_a_ri_8", into: "cmp_set_a_mi_8", load: "mov_rm_8", swapped: None },
670 Fold { from: "cmp_set_a_ri_16", into: "cmp_set_a_mi_16", load: "mov_rm_16", swapped: None },
671 Fold { from: "cmp_set_a_ri_32", into: "cmp_set_a_mi_32", load: "mov_rm_32", swapped: None },
672 Fold { from: "cmp_set_a_ri_64", into: "cmp_set_a_mi_64", load: "mov_rm_64", swapped: None },
673 Fold { from: "cmp_set_ae_ri_8", into: "cmp_set_ae_mi_8", load: "mov_rm_8", swapped: None },
674 Fold { from: "cmp_set_ae_ri_16", into: "cmp_set_ae_mi_16", load: "mov_rm_16", swapped: None },
675 Fold { from: "cmp_set_ae_ri_32", into: "cmp_set_ae_mi_32", load: "mov_rm_32", swapped: None },
676 Fold { from: "cmp_set_ae_ri_64", into: "cmp_set_ae_mi_64", load: "mov_rm_64", swapped: None },
677];
678
679pub static WIDENINGS: &[Fold] = &[
686 Fold { from: "movzx_8_16", into: "movzx_rm_8_16", load: "mov_rm_8", swapped: None },
687 Fold { from: "movzx_8_32", into: "movzx_rm_8_32", load: "mov_rm_8", swapped: None },
688 Fold { from: "movzx_8_64", into: "movzx_rm_8_64", load: "mov_rm_8", swapped: None },
689 Fold { from: "movzx_16_32", into: "movzx_rm_16_32", load: "mov_rm_16", swapped: None },
690 Fold { from: "movzx_16_64", into: "movzx_rm_16_64", load: "mov_rm_16", swapped: None },
691 Fold { from: "movsx_8_16", into: "movsx_rm_8_16", load: "mov_rm_8", swapped: None },
692 Fold { from: "movsx_8_32", into: "movsx_rm_8_32", load: "mov_rm_8", swapped: None },
693 Fold { from: "movsx_8_64", into: "movsx_rm_8_64", load: "mov_rm_8", swapped: None },
694 Fold { from: "movsx_16_32", into: "movsx_rm_16_32", load: "mov_rm_16", swapped: None },
695 Fold { from: "movsx_16_64", into: "movsx_rm_16_64", load: "mov_rm_16", swapped: None },
696 Fold { from: "movsxd_32_64", into: "movsxd_rm_32_64", load: "mov_rm_32", swapped: None },
697 Fold { from: "mov_32_to_64", into: "mov_rm_32", load: "mov_rm_32", swapped: None },
698];
699
700#[derive(Debug, Clone, Copy, PartialEq, Eq)]
707pub struct Update {
708 pub from: &'static str,
710 pub into: &'static str,
712 pub load: &'static str,
714 pub store: &'static str,
716 pub commutes: bool,
718}
719
720pub static UPDATES: &[Update] = &[
731 Update {
732 from: "add_rr_8",
733 into: "add_mr_8",
734 load: "mov_rm_8",
735 store: "mov_mr_8",
736 commutes: true,
737 },
738 Update {
739 from: "add_rr_16",
740 into: "add_mr_16",
741 load: "mov_rm_16",
742 store: "mov_mr_16",
743 commutes: true,
744 },
745 Update {
746 from: "add_rr_32",
747 into: "add_mr_32",
748 load: "mov_rm_32",
749 store: "mov_mr_32",
750 commutes: true,
751 },
752 Update {
753 from: "add_rr_64",
754 into: "add_mr_64",
755 load: "mov_rm_64",
756 store: "mov_mr_64",
757 commutes: true,
758 },
759 Update {
760 from: "sub_rr_8",
761 into: "sub_mr_8",
762 load: "mov_rm_8",
763 store: "mov_mr_8",
764 commutes: false,
765 },
766 Update {
767 from: "sub_rr_16",
768 into: "sub_mr_16",
769 load: "mov_rm_16",
770 store: "mov_mr_16",
771 commutes: false,
772 },
773 Update {
774 from: "sub_rr_32",
775 into: "sub_mr_32",
776 load: "mov_rm_32",
777 store: "mov_mr_32",
778 commutes: false,
779 },
780 Update {
781 from: "sub_rr_64",
782 into: "sub_mr_64",
783 load: "mov_rm_64",
784 store: "mov_mr_64",
785 commutes: false,
786 },
787 Update {
788 from: "and_rr_8",
789 into: "and_mr_8",
790 load: "mov_rm_8",
791 store: "mov_mr_8",
792 commutes: true,
793 },
794 Update {
795 from: "and_rr_16",
796 into: "and_mr_16",
797 load: "mov_rm_16",
798 store: "mov_mr_16",
799 commutes: true,
800 },
801 Update {
802 from: "and_rr_32",
803 into: "and_mr_32",
804 load: "mov_rm_32",
805 store: "mov_mr_32",
806 commutes: true,
807 },
808 Update {
809 from: "and_rr_64",
810 into: "and_mr_64",
811 load: "mov_rm_64",
812 store: "mov_mr_64",
813 commutes: true,
814 },
815 Update {
816 from: "or_rr_8",
817 into: "or_mr_8",
818 load: "mov_rm_8",
819 store: "mov_mr_8",
820 commutes: true,
821 },
822 Update {
823 from: "or_rr_16",
824 into: "or_mr_16",
825 load: "mov_rm_16",
826 store: "mov_mr_16",
827 commutes: true,
828 },
829 Update {
830 from: "or_rr_32",
831 into: "or_mr_32",
832 load: "mov_rm_32",
833 store: "mov_mr_32",
834 commutes: true,
835 },
836 Update {
837 from: "or_rr_64",
838 into: "or_mr_64",
839 load: "mov_rm_64",
840 store: "mov_mr_64",
841 commutes: true,
842 },
843 Update {
844 from: "xor_rr_8",
845 into: "xor_mr_8",
846 load: "mov_rm_8",
847 store: "mov_mr_8",
848 commutes: true,
849 },
850 Update {
851 from: "xor_rr_16",
852 into: "xor_mr_16",
853 load: "mov_rm_16",
854 store: "mov_mr_16",
855 commutes: true,
856 },
857 Update {
858 from: "xor_rr_32",
859 into: "xor_mr_32",
860 load: "mov_rm_32",
861 store: "mov_mr_32",
862 commutes: true,
863 },
864 Update {
865 from: "xor_rr_64",
866 into: "xor_mr_64",
867 load: "mov_rm_64",
868 store: "mov_mr_64",
869 commutes: true,
870 },
871];
872
873#[derive(Debug, Clone, Copy, PartialEq, Eq)]
882pub struct Bump {
883 pub from: &'static str,
885 pub into: &'static str,
887 pub load: &'static str,
889 pub store: &'static str,
891}
892
893pub static BUMPS: &[Bump] = &[
904 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
905 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
906 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
907 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
908 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
909 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
910 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
911 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
912 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
913 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
914 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
915 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
916 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
917 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
918 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
919 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
920 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
921 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
922 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
923 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
924];
925
926#[derive(Debug, Clone, Copy)]
931struct Waiting {
932 inst: Inst,
934 reg: Reg,
936 load: &'static str,
938 at: usize,
940}
941
942pub fn loads(
953 func: &mut Func,
954 machine: &MachineInsts,
955 names: &mut Interner,
956 pending: &mut Pending<'_>,
957) -> usize {
958 let mut reads = Reads::of(func);
959 let mut done = 0;
960 let mut seen = Map::default();
961 for block in func.blocks().collect::<Vec<_>>() {
962 let mut waiting: Option<Waiting> = None;
963 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
964 let opcode = func[inst].opcode;
970 let Loads { barrier, load } =
971 *seen.entry(opcode).or_insert_with(|| Loads::of(machine, names, opcode));
972 if let Some(carried) = waiting {
973 let bare = machine.bare(names.resolve(opcode.name())).to_owned();
974 if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
975 let mut set = Changes::new();
976 set.rewrite(inst, plan);
977 set.remove(carried.inst);
978 if set.commit(func, &mut reads, names, machine).is_ok() {
979 pending.moved(carried.inst, &[inst]);
980 waiting = None;
981 done += 1;
982 }
983 }
984 }
985 if barrier {
986 waiting = None;
987 }
988 if let Some(carried) = waiting {
989 if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
990 waiting = None;
991 }
992 }
993 if insisted(func, inst) {
999 continue;
1000 }
1001 if let Some(load) = load {
1002 let operands = &func[func[inst].operands];
1003 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
1004 waiting = Some(Waiting { inst, reg: first.reg, load, at });
1005 }
1006 }
1007 }
1008 }
1009 done
1010}
1011
1012#[derive(Debug, Clone, Copy)]
1019struct Loads {
1020 barrier: bool,
1023 load: Option<&'static str>,
1025}
1026
1027impl Loads {
1028 fn of(machine: &MachineInsts, names: &Interner, opcode: Opcode) -> Self {
1029 let name = names.resolve(opcode.name());
1030 let bare = machine.bare(name);
1031 let mut rows = FOLDS.iter().chain(WIDENINGS);
1032 Self {
1033 barrier: machine.calls(name) || !machine.has(name) || machine.touches_mem(name),
1034 load: rows.find(|fold| fold.load == bare).map(|fold| fold.load),
1035 }
1036 }
1037}
1038
1039#[derive(Debug, Clone, Copy)]
1041struct Run {
1042 load: Inst,
1044 alu: Inst,
1046 store: Inst,
1048 update: &'static Update,
1050 kept: Operand,
1052}
1053
1054#[derive(Debug, Clone, Copy)]
1060struct Bumped {
1061 load: Inst,
1063 alu: Inst,
1065 store: Inst,
1067 bump: &'static Bump,
1069 imm: i64,
1071}
1072
1073pub fn stores(
1090 func: &mut Func,
1091 machine: &MachineInsts,
1092 flags: &FlagInsts,
1093 names: &mut Interner,
1094 pending: &mut Pending<'_>,
1095) -> usize {
1096 let mut reads = Reads::of(func);
1097 let mut done = 0;
1098 for block in func.blocks().collect::<Vec<_>>() {
1099 let insts: Vec<Inst> = func.insts(block).collect();
1100 for at in 0..insts.len() {
1101 let found = match run(func, &reads, machine, flags, names, &insts, at) {
1102 Some(found) => Some((
1103 found.load,
1104 found.alu,
1105 found.store,
1106 updated(func, machine, names, &found),
1107 )),
1108 None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1109 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1110 }),
1111 };
1112 let Some((load, alu, store, plan)) = found else { continue };
1113 if !pending.alike(load, store) {
1114 continue;
1115 }
1116 let mut set = Changes::new();
1117 set.rewrite(store, plan);
1118 set.remove(alu);
1119 set.remove(load);
1120 if set.commit(func, &mut reads, names, machine).is_ok() {
1121 pending.moved(load, &[]);
1122 done += 1;
1123 }
1124 }
1125 }
1126 done
1127}
1128
1129fn run(
1141 func: &Func,
1142 reads: &Reads,
1143 machine: &MachineInsts,
1144 flags: &FlagInsts,
1145 names: &Interner,
1146 insts: &[Inst],
1147 at: usize,
1148) -> Option<Run> {
1149 let store = insts[at];
1150 if insisted(func, store) {
1151 return None;
1152 }
1153 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1154 let value = *func[func[store].operands].first()?;
1155 if value.role.is_def() || reads.count(value.reg) != 1 {
1156 return None;
1157 }
1158 let earliest = at.saturating_sub(WINDOW);
1161 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1162 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1163 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1164 if !quiet(func, flags, names, insts, (alu, at)) {
1165 return None;
1166 }
1167 let operands = func[func[insts[alu]].operands].to_vec();
1168 let [_, first, second] = operands[..] else { return None };
1169 let both = [(first, second), (second, first)];
1174 let tried = if update.commutes { &both[..] } else { &both[..1] };
1175 for &(source, kept) in tried {
1176 if reads.count(source.reg) != 1 {
1177 continue;
1178 }
1179 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1180 continue;
1181 };
1182 let load = insts[from];
1183 if insisted(func, load) {
1184 continue;
1185 }
1186 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1187 continue;
1188 }
1189 if !same_place(func, load, store) {
1190 continue;
1191 }
1192 let mut wanted: Vec<Reg> =
1197 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1198 wanted.push(kept.reg);
1199 if !clear(func, machine, names, insts, (from, at), &wanted) {
1200 continue;
1201 }
1202 return Some(Run { load, alu: insts[alu], store, update, kept });
1203 }
1204 None
1205}
1206
1207fn constant(
1221 func: &Func,
1222 reads: &Reads,
1223 machine: &MachineInsts,
1224 flags: &FlagInsts,
1225 names: &Interner,
1226 insts: &[Inst],
1227 at: usize,
1228) -> Option<Bumped> {
1229 let store = insts[at];
1230 if insisted(func, store) {
1231 return None;
1232 }
1233 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1234 let value = *func[func[store].operands].first()?;
1235 if value.role.is_def() || reads.count(value.reg) != 1 {
1236 return None;
1237 }
1238 let mem = func[func[store].mem?];
1239 if mem.base == Some(0) || mem.index == Some(0) {
1240 return None;
1241 }
1242 let earliest = at.saturating_sub(WINDOW);
1243 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1244 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1245 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1246 if !quiet(func, flags, names, insts, (alu, at)) {
1247 return None;
1248 }
1249 let operands = func[func[insts[alu]].operands].to_vec();
1250 let [_, source] = operands[..] else { return None };
1251 let imm = func[func[insts[alu]].imm?].0;
1252 if reads.count(source.reg) != 1 {
1253 return None;
1254 }
1255 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1256 let load = insts[from];
1257 if insisted(func, load) {
1258 return None;
1259 }
1260 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1261 return None;
1262 }
1263 if !same_place(func, load, store) {
1264 return None;
1265 }
1266 let wanted: Vec<Reg> =
1269 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1270 if !clear(func, machine, names, insts, (from, at), &wanted) {
1271 return None;
1272 }
1273 Some(Bumped { load, alu: insts[alu], store, bump, imm })
1274}
1275
1276fn insisted(func: &Func, inst: Inst) -> bool {
1281 func[inst].flags.contains(Flags::VOLATILE)
1282}
1283
1284fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1286 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1287}
1288
1289fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1296 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1297 let (here, there) = (func[here], func[there]);
1298 if func[one].symbol != func[other].symbol {
1299 return false;
1300 }
1301 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1302 if bare(here) != bare(there) {
1303 return false;
1304 }
1305 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1306 (None, None) => true,
1307 (Some(left), Some(right)) => {
1308 func[func[one].operands][usize::from(left)].reg
1309 == func[func[other].operands][usize::from(right)].reg
1310 }
1311 _ => false,
1312 };
1313 same(here.base, there.base) && same(here.index, there.index)
1314}
1315
1316fn clear(
1323 func: &Func,
1324 machine: &MachineInsts,
1325 names: &Interner,
1326 insts: &[Inst],
1327 span: (usize, usize),
1328 wanted: &[Reg],
1329) -> bool {
1330 let (from, to) = span;
1331 insts[from + 1..to].iter().all(|&inst| {
1332 let name = names.resolve(func[inst].opcode.name());
1333 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1334 return false;
1335 }
1336 !func[func[inst].operands]
1337 .iter()
1338 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1339 })
1340}
1341
1342fn quiet(
1359 func: &Func,
1360 flags: &FlagInsts,
1361 names: &Interner,
1362 insts: &[Inst],
1363 span: (usize, usize),
1364) -> bool {
1365 let (alu, to) = span;
1366 insts[alu + 1..to].iter().all(|&inst| {
1367 let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1368 return false;
1369 };
1370 flags.reads(name).is_none() && !(flags.writes)(name)
1371 })
1372}
1373
1374fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1380 let operands = func[func[run.store].operands].to_vec();
1381 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1382 Plan {
1383 opcode: Opcode::new(into),
1384 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1385 imm: None,
1386 amode: func[run.store].mem.map(|mem| func[mem]),
1387 symbol: func[run.store].symbol,
1388 }
1389}
1390
1391fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1399 let operands = func[func[run.store].operands][1..].to_vec();
1400 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1401 let back = |at: Option<u8>| at.map(|at| at - 1);
1402 Plan {
1403 opcode: Opcode::new(into),
1404 operands,
1405 imm: Some(run.imm),
1406 amode: func[run.store].mem.map(|mem| {
1407 let mem = func[mem];
1408 Amode { base: back(mem.base), index: back(mem.index), ..mem }
1409 }),
1410 symbol: func[run.store].symbol,
1411 }
1412}
1413
1414fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1420 let written: Vec<Reg> = func[func[inst].operands]
1421 .iter()
1422 .filter(|operand| operand.role.is_def())
1423 .map(|operand| operand.reg)
1424 .collect();
1425 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1426}
1427
1428fn joined(
1433 func: &Func,
1434 reads: &Reads,
1435 carried: Waiting,
1436 machine: &MachineInsts,
1437 names: &mut Interner,
1438 inst: Inst,
1439 bare: &str,
1440) -> Option<Plan> {
1441 let fold = FOLDS.iter().chain(WIDENINGS).find(|fold| fold.from == bare)?;
1442 if carried.load != fold.load || reads.count(carried.reg) != 1 {
1443 return None;
1444 }
1445 let operands = func[func[inst].operands].to_vec();
1446 let (front, into) = match operands[..] {
1457 [answer, first, second] => {
1458 let (kept, into) = if second.reg == carried.reg {
1459 (first, fold.into)
1460 } else if first.reg == carried.reg {
1461 (second, fold.swapped?)
1462 } else {
1463 return None;
1464 };
1465 (vec![answer, kept], into)
1466 }
1467 [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1468 _ => return None,
1469 };
1470 let load = carried.inst;
1471 let address = func[func[load].operands][1..].to_vec();
1472 let mut amode = func[func[load].mem?];
1473 let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1477 amode.base = amode.base.map(|at| at + along);
1478 amode.index = amode.index.map(|at| at + along);
1479 let into = names.intern(&format!("{}{}", machine.prefix, into));
1480 Some(Plan {
1481 opcode: Opcode::new(into),
1482 operands: front.into_iter().chain(address).collect(),
1483 imm: func[inst].imm.map(|at| func[at].0),
1484 amode: Some(amode),
1485 symbol: func[load].symbol,
1486 })
1487}
1488
1489#[cfg(test)]
1490mod tests {
1491 use rucc_mir::{self as mir, Constraint, Mem, Operand};
1492 use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1493
1494 use super::*;
1495
1496 fn empty() -> (Interner, Func, mir::Block) {
1498 let mut names = Interner::new();
1499 let mut func = Func::new(names.intern("f"));
1500 let block = func.create_block();
1501 (names, func, block)
1502 }
1503
1504 fn op(names: &mut Interner, name: &str) -> Opcode {
1506 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1507 }
1508
1509 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1511 let into = func.new_vreg(GPR);
1512 let mov = op(names, "mov_rm_64");
1513 func.build(block, mov)
1514 .def(into, GPR)
1515 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1516 .finish();
1517 into
1518 }
1519
1520 fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1522 let into = func.new_vreg(GPR);
1523 let mov = op(names, "mov_rm_64");
1524 func.build(block, mov)
1525 .def(into, GPR)
1526 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1527 .flags(Flags::VOLATILE)
1528 .finish();
1529 into
1530 }
1531
1532 fn alu(
1534 func: &mut Func,
1535 names: &mut Interner,
1536 block: mir::Block,
1537 name: &str,
1538 first: Reg,
1539 second: Reg,
1540 ) -> Reg {
1541 let answer = func.new_vreg(GPR);
1542 let opcode = op(names, name);
1543 func.build(block, opcode)
1544 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1545 .uses(first, GPR)
1546 .uses(second, GPR)
1547 .finish();
1548 answer
1549 }
1550
1551 fn compare(
1554 func: &mut Func,
1555 names: &mut Interner,
1556 block: mir::Block,
1557 name: &str,
1558 first: Reg,
1559 second: Reg,
1560 ) -> Reg {
1561 let byte = func.new_vreg(GPR);
1562 let opcode = op(names, name);
1563 func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1564 byte
1565 }
1566
1567 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1569 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1570 }
1571
1572 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1574 let mut addresses = Vec::new();
1575 let mut arguments = Vec::new();
1576 let mut dynamic = Vec::new();
1577 let mut pending =
1578 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1579 loads(func, &MACHINE, names, &mut pending)
1580 }
1581
1582 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1584 let mov = op(names, "mov_mr_64");
1585 func.build(block, mov)
1586 .uses(value, GPR)
1587 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1588 .finish();
1589 }
1590
1591 fn insisted_store(
1593 func: &mut Func,
1594 names: &mut Interner,
1595 block: mir::Block,
1596 base: Reg,
1597 value: Reg,
1598 ) {
1599 let mov = op(names, "mov_mr_64");
1600 func.build(block, mov)
1601 .uses(value, GPR)
1602 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1603 .flags(Flags::VOLATILE)
1604 .finish();
1605 }
1606
1607 fn update(func: &mut Func, names: &mut Interner) -> usize {
1609 let mut addresses = Vec::new();
1610 let mut arguments = Vec::new();
1611 let mut dynamic = Vec::new();
1612 let mut pending =
1613 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1614 stores(func, &MACHINE, &FLAGS, names, &mut pending)
1615 }
1616
1617 #[test]
1619 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1620 let (mut names, mut func, block) = empty();
1621 let base = func.new_vreg(GPR);
1622 let other = func.new_vreg(GPR);
1623 let word = load(&mut func, &mut names, block, base);
1624 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1625 store(&mut func, &mut names, block, base, sum);
1626
1627 assert_eq!(update(&mut func, &mut names), 1);
1628 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1629 let inst = func.insts(block).next().expect("the addition");
1630 let mem = func[inst].mem.expect("it writes memory");
1631 assert_eq!(func[mem].disp, 16, "the address came from the store");
1632 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1633 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1634 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1635 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1636 }
1637
1638 #[test]
1642 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1643 let (mut names, mut func, block) = empty();
1644 let base = func.new_vreg(GPR);
1645 let other = func.new_vreg(GPR);
1646 let word = load(&mut func, &mut names, block, base);
1647 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1648 store(&mut func, &mut names, block, base, sum);
1649
1650 assert_eq!(update(&mut func, &mut names), 1);
1651 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1652 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1653 }
1654
1655 #[test]
1658 fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1659 let (mut names, mut func, block) = empty();
1660 let base = func.new_vreg(GPR);
1661 let other = func.new_vreg(GPR);
1662 let word = load(&mut func, &mut names, block, base);
1663 let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1664 store(&mut func, &mut names, block, base, left);
1665
1666 assert_eq!(update(&mut func, &mut names), 1);
1667 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1668 }
1669
1670 #[test]
1673 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
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 left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1679 store(&mut func, &mut names, block, base, left);
1680
1681 assert_eq!(update(&mut func, &mut names), 0);
1682 assert_eq!(
1683 shape(&func, &names, block),
1684 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1685 );
1686 }
1687
1688 #[test]
1691 fn a_store_to_another_address_stays_three_instructions() {
1692 let (mut names, mut func, block) = empty();
1693 let base = func.new_vreg(GPR);
1694 let elsewhere = func.new_vreg(GPR);
1695 let other = func.new_vreg(GPR);
1696 let word = load(&mut func, &mut names, block, base);
1697 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1698 store(&mut func, &mut names, block, elsewhere, sum);
1699
1700 assert_eq!(update(&mut func, &mut names), 0);
1701 }
1702
1703 #[test]
1706 fn a_store_at_another_displacement_stays_three_instructions() {
1707 let (mut names, mut func, block) = empty();
1708 let base = func.new_vreg(GPR);
1709 let other = func.new_vreg(GPR);
1710 let word = load(&mut func, &mut names, block, base);
1711 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1712 let mov = op(&mut names, "mov_mr_64");
1713 func.build(block, mov)
1714 .uses(sum, GPR)
1715 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1716 .finish();
1717
1718 assert_eq!(update(&mut func, &mut names), 0);
1719 }
1720
1721 #[test]
1724 fn a_word_two_instructions_read_stays_three_instructions() {
1725 let (mut names, mut func, block) = empty();
1726 let base = func.new_vreg(GPR);
1727 let other = func.new_vreg(GPR);
1728 let word = load(&mut func, &mut names, block, base);
1729 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1730 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1731 store(&mut func, &mut names, block, base, sum);
1732
1733 assert_eq!(update(&mut func, &mut names), 0);
1734 }
1735
1736 #[test]
1739 fn an_answer_something_else_reads_stays_three_instructions() {
1740 let (mut names, mut func, block) = empty();
1741 let base = func.new_vreg(GPR);
1742 let other = func.new_vreg(GPR);
1743 let word = load(&mut func, &mut names, block, base);
1744 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1745 store(&mut func, &mut names, block, base, sum);
1746 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1747
1748 assert_eq!(update(&mut func, &mut names), 0);
1749 }
1750
1751 #[test]
1754 fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1755 let (mut names, mut func, block) = empty();
1756 let base = func.new_vreg(GPR);
1757 let other = func.new_vreg(GPR);
1758 let word = load(&mut func, &mut names, block, base);
1759 load(&mut func, &mut names, block, other);
1760 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1761 store(&mut func, &mut names, block, base, sum);
1762
1763 assert_eq!(update(&mut func, &mut names), 0);
1764 }
1765
1766 #[test]
1769 fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1770 let (mut names, mut func, block) = empty();
1771 let base = Reg::physical(rucc_target::x86_64::RSP);
1772 let other = func.new_vreg(GPR);
1773 let word = load(&mut func, &mut names, block, base);
1774 let sub = op(&mut names, "sub_ri_64");
1775 func.build(block, sub)
1776 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1777 .uses(base, GPR)
1778 .imm(32)
1779 .finish();
1780 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1781 store(&mut func, &mut names, block, base, sum);
1782
1783 assert_eq!(update(&mut func, &mut names), 0);
1784 }
1785
1786 #[test]
1790 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1791 let (mut names, mut func, block) = empty();
1792 let base = Reg::physical(rucc_target::x86_64::RSP);
1793 let other = func.new_vreg(GPR);
1794 let mov = op(&mut names, "mov_rm_64");
1795 let word = func.new_vreg(GPR);
1796 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1797 let read = func.insts(block).next().expect("the load");
1798 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1799 let put = op(&mut names, "mov_mr_64");
1800 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1801 let written = func.insts(block).nth(2).expect("the store");
1802
1803 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1804 let mut arguments = Vec::new();
1805 let mut dynamic = Vec::new();
1806 let mut pending =
1807 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1808 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1809 }
1810
1811 #[test]
1815 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1816 let (mut names, mut func, block) = empty();
1817 let base = Reg::physical(rucc_target::x86_64::RSP);
1818 let other = func.new_vreg(GPR);
1819 let mov = op(&mut names, "mov_rm_64");
1820 let word = func.new_vreg(GPR);
1821 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1822 let read = func.insts(block).next().expect("the load");
1823 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1824 let put = op(&mut names, "mov_mr_64");
1825 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1826 let written = func.insts(block).nth(2).expect("the store");
1827
1828 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1829 let mut arguments = Vec::new();
1830 let mut dynamic = Vec::new();
1831 let mut pending =
1832 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1833 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
1834
1835 let inst = func.insts(block).next().expect("the addition");
1836 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1837 }
1838
1839 #[test]
1841 fn a_run_whose_widths_disagree_stays_three_instructions() {
1842 let (mut names, mut func, block) = empty();
1843 let base = func.new_vreg(GPR);
1844 let other = func.new_vreg(GPR);
1845 let into = func.new_vreg(GPR);
1846 let narrow = op(&mut names, "mov_rm_32");
1847 func.build(block, narrow)
1848 .def(into, GPR)
1849 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1850 .finish();
1851 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1852 store(&mut func, &mut names, block, base, sum);
1853
1854 assert_eq!(update(&mut func, &mut names), 0);
1855 }
1856
1857 #[test]
1861 fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1862 let (mut names, mut func, block) = empty();
1863 let base = func.new_vreg(GPR);
1864 let other = func.new_vreg(GPR);
1865 let carry = func.new_vreg(GPR);
1866 let word = load(&mut func, &mut names, block, base);
1867 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1868 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1869 store(&mut func, &mut names, block, base, sum);
1870
1871 assert_eq!(update(&mut func, &mut names), 0);
1872 }
1873
1874 #[test]
1879 fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1880 let (mut names, mut func, block) = empty();
1881 let base = func.new_vreg(GPR);
1882 let other = func.new_vreg(GPR);
1883 let left = func.new_vreg(GPR);
1884 let right = func.new_vreg(GPR);
1885 let word = load(&mut func, &mut names, block, base);
1886 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1887 alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1888 store(&mut func, &mut names, block, base, sum);
1889
1890 assert_eq!(update(&mut func, &mut names), 0);
1891 }
1892
1893 #[test]
1896 fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1897 let (mut names, mut func, block) = empty();
1898 let base = func.new_vreg(GPR);
1899 let other = func.new_vreg(GPR);
1900 let from = func.new_vreg(GPR);
1901 let into = func.new_vreg(GPR);
1902 let word = load(&mut func, &mut names, block, base);
1903 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1904 let copy = op(&mut names, "mov_rr_64");
1905 func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1906 store(&mut func, &mut names, block, base, sum);
1907
1908 assert_eq!(update(&mut func, &mut names), 1);
1909 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1910 }
1911
1912 #[test]
1914 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1915 for update in UPDATES {
1916 for name in [update.from, update.into, update.load, update.store] {
1917 assert!(MACHINE.has(name), "{name} is not an instruction");
1918 }
1919 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1920 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1921 assert_eq!(
1922 width(update.from),
1923 width(update.load),
1924 "{} loads another width",
1925 update.from
1926 );
1927 assert_eq!(
1928 width(update.from),
1929 width(update.store),
1930 "{} stores another width",
1931 update.from
1932 );
1933 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1934 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1935 }
1936 }
1937
1938 #[test]
1941 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1942 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1943 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1944 assert_eq!(commuting, 16, "everything but the four subtractions");
1945 }
1946
1947 fn alu_imm(
1949 func: &mut Func,
1950 names: &mut Interner,
1951 block: mir::Block,
1952 name: &str,
1953 source: Reg,
1954 value: i64,
1955 ) -> Reg {
1956 let answer = func.new_vreg(GPR);
1957 let opcode = op(names, name);
1958 func.build(block, opcode)
1959 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1960 .uses(source, GPR)
1961 .imm(value)
1962 .finish();
1963 answer
1964 }
1965
1966 #[test]
1968 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1969 let (mut names, mut func, block) = empty();
1970 let base = func.new_vreg(GPR);
1971 let word = load(&mut func, &mut names, block, base);
1972 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1973 store(&mut func, &mut names, block, base, sum);
1974
1975 assert_eq!(update(&mut func, &mut names), 1);
1976 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1977 let inst = func.insts(block).next().expect("the addition");
1978 let mem = func[inst].mem.expect("it writes memory");
1979 assert_eq!(func[mem].disp, 16, "the address came from the store");
1980 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1981 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1982 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1983 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1984 }
1985
1986 #[test]
1989 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1990 let (mut names, mut func, block) = empty();
1991 let base = func.new_vreg(GPR);
1992 let word = load(&mut func, &mut names, block, base);
1993 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1994 store(&mut func, &mut names, block, base, left);
1995
1996 assert_eq!(update(&mut func, &mut names), 1);
1997 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1998 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1999 }
2000
2001 #[test]
2004 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
2005 let (mut names, mut func, block) = empty();
2006 let base = func.new_vreg(GPR);
2007 let word = func.new_vreg(GPR);
2008 let mov = op(&mut names, "mov_rm_8");
2009 func.build(block, mov)
2010 .def(word, GPR)
2011 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2012 .finish();
2013 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
2014 let put = op(&mut names, "mov_mr_8");
2015 func.build(block, put)
2016 .uses(sum, GPR)
2017 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2018 .finish();
2019
2020 assert_eq!(update(&mut func, &mut names), 1);
2021 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
2022 }
2023
2024 #[test]
2027 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
2028 let (mut names, mut func, block) = empty();
2029 let base = func.new_vreg(GPR);
2030 let other = func.new_vreg(GPR);
2031 let word = load(&mut func, &mut names, block, base);
2032 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2033 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
2034 store(&mut func, &mut names, block, base, sum);
2035
2036 assert_eq!(update(&mut func, &mut names), 0);
2037 }
2038
2039 #[test]
2042 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
2043 let (mut names, mut func, block) = empty();
2044 let base = func.new_vreg(GPR);
2045 let elsewhere = func.new_vreg(GPR);
2046 let word = load(&mut func, &mut names, block, base);
2047 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2048 load(&mut func, &mut names, block, elsewhere);
2049 store(&mut func, &mut names, block, base, sum);
2050
2051 assert_eq!(update(&mut func, &mut names), 0);
2052 }
2053
2054 #[test]
2057 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
2058 let (mut names, mut func, block) = empty();
2059 let base = Reg::physical(rucc_target::x86_64::RAX);
2060 let word = load(&mut func, &mut names, block, base);
2061 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2062 let mov = op(&mut names, "mov_ri_64");
2063 func.build(block, mov).def(base, GPR).imm(0).finish();
2064 store(&mut func, &mut names, block, base, sum);
2065
2066 assert_eq!(update(&mut func, &mut names), 0);
2067 }
2068
2069 #[test]
2071 fn a_constant_written_to_another_address_stays_three_instructions() {
2072 let (mut names, mut func, block) = empty();
2073 let base = func.new_vreg(GPR);
2074 let elsewhere = func.new_vreg(GPR);
2075 let word = load(&mut func, &mut names, block, base);
2076 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2077 store(&mut func, &mut names, block, elsewhere, sum);
2078
2079 assert_eq!(update(&mut func, &mut names), 0);
2080 }
2081
2082 #[test]
2084 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2085 let (mut names, mut func, block) = empty();
2086 let base = func.new_vreg(GPR);
2087 let into = func.new_vreg(GPR);
2088 let narrow = op(&mut names, "mov_rm_32");
2089 func.build(block, narrow)
2090 .def(into, GPR)
2091 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2092 .finish();
2093 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2094 store(&mut func, &mut names, block, base, sum);
2095
2096 assert_eq!(update(&mut func, &mut names), 0);
2097 }
2098
2099 #[test]
2102 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2103 let (mut names, mut func, block) = empty();
2104 let base = func.new_vreg(GPR);
2105 let word = load(&mut func, &mut names, block, base);
2106 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2107 store(&mut func, &mut names, block, base, product);
2108
2109 assert_eq!(update(&mut func, &mut names), 0);
2110 }
2111
2112 #[test]
2115 fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2116 let (mut names, mut func, block) = empty();
2117 let base = func.new_vreg(GPR);
2118 let carry = func.new_vreg(GPR);
2119 let word = load(&mut func, &mut names, block, base);
2120 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2121 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2122 store(&mut func, &mut names, block, base, sum);
2123
2124 assert_eq!(update(&mut func, &mut names), 0);
2125 }
2126
2127 #[test]
2130 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2131 let (mut names, mut func, block) = empty();
2132 let base = Reg::physical(rucc_target::x86_64::RSP);
2133 let mov = op(&mut names, "mov_rm_64");
2134 let word = func.new_vreg(GPR);
2135 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2136 let read = func.insts(block).next().expect("the load");
2137 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2138 let put = op(&mut names, "mov_mr_64");
2139 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2140 let written = func.insts(block).nth(2).expect("the store");
2141
2142 let mut addresses = vec![(read, 3usize), (written, 3usize)];
2143 let mut arguments = Vec::new();
2144 let mut dynamic = Vec::new();
2145 let mut pending =
2146 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2147 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2148
2149 let inst = func.insts(block).next().expect("the addition");
2150 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2151 }
2152
2153 #[test]
2156 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
2157 let (mut names, mut func, block) = empty();
2158 let base = Reg::physical(rucc_target::x86_64::RSP);
2159 let mov = op(&mut names, "mov_rm_64");
2160 let word = func.new_vreg(GPR);
2161 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2162 let read = func.insts(block).next().expect("the load");
2163 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2164 let put = op(&mut names, "mov_mr_64");
2165 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2166 let written = func.insts(block).nth(2).expect("the store");
2167
2168 let mut addresses = vec![(read, 3usize), (written, 4usize)];
2169 let mut arguments = Vec::new();
2170 let mut dynamic = Vec::new();
2171 let mut pending =
2172 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2173 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
2174 }
2175
2176 #[test]
2178 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2179 for bump in BUMPS {
2180 for name in [bump.from, bump.into, bump.load, bump.store] {
2181 assert!(MACHINE.has(name), "{name} is not an instruction");
2182 }
2183 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2184 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2185 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2186 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2187 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2188 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2189 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2190 }
2191 }
2192
2193 #[test]
2196 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2197 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2198 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2199 for bump in BUMPS {
2200 let same = bump.from.replace("_ri_", "_rr_");
2201 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2202 }
2203 }
2204
2205 #[test]
2208 fn nothing_is_both_a_register_run_and_a_constant_run() {
2209 for bump in BUMPS {
2210 assert!(
2211 !UPDATES.iter().any(|update| update.from == bump.from),
2212 "{} starts both kinds of run",
2213 bump.from
2214 );
2215 }
2216 }
2217
2218 #[test]
2220 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2221 let (mut names, mut func, block) = empty();
2222 let base = func.new_vreg(GPR);
2223 let other = func.new_vreg(GPR);
2224 let word = load(&mut func, &mut names, block, base);
2225 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2226
2227 assert_eq!(combine(&mut func, &mut names), 1);
2228 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2229 let inst = func.insts(block).next().expect("the addition");
2230 let mem = func[inst].mem.expect("the addition reads memory now");
2231 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2232 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2233 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2234 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2235 }
2236
2237 #[test]
2240 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2241 let (mut names, mut func, block) = empty();
2242 let base = func.new_vreg(GPR);
2243 let other = func.new_vreg(GPR);
2244 let word = load(&mut func, &mut names, block, base);
2245 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2246
2247 assert_eq!(combine(&mut func, &mut names), 1);
2248 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2249 let inst = func.insts(block).next().expect("the addition");
2250 assert_eq!(func[func[inst].operands][1].reg, other);
2251 }
2252
2253 #[test]
2256 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2257 let (mut names, mut func, block) = empty();
2258 let base = func.new_vreg(GPR);
2259 let other = func.new_vreg(GPR);
2260 let word = load(&mut func, &mut names, block, base);
2261 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2262
2263 assert_eq!(combine(&mut func, &mut names), 0);
2264 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2265 }
2266
2267 #[test]
2269 fn a_load_feeding_the_right_of_a_subtraction_folds() {
2270 let (mut names, mut func, block) = empty();
2271 let base = func.new_vreg(GPR);
2272 let other = func.new_vreg(GPR);
2273 let word = load(&mut func, &mut names, block, base);
2274 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2275
2276 assert_eq!(combine(&mut func, &mut names), 1);
2277 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2278 }
2279
2280 #[test]
2283 fn a_load_two_instructions_read_stays_a_load() {
2284 let (mut names, mut func, block) = empty();
2285 let base = func.new_vreg(GPR);
2286 let other = func.new_vreg(GPR);
2287 let word = load(&mut func, &mut names, block, base);
2288 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2289 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2290
2291 assert_eq!(combine(&mut func, &mut names), 0);
2292 assert_eq!(
2293 shape(&func, &names, block),
2294 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2295 );
2296 }
2297
2298 #[test]
2301 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2302 let (mut names, mut func, block) = empty();
2303 let base = func.new_vreg(GPR);
2304 let other = func.new_vreg(GPR);
2305 let word = load(&mut func, &mut names, block, base);
2306 let store = op(&mut names, "mov_mr_64");
2307 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2308 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2309
2310 assert_eq!(combine(&mut func, &mut names), 0);
2311 assert_eq!(
2312 shape(&func, &names, block),
2313 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2314 );
2315 }
2316
2317 #[test]
2323 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2324 let (mut names, mut func, block) = empty();
2325 let base = func.new_vreg(GPR);
2326 let other = func.new_vreg(GPR);
2327 let word = load(&mut func, &mut names, block, base);
2328 load(&mut func, &mut names, block, other);
2329 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2330
2331 assert_eq!(combine(&mut func, &mut names), 0);
2332 assert_eq!(
2333 shape(&func, &names, block),
2334 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2335 );
2336 }
2337
2338 #[test]
2341 fn the_later_of_two_loads_is_the_one_that_folds() {
2342 let (mut names, mut func, block) = empty();
2343 let base = func.new_vreg(GPR);
2344 let other = func.new_vreg(GPR);
2345 let first = load(&mut func, &mut names, block, base);
2346 let second = load(&mut func, &mut names, block, other);
2347 alu(&mut func, &mut names, block, "add_rr_64", first, second);
2348
2349 assert_eq!(combine(&mut func, &mut names), 1);
2350 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2351 let addition = func.insts(block).nth(1).expect("the addition");
2352 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2353 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2354 }
2355
2356 #[test]
2359 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2360 let (mut names, mut func, block) = empty();
2361 let base = func.new_vreg(GPR);
2362 let other = func.new_vreg(GPR);
2363 let word = load(&mut func, &mut names, block, base);
2364 let call = op(&mut names, "call");
2365 func.build(block, call).finish();
2366 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2367
2368 assert_eq!(combine(&mut func, &mut names), 0);
2369 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2370 }
2371
2372 #[test]
2375 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2376 let (mut names, mut func, block) = empty();
2377 let base = Reg::physical(rucc_target::x86_64::RSP);
2378 let other = func.new_vreg(GPR);
2379 let word = load(&mut func, &mut names, block, base);
2380 let sub = op(&mut names, "sub_ri_64");
2381 func.build(block, sub)
2382 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2383 .uses(base, GPR)
2384 .imm(32)
2385 .finish();
2386 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2387
2388 assert_eq!(combine(&mut func, &mut names), 0);
2389 }
2390
2391 #[test]
2394 fn a_load_of_the_wrong_width_stays_a_load() {
2395 let (mut names, mut func, block) = empty();
2396 let base = func.new_vreg(GPR);
2397 let other = func.new_vreg(GPR);
2398 let into = func.new_vreg(GPR);
2399 let narrow = op(&mut names, "mov_rm_32");
2400 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2401 alu(&mut func, &mut names, block, "add_rr_64", other, into);
2402
2403 assert_eq!(combine(&mut func, &mut names), 0);
2404 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2405 }
2406
2407 #[test]
2410 fn a_load_whose_value_an_edge_carries_stays_a_load() {
2411 let (mut names, mut func, block) = empty();
2412 let next = func.create_block();
2413 let base = func.new_vreg(GPR);
2414 let other = func.new_vreg(GPR);
2415 let word = load(&mut func, &mut names, block, base);
2416 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2417 let arrived = func.new_vreg(GPR);
2418 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2419 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2420
2421 assert_eq!(combine(&mut func, &mut names), 0);
2422 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2423 }
2424
2425 #[test]
2427 fn a_reader_in_another_block_stays_where_it_is() {
2428 let (mut names, mut func, block) = empty();
2429 let next = func.create_block();
2430 let base = func.new_vreg(GPR);
2431 let other = func.new_vreg(GPR);
2432 let word = load(&mut func, &mut names, block, base);
2433 alu(&mut func, &mut names, next, "add_rr_64", other, word);
2434
2435 assert_eq!(combine(&mut func, &mut names), 0);
2436 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2437 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2438 }
2439
2440 #[test]
2442 fn a_reader_past_the_window_stays_where_it_is() {
2443 let (mut names, mut func, block) = empty();
2444 let base = func.new_vreg(GPR);
2445 let other = func.new_vreg(GPR);
2446 let word = load(&mut func, &mut names, block, base);
2447 let nop = op(&mut names, "nop");
2448 for _ in 0..WINDOW {
2449 func.build(block, nop).finish();
2450 }
2451 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2452
2453 assert_eq!(combine(&mut func, &mut names), 0);
2454 }
2455
2456 #[test]
2458 fn a_reader_at_the_edge_of_the_window_folds() {
2459 let (mut names, mut func, block) = empty();
2460 let base = func.new_vreg(GPR);
2461 let other = func.new_vreg(GPR);
2462 let word = load(&mut func, &mut names, block, base);
2463 let nop = op(&mut names, "nop");
2464 for _ in 0..WINDOW - 1 {
2465 func.build(block, nop).finish();
2466 }
2467 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2468
2469 assert_eq!(combine(&mut func, &mut names), 1);
2470 }
2471
2472 #[test]
2475 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2476 let (mut names, mut func, block) = empty();
2477 let base = Reg::physical(rucc_target::x86_64::RSP);
2478 let other = func.new_vreg(GPR);
2479 let word = load(&mut func, &mut names, block, base);
2480 let reader = func.insts(block).nth(1);
2481 assert!(reader.is_none(), "the block holds the load alone so far");
2482 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2483 let held = func.insts(block).next().expect("the load");
2484
2485 let mut addresses = vec![(held, 3usize)];
2486 let mut arguments = Vec::new();
2487 let mut dynamic = Vec::new();
2488 let mut pending =
2489 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2490 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2491
2492 let inst = func.insts(block).next().expect("the addition");
2493 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2494 }
2495
2496 #[test]
2500 fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2501 let (mut names, mut func, block) = empty();
2502 let base = func.new_vreg(GPR);
2503 let other = func.new_vreg(GPR);
2504 let word = load(&mut func, &mut names, block, base);
2505 compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2506
2507 assert_eq!(combine(&mut func, &mut names), 1);
2508 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2509 let inst = func.insts(block).next().expect("the comparison");
2510 let mem = func[inst].mem.expect("it reads memory");
2511 assert_eq!(func[mem].disp, 16, "the address came from the load");
2512 assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2513 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2514 assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2515 }
2516
2517 #[test]
2521 fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2522 let (mut names, mut func, block) = empty();
2523 let base = func.new_vreg(GPR);
2524 let other = func.new_vreg(GPR);
2525 let word = load(&mut func, &mut names, block, base);
2526 compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2527
2528 assert_eq!(combine(&mut func, &mut names), 1);
2529 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2530 let inst = func.insts(block).next().expect("the comparison");
2531 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2532 }
2533
2534 #[test]
2538 fn an_equality_folded_on_either_side_is_the_same_comparison() {
2539 for (first, second) in [(true, false), (false, true)] {
2540 let (mut names, mut func, block) = empty();
2541 let base = func.new_vreg(GPR);
2542 let other = func.new_vreg(GPR);
2543 let word = load(&mut func, &mut names, block, base);
2544 let left = if first { word } else { other };
2545 let right = if second { word } else { other };
2546 compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2547
2548 assert_eq!(combine(&mut func, &mut names), 1);
2549 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2550 }
2551 }
2552
2553 #[test]
2558 fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2559 let (mut names, mut func, block) = empty();
2560 let base = func.new_vreg(GPR);
2561 let byte = func.new_vreg(GPR);
2562 let word = load(&mut func, &mut names, block, base);
2563 let opcode = op(&mut names, "cmp_set_l_ri_64");
2564 func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2565
2566 assert_eq!(combine(&mut func, &mut names), 1);
2567 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2568 let inst = func.insts(block).next().expect("the comparison");
2569 let mem = func[inst].mem.expect("it reads memory now");
2570 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2571 assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2572 assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2573 assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2574 let imm = func[inst].imm.expect("the constant is still on it");
2575 assert_eq!(func[imm].0, 7, "and is the one that was written");
2576 }
2577
2578 #[test]
2581 fn a_load_only_a_widening_reads_becomes_a_load_that_widens() {
2582 for row in WIDENINGS {
2583 let (mut names, mut func, block) = empty();
2584 let base = func.new_vreg(GPR);
2585 let narrow = func.new_vreg(GPR);
2586 let wide = func.new_vreg(GPR);
2587 let read = op(&mut names, row.load);
2588 func.build(block, read)
2589 .def(narrow, GPR)
2590 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2591 .finish();
2592 let widen = op(&mut names, row.from);
2593 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2594
2595 assert_eq!(combine(&mut func, &mut names), 1, "{} took no load", row.from);
2596 assert_eq!(shape(&func, &names, block), [format!("x64.{}", row.into)]);
2597 let inst = func.insts(block).next().expect("the widening");
2598 assert_eq!(func[func[inst].operands][0].reg, wide, "{} writes elsewhere", row.from);
2599 assert_eq!(func[func[inst].operands][1].reg, base, "{} lost the address", row.from);
2600 let mem = func[inst].mem.expect("it reads memory now");
2601 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2602 assert_eq!(func[mem].base, Some(1), "and names the operand behind the answer");
2603 }
2604 }
2605
2606 #[test]
2609 fn a_load_read_by_a_widening_and_something_else_stays_where_it_is() {
2610 let (mut names, mut func, block) = empty();
2611 let base = func.new_vreg(GPR);
2612 let other = func.new_vreg(GPR);
2613 let narrow = func.new_vreg(GPR);
2614 let wide = func.new_vreg(GPR);
2615 let read = op(&mut names, "mov_rm_32");
2616 func.build(block, read)
2617 .def(narrow, GPR)
2618 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2619 .finish();
2620 let widen = op(&mut names, "movsxd_32_64");
2621 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2622 alu(&mut func, &mut names, block, "add_rr_32", other, narrow);
2623
2624 assert_eq!(combine(&mut func, &mut names), 0);
2625 assert_eq!(
2626 shape(&func, &names, block),
2627 ["x64.mov_rm_32", "x64.movsxd_32_64", "x64.add_rr_32"]
2628 );
2629 }
2630
2631 #[test]
2634 fn a_volatile_load_is_not_widened_on_the_way_in() {
2635 let (mut names, mut func, block) = empty();
2636 let base = func.new_vreg(GPR);
2637 let narrow = func.new_vreg(GPR);
2638 let wide = func.new_vreg(GPR);
2639 let read = op(&mut names, "mov_rm_16");
2640 func.build(block, read)
2641 .def(narrow, GPR)
2642 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2643 .flags(Flags::VOLATILE)
2644 .finish();
2645 let widen = op(&mut names, "movsx_16_32");
2646 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2647
2648 assert_eq!(combine(&mut func, &mut names), 0);
2649 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_16", "x64.movsx_16_32"]);
2650 }
2651
2652 #[test]
2655 fn every_widening_takes_a_load_of_the_width_it_widens_from() {
2656 let from = |name: &str| {
2657 name.split('_').find(|part| part.parse::<u32>().is_ok()).map(str::to_owned)
2658 };
2659 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2660 for row in WIDENINGS {
2661 assert!(MACHINE.has(row.from), "{} is not an instruction", row.from);
2662 assert!(MACHINE.has(row.into), "{} is not an instruction", row.into);
2663 assert!(MACHINE.has(row.load), "{} is not an instruction", row.load);
2664 assert_eq!(from(row.from), width(row.load), "{} loads another width", row.from);
2665 assert!((MACHINE.takes_mem)(row.into), "{} reads no memory", row.into);
2666 assert!(!(MACHINE.takes_mem)(row.from), "{} already reads memory", row.from);
2667 assert_eq!(row.swapped, None, "{} has nothing to swap", row.from);
2668 }
2669 }
2670
2671 #[test]
2675 fn every_row_of_the_table_is_three_instructions_this_target_has() {
2676 for fold in FOLDS {
2677 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2678 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2679 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2680 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2681 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2682 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2683 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2684 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2685 let Some(swapped) = fold.swapped else { continue };
2686 assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2687 assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2688 assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2689 }
2690 }
2691
2692 #[test]
2696 fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2697 let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2698 assert_eq!(
2699 compares, 80,
2700 "ten conditions at four widths, against a register and a constant"
2701 );
2702 let arithmetic = FOLDS.len() - compares;
2703 assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2704 let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2705 assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2706 }
2707
2708 #[test]
2716 fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2717 let turned = |condition: &str| match condition {
2718 "e" => "e",
2719 "ne" => "ne",
2720 "l" => "g",
2721 "g" => "l",
2722 "le" => "ge",
2723 "ge" => "le",
2724 "b" => "a",
2725 "a" => "b",
2726 "be" => "ae",
2727 "ae" => "be",
2728 other => panic!("{other} is not a condition this machine has"),
2729 };
2730 let compares = FOLDS
2731 .iter()
2732 .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2733 for fold in compares {
2734 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2735 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2736 assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2737 let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2738 assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2739 }
2740 }
2741
2742 #[test]
2747 fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2748 let compares = FOLDS
2749 .iter()
2750 .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2751 let mut rows = 0;
2752 for fold in compares {
2753 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2754 let front = front.strip_suffix("_ri").expect("a name against a constant");
2755 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2756 assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2757 assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2758 assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2759 rows += 1;
2760 }
2761 assert_eq!(rows, 40, "ten conditions at four widths");
2762 }
2763
2764 #[test]
2771 fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2772 let (mut names, mut func, block) = empty();
2773 let base = func.new_vreg(GPR);
2774 let other = func.new_vreg(GPR);
2775 let word = insisted_load(&mut func, &mut names, block, base);
2776 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2777
2778 assert_eq!(combine(&mut func, &mut names), 0);
2779 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2780 }
2781
2782 #[test]
2786 fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2787 let (mut names, mut func, block) = empty();
2788 let base = func.new_vreg(GPR);
2789 let other = func.new_vreg(GPR);
2790 let word = insisted_load(&mut func, &mut names, block, base);
2791 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2792 store(&mut func, &mut names, block, base, sum);
2793
2794 assert_eq!(update(&mut func, &mut names), 0);
2795 }
2796
2797 #[test]
2802 fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2803 let (mut names, mut func, block) = empty();
2804 let base = func.new_vreg(GPR);
2805 let other = func.new_vreg(GPR);
2806 let word = load(&mut func, &mut names, block, base);
2807 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2808 insisted_store(&mut func, &mut names, block, base, sum);
2809
2810 assert_eq!(update(&mut func, &mut names), 0);
2811 }
2812
2813 #[test]
2816 fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2817 let (mut names, mut func, block) = empty();
2818 let base = func.new_vreg(GPR);
2819 let word = insisted_load(&mut func, &mut names, block, base);
2820 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2821 store(&mut func, &mut names, block, base, sum);
2822
2823 assert_eq!(update(&mut func, &mut names), 0);
2824 }
2825
2826 #[test]
2828 fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2829 let (mut names, mut func, block) = empty();
2830 let base = func.new_vreg(GPR);
2831 let word = load(&mut func, &mut names, block, base);
2832 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2833 insisted_store(&mut func, &mut names, block, base, sum);
2834
2835 assert_eq!(update(&mut func, &mut names), 0);
2836 }
2837
2838 #[test]
2841 fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2842 let (mut names, mut func, block) = empty();
2843 let base = func.new_vreg(GPR);
2844 let other = func.new_vreg(GPR);
2845 let word = load(&mut func, &mut names, block, base);
2846 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2847 store(&mut func, &mut names, block, base, sum);
2848
2849 assert_eq!(update(&mut func, &mut names), 1);
2850 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2851 }
2852}