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