1use rucc_base::Interner;
301use rucc_mir::{Amode, Flags, Func, Inst, Opcode, Operand, Reg};
302use rucc_target::{FlagInsts, MachineInsts};
303
304use crate::changes::{Changes, Plan, Reads};
305use crate::fold::Pending;
306
307pub const WINDOW: usize = 16;
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct Fold {
334 pub from: &'static str,
336 pub into: &'static str,
338 pub load: &'static str,
340 pub swapped: Option<&'static str>,
356}
357
358pub static FOLDS: &[Fold] = &[
373 Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", swapped: Some("add_rm_8") },
374 Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", swapped: Some("add_rm_16") },
375 Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", swapped: Some("add_rm_32") },
376 Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", swapped: Some("add_rm_64") },
377 Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", swapped: None },
378 Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", swapped: None },
379 Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", swapped: None },
380 Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", swapped: None },
381 Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", swapped: Some("and_rm_8") },
382 Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", swapped: Some("and_rm_16") },
383 Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", swapped: Some("and_rm_32") },
384 Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", swapped: Some("and_rm_64") },
385 Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", swapped: Some("or_rm_8") },
386 Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", swapped: Some("or_rm_16") },
387 Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", swapped: Some("or_rm_32") },
388 Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", swapped: Some("or_rm_64") },
389 Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", swapped: Some("xor_rm_8") },
390 Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", swapped: Some("xor_rm_16") },
391 Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", swapped: Some("xor_rm_32") },
392 Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", swapped: Some("xor_rm_64") },
393 Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", swapped: Some("imul_rm_16") },
394 Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", swapped: Some("imul_rm_32") },
395 Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", swapped: Some("imul_rm_64") },
396 Fold {
397 from: "cmp_set_e_8",
398 into: "cmp_set_e_rm_8",
399 load: "mov_rm_8",
400 swapped: Some("cmp_set_e_rm_8"),
401 },
402 Fold {
403 from: "cmp_set_e_16",
404 into: "cmp_set_e_rm_16",
405 load: "mov_rm_16",
406 swapped: Some("cmp_set_e_rm_16"),
407 },
408 Fold {
409 from: "cmp_set_e_32",
410 into: "cmp_set_e_rm_32",
411 load: "mov_rm_32",
412 swapped: Some("cmp_set_e_rm_32"),
413 },
414 Fold {
415 from: "cmp_set_e_64",
416 into: "cmp_set_e_rm_64",
417 load: "mov_rm_64",
418 swapped: Some("cmp_set_e_rm_64"),
419 },
420 Fold {
421 from: "cmp_set_ne_8",
422 into: "cmp_set_ne_rm_8",
423 load: "mov_rm_8",
424 swapped: Some("cmp_set_ne_rm_8"),
425 },
426 Fold {
427 from: "cmp_set_ne_16",
428 into: "cmp_set_ne_rm_16",
429 load: "mov_rm_16",
430 swapped: Some("cmp_set_ne_rm_16"),
431 },
432 Fold {
433 from: "cmp_set_ne_32",
434 into: "cmp_set_ne_rm_32",
435 load: "mov_rm_32",
436 swapped: Some("cmp_set_ne_rm_32"),
437 },
438 Fold {
439 from: "cmp_set_ne_64",
440 into: "cmp_set_ne_rm_64",
441 load: "mov_rm_64",
442 swapped: Some("cmp_set_ne_rm_64"),
443 },
444 Fold {
445 from: "cmp_set_l_8",
446 into: "cmp_set_l_rm_8",
447 load: "mov_rm_8",
448 swapped: Some("cmp_set_g_rm_8"),
449 },
450 Fold {
451 from: "cmp_set_l_16",
452 into: "cmp_set_l_rm_16",
453 load: "mov_rm_16",
454 swapped: Some("cmp_set_g_rm_16"),
455 },
456 Fold {
457 from: "cmp_set_l_32",
458 into: "cmp_set_l_rm_32",
459 load: "mov_rm_32",
460 swapped: Some("cmp_set_g_rm_32"),
461 },
462 Fold {
463 from: "cmp_set_l_64",
464 into: "cmp_set_l_rm_64",
465 load: "mov_rm_64",
466 swapped: Some("cmp_set_g_rm_64"),
467 },
468 Fold {
469 from: "cmp_set_le_8",
470 into: "cmp_set_le_rm_8",
471 load: "mov_rm_8",
472 swapped: Some("cmp_set_ge_rm_8"),
473 },
474 Fold {
475 from: "cmp_set_le_16",
476 into: "cmp_set_le_rm_16",
477 load: "mov_rm_16",
478 swapped: Some("cmp_set_ge_rm_16"),
479 },
480 Fold {
481 from: "cmp_set_le_32",
482 into: "cmp_set_le_rm_32",
483 load: "mov_rm_32",
484 swapped: Some("cmp_set_ge_rm_32"),
485 },
486 Fold {
487 from: "cmp_set_le_64",
488 into: "cmp_set_le_rm_64",
489 load: "mov_rm_64",
490 swapped: Some("cmp_set_ge_rm_64"),
491 },
492 Fold {
493 from: "cmp_set_g_8",
494 into: "cmp_set_g_rm_8",
495 load: "mov_rm_8",
496 swapped: Some("cmp_set_l_rm_8"),
497 },
498 Fold {
499 from: "cmp_set_g_16",
500 into: "cmp_set_g_rm_16",
501 load: "mov_rm_16",
502 swapped: Some("cmp_set_l_rm_16"),
503 },
504 Fold {
505 from: "cmp_set_g_32",
506 into: "cmp_set_g_rm_32",
507 load: "mov_rm_32",
508 swapped: Some("cmp_set_l_rm_32"),
509 },
510 Fold {
511 from: "cmp_set_g_64",
512 into: "cmp_set_g_rm_64",
513 load: "mov_rm_64",
514 swapped: Some("cmp_set_l_rm_64"),
515 },
516 Fold {
517 from: "cmp_set_ge_8",
518 into: "cmp_set_ge_rm_8",
519 load: "mov_rm_8",
520 swapped: Some("cmp_set_le_rm_8"),
521 },
522 Fold {
523 from: "cmp_set_ge_16",
524 into: "cmp_set_ge_rm_16",
525 load: "mov_rm_16",
526 swapped: Some("cmp_set_le_rm_16"),
527 },
528 Fold {
529 from: "cmp_set_ge_32",
530 into: "cmp_set_ge_rm_32",
531 load: "mov_rm_32",
532 swapped: Some("cmp_set_le_rm_32"),
533 },
534 Fold {
535 from: "cmp_set_ge_64",
536 into: "cmp_set_ge_rm_64",
537 load: "mov_rm_64",
538 swapped: Some("cmp_set_le_rm_64"),
539 },
540 Fold {
541 from: "cmp_set_b_8",
542 into: "cmp_set_b_rm_8",
543 load: "mov_rm_8",
544 swapped: Some("cmp_set_a_rm_8"),
545 },
546 Fold {
547 from: "cmp_set_b_16",
548 into: "cmp_set_b_rm_16",
549 load: "mov_rm_16",
550 swapped: Some("cmp_set_a_rm_16"),
551 },
552 Fold {
553 from: "cmp_set_b_32",
554 into: "cmp_set_b_rm_32",
555 load: "mov_rm_32",
556 swapped: Some("cmp_set_a_rm_32"),
557 },
558 Fold {
559 from: "cmp_set_b_64",
560 into: "cmp_set_b_rm_64",
561 load: "mov_rm_64",
562 swapped: Some("cmp_set_a_rm_64"),
563 },
564 Fold {
565 from: "cmp_set_be_8",
566 into: "cmp_set_be_rm_8",
567 load: "mov_rm_8",
568 swapped: Some("cmp_set_ae_rm_8"),
569 },
570 Fold {
571 from: "cmp_set_be_16",
572 into: "cmp_set_be_rm_16",
573 load: "mov_rm_16",
574 swapped: Some("cmp_set_ae_rm_16"),
575 },
576 Fold {
577 from: "cmp_set_be_32",
578 into: "cmp_set_be_rm_32",
579 load: "mov_rm_32",
580 swapped: Some("cmp_set_ae_rm_32"),
581 },
582 Fold {
583 from: "cmp_set_be_64",
584 into: "cmp_set_be_rm_64",
585 load: "mov_rm_64",
586 swapped: Some("cmp_set_ae_rm_64"),
587 },
588 Fold {
589 from: "cmp_set_a_8",
590 into: "cmp_set_a_rm_8",
591 load: "mov_rm_8",
592 swapped: Some("cmp_set_b_rm_8"),
593 },
594 Fold {
595 from: "cmp_set_a_16",
596 into: "cmp_set_a_rm_16",
597 load: "mov_rm_16",
598 swapped: Some("cmp_set_b_rm_16"),
599 },
600 Fold {
601 from: "cmp_set_a_32",
602 into: "cmp_set_a_rm_32",
603 load: "mov_rm_32",
604 swapped: Some("cmp_set_b_rm_32"),
605 },
606 Fold {
607 from: "cmp_set_a_64",
608 into: "cmp_set_a_rm_64",
609 load: "mov_rm_64",
610 swapped: Some("cmp_set_b_rm_64"),
611 },
612 Fold {
613 from: "cmp_set_ae_8",
614 into: "cmp_set_ae_rm_8",
615 load: "mov_rm_8",
616 swapped: Some("cmp_set_be_rm_8"),
617 },
618 Fold {
619 from: "cmp_set_ae_16",
620 into: "cmp_set_ae_rm_16",
621 load: "mov_rm_16",
622 swapped: Some("cmp_set_be_rm_16"),
623 },
624 Fold {
625 from: "cmp_set_ae_32",
626 into: "cmp_set_ae_rm_32",
627 load: "mov_rm_32",
628 swapped: Some("cmp_set_be_rm_32"),
629 },
630 Fold {
631 from: "cmp_set_ae_64",
632 into: "cmp_set_ae_rm_64",
633 load: "mov_rm_64",
634 swapped: Some("cmp_set_be_rm_64"),
635 },
636 Fold { from: "cmp_set_e_ri_8", into: "cmp_set_e_mi_8", load: "mov_rm_8", swapped: None },
637 Fold { from: "cmp_set_e_ri_16", into: "cmp_set_e_mi_16", load: "mov_rm_16", swapped: None },
638 Fold { from: "cmp_set_e_ri_32", into: "cmp_set_e_mi_32", load: "mov_rm_32", swapped: None },
639 Fold { from: "cmp_set_e_ri_64", into: "cmp_set_e_mi_64", load: "mov_rm_64", swapped: None },
640 Fold { from: "cmp_set_ne_ri_8", into: "cmp_set_ne_mi_8", load: "mov_rm_8", swapped: None },
641 Fold { from: "cmp_set_ne_ri_16", into: "cmp_set_ne_mi_16", load: "mov_rm_16", swapped: None },
642 Fold { from: "cmp_set_ne_ri_32", into: "cmp_set_ne_mi_32", load: "mov_rm_32", swapped: None },
643 Fold { from: "cmp_set_ne_ri_64", into: "cmp_set_ne_mi_64", load: "mov_rm_64", swapped: None },
644 Fold { from: "cmp_set_l_ri_8", into: "cmp_set_l_mi_8", load: "mov_rm_8", swapped: None },
645 Fold { from: "cmp_set_l_ri_16", into: "cmp_set_l_mi_16", load: "mov_rm_16", swapped: None },
646 Fold { from: "cmp_set_l_ri_32", into: "cmp_set_l_mi_32", load: "mov_rm_32", swapped: None },
647 Fold { from: "cmp_set_l_ri_64", into: "cmp_set_l_mi_64", load: "mov_rm_64", swapped: None },
648 Fold { from: "cmp_set_le_ri_8", into: "cmp_set_le_mi_8", load: "mov_rm_8", swapped: None },
649 Fold { from: "cmp_set_le_ri_16", into: "cmp_set_le_mi_16", load: "mov_rm_16", swapped: None },
650 Fold { from: "cmp_set_le_ri_32", into: "cmp_set_le_mi_32", load: "mov_rm_32", swapped: None },
651 Fold { from: "cmp_set_le_ri_64", into: "cmp_set_le_mi_64", load: "mov_rm_64", swapped: None },
652 Fold { from: "cmp_set_g_ri_8", into: "cmp_set_g_mi_8", load: "mov_rm_8", swapped: None },
653 Fold { from: "cmp_set_g_ri_16", into: "cmp_set_g_mi_16", load: "mov_rm_16", swapped: None },
654 Fold { from: "cmp_set_g_ri_32", into: "cmp_set_g_mi_32", load: "mov_rm_32", swapped: None },
655 Fold { from: "cmp_set_g_ri_64", into: "cmp_set_g_mi_64", load: "mov_rm_64", swapped: None },
656 Fold { from: "cmp_set_ge_ri_8", into: "cmp_set_ge_mi_8", load: "mov_rm_8", swapped: None },
657 Fold { from: "cmp_set_ge_ri_16", into: "cmp_set_ge_mi_16", load: "mov_rm_16", swapped: None },
658 Fold { from: "cmp_set_ge_ri_32", into: "cmp_set_ge_mi_32", load: "mov_rm_32", swapped: None },
659 Fold { from: "cmp_set_ge_ri_64", into: "cmp_set_ge_mi_64", load: "mov_rm_64", swapped: None },
660 Fold { from: "cmp_set_b_ri_8", into: "cmp_set_b_mi_8", load: "mov_rm_8", swapped: None },
661 Fold { from: "cmp_set_b_ri_16", into: "cmp_set_b_mi_16", load: "mov_rm_16", swapped: None },
662 Fold { from: "cmp_set_b_ri_32", into: "cmp_set_b_mi_32", load: "mov_rm_32", swapped: None },
663 Fold { from: "cmp_set_b_ri_64", into: "cmp_set_b_mi_64", load: "mov_rm_64", swapped: None },
664 Fold { from: "cmp_set_be_ri_8", into: "cmp_set_be_mi_8", load: "mov_rm_8", swapped: None },
665 Fold { from: "cmp_set_be_ri_16", into: "cmp_set_be_mi_16", load: "mov_rm_16", swapped: None },
666 Fold { from: "cmp_set_be_ri_32", into: "cmp_set_be_mi_32", load: "mov_rm_32", swapped: None },
667 Fold { from: "cmp_set_be_ri_64", into: "cmp_set_be_mi_64", load: "mov_rm_64", swapped: None },
668 Fold { from: "cmp_set_a_ri_8", into: "cmp_set_a_mi_8", load: "mov_rm_8", swapped: None },
669 Fold { from: "cmp_set_a_ri_16", into: "cmp_set_a_mi_16", load: "mov_rm_16", swapped: None },
670 Fold { from: "cmp_set_a_ri_32", into: "cmp_set_a_mi_32", load: "mov_rm_32", swapped: None },
671 Fold { from: "cmp_set_a_ri_64", into: "cmp_set_a_mi_64", load: "mov_rm_64", swapped: None },
672 Fold { from: "cmp_set_ae_ri_8", into: "cmp_set_ae_mi_8", load: "mov_rm_8", swapped: None },
673 Fold { from: "cmp_set_ae_ri_16", into: "cmp_set_ae_mi_16", load: "mov_rm_16", swapped: None },
674 Fold { from: "cmp_set_ae_ri_32", into: "cmp_set_ae_mi_32", load: "mov_rm_32", swapped: None },
675 Fold { from: "cmp_set_ae_ri_64", into: "cmp_set_ae_mi_64", load: "mov_rm_64", swapped: None },
676];
677
678pub static WIDENINGS: &[Fold] = &[
685 Fold { from: "movzx_8_16", into: "movzx_rm_8_16", load: "mov_rm_8", swapped: None },
686 Fold { from: "movzx_8_32", into: "movzx_rm_8_32", load: "mov_rm_8", swapped: None },
687 Fold { from: "movzx_8_64", into: "movzx_rm_8_64", load: "mov_rm_8", swapped: None },
688 Fold { from: "movzx_16_32", into: "movzx_rm_16_32", load: "mov_rm_16", swapped: None },
689 Fold { from: "movzx_16_64", into: "movzx_rm_16_64", load: "mov_rm_16", swapped: None },
690 Fold { from: "movsx_8_16", into: "movsx_rm_8_16", load: "mov_rm_8", swapped: None },
691 Fold { from: "movsx_8_32", into: "movsx_rm_8_32", load: "mov_rm_8", swapped: None },
692 Fold { from: "movsx_8_64", into: "movsx_rm_8_64", load: "mov_rm_8", swapped: None },
693 Fold { from: "movsx_16_32", into: "movsx_rm_16_32", load: "mov_rm_16", swapped: None },
694 Fold { from: "movsx_16_64", into: "movsx_rm_16_64", load: "mov_rm_16", swapped: None },
695 Fold { from: "movsxd_32_64", into: "movsxd_rm_32_64", load: "mov_rm_32", swapped: None },
696 Fold { from: "mov_32_to_64", into: "mov_rm_32", load: "mov_rm_32", swapped: None },
697];
698
699#[derive(Debug, Clone, Copy, PartialEq, Eq)]
706pub struct Update {
707 pub from: &'static str,
709 pub into: &'static str,
711 pub load: &'static str,
713 pub store: &'static str,
715 pub commutes: bool,
717}
718
719pub static UPDATES: &[Update] = &[
730 Update {
731 from: "add_rr_8",
732 into: "add_mr_8",
733 load: "mov_rm_8",
734 store: "mov_mr_8",
735 commutes: true,
736 },
737 Update {
738 from: "add_rr_16",
739 into: "add_mr_16",
740 load: "mov_rm_16",
741 store: "mov_mr_16",
742 commutes: true,
743 },
744 Update {
745 from: "add_rr_32",
746 into: "add_mr_32",
747 load: "mov_rm_32",
748 store: "mov_mr_32",
749 commutes: true,
750 },
751 Update {
752 from: "add_rr_64",
753 into: "add_mr_64",
754 load: "mov_rm_64",
755 store: "mov_mr_64",
756 commutes: true,
757 },
758 Update {
759 from: "sub_rr_8",
760 into: "sub_mr_8",
761 load: "mov_rm_8",
762 store: "mov_mr_8",
763 commutes: false,
764 },
765 Update {
766 from: "sub_rr_16",
767 into: "sub_mr_16",
768 load: "mov_rm_16",
769 store: "mov_mr_16",
770 commutes: false,
771 },
772 Update {
773 from: "sub_rr_32",
774 into: "sub_mr_32",
775 load: "mov_rm_32",
776 store: "mov_mr_32",
777 commutes: false,
778 },
779 Update {
780 from: "sub_rr_64",
781 into: "sub_mr_64",
782 load: "mov_rm_64",
783 store: "mov_mr_64",
784 commutes: false,
785 },
786 Update {
787 from: "and_rr_8",
788 into: "and_mr_8",
789 load: "mov_rm_8",
790 store: "mov_mr_8",
791 commutes: true,
792 },
793 Update {
794 from: "and_rr_16",
795 into: "and_mr_16",
796 load: "mov_rm_16",
797 store: "mov_mr_16",
798 commutes: true,
799 },
800 Update {
801 from: "and_rr_32",
802 into: "and_mr_32",
803 load: "mov_rm_32",
804 store: "mov_mr_32",
805 commutes: true,
806 },
807 Update {
808 from: "and_rr_64",
809 into: "and_mr_64",
810 load: "mov_rm_64",
811 store: "mov_mr_64",
812 commutes: true,
813 },
814 Update {
815 from: "or_rr_8",
816 into: "or_mr_8",
817 load: "mov_rm_8",
818 store: "mov_mr_8",
819 commutes: true,
820 },
821 Update {
822 from: "or_rr_16",
823 into: "or_mr_16",
824 load: "mov_rm_16",
825 store: "mov_mr_16",
826 commutes: true,
827 },
828 Update {
829 from: "or_rr_32",
830 into: "or_mr_32",
831 load: "mov_rm_32",
832 store: "mov_mr_32",
833 commutes: true,
834 },
835 Update {
836 from: "or_rr_64",
837 into: "or_mr_64",
838 load: "mov_rm_64",
839 store: "mov_mr_64",
840 commutes: true,
841 },
842 Update {
843 from: "xor_rr_8",
844 into: "xor_mr_8",
845 load: "mov_rm_8",
846 store: "mov_mr_8",
847 commutes: true,
848 },
849 Update {
850 from: "xor_rr_16",
851 into: "xor_mr_16",
852 load: "mov_rm_16",
853 store: "mov_mr_16",
854 commutes: true,
855 },
856 Update {
857 from: "xor_rr_32",
858 into: "xor_mr_32",
859 load: "mov_rm_32",
860 store: "mov_mr_32",
861 commutes: true,
862 },
863 Update {
864 from: "xor_rr_64",
865 into: "xor_mr_64",
866 load: "mov_rm_64",
867 store: "mov_mr_64",
868 commutes: true,
869 },
870];
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq)]
881pub struct Bump {
882 pub from: &'static str,
884 pub into: &'static str,
886 pub load: &'static str,
888 pub store: &'static str,
890}
891
892pub static BUMPS: &[Bump] = &[
907 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
908 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
909 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
910 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
911 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
912 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
913 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
914 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
915 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
916 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
917 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
918 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
919 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
920 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
921 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
922 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
923 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
924 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
925 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
926 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
927];
928
929#[derive(Debug, Clone, Copy)]
934struct Waiting {
935 inst: Inst,
937 reg: Reg,
939 load: &'static str,
941 at: usize,
943}
944
945pub fn loads(
956 func: &mut Func,
957 machine: &MachineInsts,
958 names: &mut Interner,
959 pending: &mut Pending<'_>,
960) -> usize {
961 let mut reads = Reads::of(func);
962 let mut done = 0;
963 for block in func.blocks().collect::<Vec<_>>() {
964 let mut waiting: Option<Waiting> = None;
965 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
966 let name = names.resolve(func[inst].opcode.name()).to_owned();
967 let bare = machine.bare(&name).to_owned();
968 let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
974 if let Some(carried) = waiting {
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 let mut rows = FOLDS.iter().chain(WIDENINGS);
1003 if let Some(load) = rows.find(|fold| fold.load == bare).map(|fold| fold.load) {
1004 let operands = &func[func[inst].operands];
1005 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
1006 waiting = Some(Waiting { inst, reg: first.reg, load, at });
1007 }
1008 }
1009 }
1010 }
1011 done
1012}
1013
1014#[derive(Debug, Clone, Copy)]
1016struct Run {
1017 load: Inst,
1019 alu: Inst,
1021 store: Inst,
1023 update: &'static Update,
1025 kept: Operand,
1027}
1028
1029#[derive(Debug, Clone, Copy)]
1035struct Bumped {
1036 load: Inst,
1038 alu: Inst,
1040 store: Inst,
1042 bump: &'static Bump,
1044 imm: i64,
1046}
1047
1048pub fn stores(
1065 func: &mut Func,
1066 machine: &MachineInsts,
1067 flags: &FlagInsts,
1068 names: &mut Interner,
1069 pending: &mut Pending<'_>,
1070) -> usize {
1071 let mut reads = Reads::of(func);
1072 let mut done = 0;
1073 for block in func.blocks().collect::<Vec<_>>() {
1074 let insts: Vec<Inst> = func.insts(block).collect();
1075 for at in 0..insts.len() {
1076 let found = match run(func, &reads, machine, flags, names, &insts, at) {
1077 Some(found) => Some((
1078 found.load,
1079 found.alu,
1080 found.store,
1081 updated(func, machine, names, &found),
1082 )),
1083 None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1084 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1085 }),
1086 };
1087 let Some((load, alu, store, plan)) = found else { continue };
1088 if !pending.alike(load, store) {
1089 continue;
1090 }
1091 let mut set = Changes::new();
1092 set.rewrite(store, plan);
1093 set.remove(alu);
1094 set.remove(load);
1095 if set.commit(func, &mut reads, names, machine).is_ok() {
1096 pending.moved(load, &[]);
1097 done += 1;
1098 }
1099 }
1100 }
1101 done
1102}
1103
1104fn run(
1116 func: &Func,
1117 reads: &Reads,
1118 machine: &MachineInsts,
1119 flags: &FlagInsts,
1120 names: &Interner,
1121 insts: &[Inst],
1122 at: usize,
1123) -> Option<Run> {
1124 let store = insts[at];
1125 if insisted(func, store) {
1126 return None;
1127 }
1128 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1129 let value = *func[func[store].operands].first()?;
1130 if value.role.is_def() || reads.count(value.reg) != 1 {
1131 return None;
1132 }
1133 let earliest = at.saturating_sub(WINDOW);
1136 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1137 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1138 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1139 if !quiet(func, flags, names, insts, (alu, at)) {
1140 return None;
1141 }
1142 let operands = func[func[insts[alu]].operands].to_vec();
1143 let [_, first, second] = operands[..] else { return None };
1144 let both = [(first, second), (second, first)];
1149 let tried = if update.commutes { &both[..] } else { &both[..1] };
1150 for &(source, kept) in tried {
1151 if reads.count(source.reg) != 1 {
1152 continue;
1153 }
1154 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1155 continue;
1156 };
1157 let load = insts[from];
1158 if insisted(func, load) {
1159 continue;
1160 }
1161 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1162 continue;
1163 }
1164 if !same_place(func, load, store) {
1165 continue;
1166 }
1167 let mut wanted: Vec<Reg> =
1172 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1173 wanted.push(kept.reg);
1174 if !clear(func, machine, names, insts, (from, at), &wanted) {
1175 continue;
1176 }
1177 return Some(Run { load, alu: insts[alu], store, update, kept });
1178 }
1179 None
1180}
1181
1182fn constant(
1196 func: &Func,
1197 reads: &Reads,
1198 machine: &MachineInsts,
1199 flags: &FlagInsts,
1200 names: &Interner,
1201 insts: &[Inst],
1202 at: usize,
1203) -> Option<Bumped> {
1204 let store = insts[at];
1205 if insisted(func, store) {
1206 return None;
1207 }
1208 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1209 let value = *func[func[store].operands].first()?;
1210 if value.role.is_def() || reads.count(value.reg) != 1 {
1211 return None;
1212 }
1213 let mem = func[func[store].mem?];
1214 if mem.base == Some(0) || mem.index == Some(0) {
1215 return None;
1216 }
1217 let earliest = at.saturating_sub(WINDOW);
1218 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1219 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1220 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1221 if !quiet(func, flags, names, insts, (alu, at)) {
1222 return None;
1223 }
1224 let operands = func[func[insts[alu]].operands].to_vec();
1225 let [_, source] = operands[..] else { return None };
1226 let imm = func[func[insts[alu]].imm?].0;
1227 if reads.count(source.reg) != 1 {
1228 return None;
1229 }
1230 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1231 let load = insts[from];
1232 if insisted(func, load) {
1233 return None;
1234 }
1235 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1236 return None;
1237 }
1238 if !same_place(func, load, store) {
1239 return None;
1240 }
1241 let wanted: Vec<Reg> =
1244 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1245 if !clear(func, machine, names, insts, (from, at), &wanted) {
1246 return None;
1247 }
1248 Some(Bumped { load, alu: insts[alu], store, bump, imm })
1249}
1250
1251fn insisted(func: &Func, inst: Inst) -> bool {
1256 func[inst].flags.contains(Flags::VOLATILE)
1257}
1258
1259fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1261 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1262}
1263
1264fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1271 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1272 let (here, there) = (func[here], func[there]);
1273 if func[one].symbol != func[other].symbol {
1274 return false;
1275 }
1276 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1277 if bare(here) != bare(there) {
1278 return false;
1279 }
1280 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1281 (None, None) => true,
1282 (Some(left), Some(right)) => {
1283 func[func[one].operands][usize::from(left)].reg
1284 == func[func[other].operands][usize::from(right)].reg
1285 }
1286 _ => false,
1287 };
1288 same(here.base, there.base) && same(here.index, there.index)
1289}
1290
1291fn clear(
1298 func: &Func,
1299 machine: &MachineInsts,
1300 names: &Interner,
1301 insts: &[Inst],
1302 span: (usize, usize),
1303 wanted: &[Reg],
1304) -> bool {
1305 let (from, to) = span;
1306 insts[from + 1..to].iter().all(|&inst| {
1307 let name = names.resolve(func[inst].opcode.name());
1308 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1309 return false;
1310 }
1311 !func[func[inst].operands]
1312 .iter()
1313 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1314 })
1315}
1316
1317fn quiet(
1334 func: &Func,
1335 flags: &FlagInsts,
1336 names: &Interner,
1337 insts: &[Inst],
1338 span: (usize, usize),
1339) -> bool {
1340 let (alu, to) = span;
1341 insts[alu + 1..to].iter().all(|&inst| {
1342 let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1343 return false;
1344 };
1345 flags.reads(name).is_none() && !(flags.writes)(name)
1346 })
1347}
1348
1349fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1355 let operands = func[func[run.store].operands].to_vec();
1356 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1357 Plan {
1358 opcode: Opcode::new(into),
1359 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1360 imm: None,
1361 amode: func[run.store].mem.map(|mem| func[mem]),
1362 symbol: func[run.store].symbol,
1363 }
1364}
1365
1366fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1374 let operands = func[func[run.store].operands][1..].to_vec();
1375 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1376 let back = |at: Option<u8>| at.map(|at| at - 1);
1377 Plan {
1378 opcode: Opcode::new(into),
1379 operands,
1380 imm: Some(run.imm),
1381 amode: func[run.store].mem.map(|mem| {
1382 let mem = func[mem];
1383 Amode { base: back(mem.base), index: back(mem.index), ..mem }
1384 }),
1385 symbol: func[run.store].symbol,
1386 }
1387}
1388
1389fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1395 let written: Vec<Reg> = func[func[inst].operands]
1396 .iter()
1397 .filter(|operand| operand.role.is_def())
1398 .map(|operand| operand.reg)
1399 .collect();
1400 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1401}
1402
1403fn joined(
1408 func: &Func,
1409 reads: &Reads,
1410 carried: Waiting,
1411 machine: &MachineInsts,
1412 names: &mut Interner,
1413 inst: Inst,
1414 bare: &str,
1415) -> Option<Plan> {
1416 let fold = FOLDS.iter().chain(WIDENINGS).find(|fold| fold.from == bare)?;
1417 if carried.load != fold.load || reads.count(carried.reg) != 1 {
1418 return None;
1419 }
1420 let operands = func[func[inst].operands].to_vec();
1421 let (front, into) = match operands[..] {
1432 [answer, first, second] => {
1433 let (kept, into) = if second.reg == carried.reg {
1434 (first, fold.into)
1435 } else if first.reg == carried.reg {
1436 (second, fold.swapped?)
1437 } else {
1438 return None;
1439 };
1440 (vec![answer, kept], into)
1441 }
1442 [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1443 _ => return None,
1444 };
1445 let load = carried.inst;
1446 let address = func[func[load].operands][1..].to_vec();
1447 let mut amode = func[func[load].mem?];
1448 let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1452 amode.base = amode.base.map(|at| at + along);
1453 amode.index = amode.index.map(|at| at + along);
1454 let into = names.intern(&format!("{}{}", machine.prefix, into));
1455 Some(Plan {
1456 opcode: Opcode::new(into),
1457 operands: front.into_iter().chain(address).collect(),
1458 imm: func[inst].imm.map(|at| func[at].0),
1459 amode: Some(amode),
1460 symbol: func[load].symbol,
1461 })
1462}
1463
1464#[cfg(test)]
1465mod tests {
1466 use rucc_mir::{self as mir, Constraint, Mem, Operand};
1467 use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1468
1469 use super::*;
1470
1471 fn empty() -> (Interner, Func, mir::Block) {
1473 let mut names = Interner::new();
1474 let mut func = Func::new(names.intern("f"));
1475 let block = func.create_block();
1476 (names, func, block)
1477 }
1478
1479 fn op(names: &mut Interner, name: &str) -> Opcode {
1481 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1482 }
1483
1484 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1486 let into = func.new_vreg(GPR);
1487 let mov = op(names, "mov_rm_64");
1488 func.build(block, mov)
1489 .def(into, GPR)
1490 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1491 .finish();
1492 into
1493 }
1494
1495 fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1497 let into = func.new_vreg(GPR);
1498 let mov = op(names, "mov_rm_64");
1499 func.build(block, mov)
1500 .def(into, GPR)
1501 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1502 .flags(Flags::VOLATILE)
1503 .finish();
1504 into
1505 }
1506
1507 fn alu(
1509 func: &mut Func,
1510 names: &mut Interner,
1511 block: mir::Block,
1512 name: &str,
1513 first: Reg,
1514 second: Reg,
1515 ) -> Reg {
1516 let answer = func.new_vreg(GPR);
1517 let opcode = op(names, name);
1518 func.build(block, opcode)
1519 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1520 .uses(first, GPR)
1521 .uses(second, GPR)
1522 .finish();
1523 answer
1524 }
1525
1526 fn compare(
1529 func: &mut Func,
1530 names: &mut Interner,
1531 block: mir::Block,
1532 name: &str,
1533 first: Reg,
1534 second: Reg,
1535 ) -> Reg {
1536 let byte = func.new_vreg(GPR);
1537 let opcode = op(names, name);
1538 func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1539 byte
1540 }
1541
1542 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1544 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1545 }
1546
1547 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1549 let mut addresses = Vec::new();
1550 let mut arguments = Vec::new();
1551 let mut dynamic = Vec::new();
1552 let mut pending =
1553 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1554 loads(func, &MACHINE, names, &mut pending)
1555 }
1556
1557 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1559 let mov = op(names, "mov_mr_64");
1560 func.build(block, mov)
1561 .uses(value, GPR)
1562 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1563 .finish();
1564 }
1565
1566 fn insisted_store(
1568 func: &mut Func,
1569 names: &mut Interner,
1570 block: mir::Block,
1571 base: Reg,
1572 value: Reg,
1573 ) {
1574 let mov = op(names, "mov_mr_64");
1575 func.build(block, mov)
1576 .uses(value, GPR)
1577 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1578 .flags(Flags::VOLATILE)
1579 .finish();
1580 }
1581
1582 fn update(func: &mut Func, names: &mut Interner) -> usize {
1584 let mut addresses = Vec::new();
1585 let mut arguments = Vec::new();
1586 let mut dynamic = Vec::new();
1587 let mut pending =
1588 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1589 stores(func, &MACHINE, &FLAGS, names, &mut pending)
1590 }
1591
1592 #[test]
1594 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1595 let (mut names, mut func, block) = empty();
1596 let base = func.new_vreg(GPR);
1597 let other = func.new_vreg(GPR);
1598 let word = load(&mut func, &mut names, block, base);
1599 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1600 store(&mut func, &mut names, block, base, sum);
1601
1602 assert_eq!(update(&mut func, &mut names), 1);
1603 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1604 let inst = func.insts(block).next().expect("the addition");
1605 let mem = func[inst].mem.expect("it writes memory");
1606 assert_eq!(func[mem].disp, 16, "the address came from the store");
1607 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1608 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1609 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1610 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1611 }
1612
1613 #[test]
1617 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1618 let (mut names, mut func, block) = empty();
1619 let base = func.new_vreg(GPR);
1620 let other = func.new_vreg(GPR);
1621 let word = load(&mut func, &mut names, block, base);
1622 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1623 store(&mut func, &mut names, block, base, sum);
1624
1625 assert_eq!(update(&mut func, &mut names), 1);
1626 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1627 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1628 }
1629
1630 #[test]
1633 fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1634 let (mut names, mut func, block) = empty();
1635 let base = func.new_vreg(GPR);
1636 let other = func.new_vreg(GPR);
1637 let word = load(&mut func, &mut names, block, base);
1638 let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1639 store(&mut func, &mut names, block, base, left);
1640
1641 assert_eq!(update(&mut func, &mut names), 1);
1642 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1643 }
1644
1645 #[test]
1648 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1649 let (mut names, mut func, block) = empty();
1650 let base = func.new_vreg(GPR);
1651 let other = func.new_vreg(GPR);
1652 let word = load(&mut func, &mut names, block, base);
1653 let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1654 store(&mut func, &mut names, block, base, left);
1655
1656 assert_eq!(update(&mut func, &mut names), 0);
1657 assert_eq!(
1658 shape(&func, &names, block),
1659 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1660 );
1661 }
1662
1663 #[test]
1666 fn a_store_to_another_address_stays_three_instructions() {
1667 let (mut names, mut func, block) = empty();
1668 let base = func.new_vreg(GPR);
1669 let elsewhere = func.new_vreg(GPR);
1670 let other = func.new_vreg(GPR);
1671 let word = load(&mut func, &mut names, block, base);
1672 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1673 store(&mut func, &mut names, block, elsewhere, sum);
1674
1675 assert_eq!(update(&mut func, &mut names), 0);
1676 }
1677
1678 #[test]
1681 fn a_store_at_another_displacement_stays_three_instructions() {
1682 let (mut names, mut func, block) = empty();
1683 let base = func.new_vreg(GPR);
1684 let other = func.new_vreg(GPR);
1685 let word = load(&mut func, &mut names, block, base);
1686 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1687 let mov = op(&mut names, "mov_mr_64");
1688 func.build(block, mov)
1689 .uses(sum, GPR)
1690 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1691 .finish();
1692
1693 assert_eq!(update(&mut func, &mut names), 0);
1694 }
1695
1696 #[test]
1699 fn a_word_two_instructions_read_stays_three_instructions() {
1700 let (mut names, mut func, block) = empty();
1701 let base = func.new_vreg(GPR);
1702 let other = func.new_vreg(GPR);
1703 let word = load(&mut func, &mut names, block, base);
1704 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1705 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1706 store(&mut func, &mut names, block, base, sum);
1707
1708 assert_eq!(update(&mut func, &mut names), 0);
1709 }
1710
1711 #[test]
1714 fn an_answer_something_else_reads_stays_three_instructions() {
1715 let (mut names, mut func, block) = empty();
1716 let base = func.new_vreg(GPR);
1717 let other = func.new_vreg(GPR);
1718 let word = load(&mut func, &mut names, block, base);
1719 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1720 store(&mut func, &mut names, block, base, sum);
1721 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1722
1723 assert_eq!(update(&mut func, &mut names), 0);
1724 }
1725
1726 #[test]
1729 fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1730 let (mut names, mut func, block) = empty();
1731 let base = func.new_vreg(GPR);
1732 let other = func.new_vreg(GPR);
1733 let word = load(&mut func, &mut names, block, base);
1734 load(&mut func, &mut names, block, other);
1735 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1736 store(&mut func, &mut names, block, base, sum);
1737
1738 assert_eq!(update(&mut func, &mut names), 0);
1739 }
1740
1741 #[test]
1744 fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1745 let (mut names, mut func, block) = empty();
1746 let base = Reg::physical(rucc_target::x86_64::RSP);
1747 let other = func.new_vreg(GPR);
1748 let word = load(&mut func, &mut names, block, base);
1749 let sub = op(&mut names, "sub_ri_64");
1750 func.build(block, sub)
1751 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1752 .uses(base, GPR)
1753 .imm(32)
1754 .finish();
1755 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1756 store(&mut func, &mut names, block, base, sum);
1757
1758 assert_eq!(update(&mut func, &mut names), 0);
1759 }
1760
1761 #[test]
1765 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1766 let (mut names, mut func, block) = empty();
1767 let base = Reg::physical(rucc_target::x86_64::RSP);
1768 let other = func.new_vreg(GPR);
1769 let mov = op(&mut names, "mov_rm_64");
1770 let word = func.new_vreg(GPR);
1771 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1772 let read = func.insts(block).next().expect("the load");
1773 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1774 let put = op(&mut names, "mov_mr_64");
1775 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1776 let written = func.insts(block).nth(2).expect("the store");
1777
1778 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1779 let mut arguments = Vec::new();
1780 let mut dynamic = Vec::new();
1781 let mut pending =
1782 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1783 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1784 }
1785
1786 #[test]
1790 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
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, 3usize)];
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), 1);
1809
1810 let inst = func.insts(block).next().expect("the addition");
1811 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1812 }
1813
1814 #[test]
1816 fn a_run_whose_widths_disagree_stays_three_instructions() {
1817 let (mut names, mut func, block) = empty();
1818 let base = func.new_vreg(GPR);
1819 let other = func.new_vreg(GPR);
1820 let into = func.new_vreg(GPR);
1821 let narrow = op(&mut names, "mov_rm_32");
1822 func.build(block, narrow)
1823 .def(into, GPR)
1824 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1825 .finish();
1826 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1827 store(&mut func, &mut names, block, base, sum);
1828
1829 assert_eq!(update(&mut func, &mut names), 0);
1830 }
1831
1832 #[test]
1836 fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1837 let (mut names, mut func, block) = empty();
1838 let base = func.new_vreg(GPR);
1839 let other = func.new_vreg(GPR);
1840 let carry = func.new_vreg(GPR);
1841 let word = load(&mut func, &mut names, block, base);
1842 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1843 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1844 store(&mut func, &mut names, block, base, sum);
1845
1846 assert_eq!(update(&mut func, &mut names), 0);
1847 }
1848
1849 #[test]
1854 fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1855 let (mut names, mut func, block) = empty();
1856 let base = func.new_vreg(GPR);
1857 let other = func.new_vreg(GPR);
1858 let left = func.new_vreg(GPR);
1859 let right = func.new_vreg(GPR);
1860 let word = load(&mut func, &mut names, block, base);
1861 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1862 alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1863 store(&mut func, &mut names, block, base, sum);
1864
1865 assert_eq!(update(&mut func, &mut names), 0);
1866 }
1867
1868 #[test]
1871 fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1872 let (mut names, mut func, block) = empty();
1873 let base = func.new_vreg(GPR);
1874 let other = func.new_vreg(GPR);
1875 let from = func.new_vreg(GPR);
1876 let into = func.new_vreg(GPR);
1877 let word = load(&mut func, &mut names, block, base);
1878 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1879 let copy = op(&mut names, "mov_rr_64");
1880 func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1881 store(&mut func, &mut names, block, base, sum);
1882
1883 assert_eq!(update(&mut func, &mut names), 1);
1884 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1885 }
1886
1887 #[test]
1889 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1890 for update in UPDATES {
1891 for name in [update.from, update.into, update.load, update.store] {
1892 assert!(MACHINE.has(name), "{name} is not an instruction");
1893 }
1894 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1895 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1896 assert_eq!(
1897 width(update.from),
1898 width(update.load),
1899 "{} loads another width",
1900 update.from
1901 );
1902 assert_eq!(
1903 width(update.from),
1904 width(update.store),
1905 "{} stores another width",
1906 update.from
1907 );
1908 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1909 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1910 }
1911 }
1912
1913 #[test]
1916 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1917 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1918 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1919 assert_eq!(commuting, 16, "everything but the four subtractions");
1920 }
1921
1922 fn alu_imm(
1924 func: &mut Func,
1925 names: &mut Interner,
1926 block: mir::Block,
1927 name: &str,
1928 source: Reg,
1929 value: i64,
1930 ) -> Reg {
1931 let answer = func.new_vreg(GPR);
1932 let opcode = op(names, name);
1933 func.build(block, opcode)
1934 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1935 .uses(source, GPR)
1936 .imm(value)
1937 .finish();
1938 answer
1939 }
1940
1941 #[test]
1943 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1944 let (mut names, mut func, block) = empty();
1945 let base = func.new_vreg(GPR);
1946 let word = load(&mut func, &mut names, block, base);
1947 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1948 store(&mut func, &mut names, block, base, sum);
1949
1950 assert_eq!(update(&mut func, &mut names), 1);
1951 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1952 let inst = func.insts(block).next().expect("the addition");
1953 let mem = func[inst].mem.expect("it writes memory");
1954 assert_eq!(func[mem].disp, 16, "the address came from the store");
1955 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1956 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1957 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1958 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1959 }
1960
1961 #[test]
1964 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1965 let (mut names, mut func, block) = empty();
1966 let base = func.new_vreg(GPR);
1967 let word = load(&mut func, &mut names, block, base);
1968 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1969 store(&mut func, &mut names, block, base, left);
1970
1971 assert_eq!(update(&mut func, &mut names), 1);
1972 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1973 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1974 }
1975
1976 #[test]
1979 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1980 let (mut names, mut func, block) = empty();
1981 let base = func.new_vreg(GPR);
1982 let word = func.new_vreg(GPR);
1983 let mov = op(&mut names, "mov_rm_8");
1984 func.build(block, mov)
1985 .def(word, GPR)
1986 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1987 .finish();
1988 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1989 let put = op(&mut names, "mov_mr_8");
1990 func.build(block, put)
1991 .uses(sum, GPR)
1992 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1993 .finish();
1994
1995 assert_eq!(update(&mut func, &mut names), 1);
1996 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1997 }
1998
1999 #[test]
2002 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
2003 let (mut names, mut func, block) = empty();
2004 let base = func.new_vreg(GPR);
2005 let other = func.new_vreg(GPR);
2006 let word = load(&mut func, &mut names, block, base);
2007 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2008 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
2009 store(&mut func, &mut names, block, base, sum);
2010
2011 assert_eq!(update(&mut func, &mut names), 0);
2012 }
2013
2014 #[test]
2017 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
2018 let (mut names, mut func, block) = empty();
2019 let base = func.new_vreg(GPR);
2020 let elsewhere = func.new_vreg(GPR);
2021 let word = load(&mut func, &mut names, block, base);
2022 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2023 load(&mut func, &mut names, block, elsewhere);
2024 store(&mut func, &mut names, block, base, sum);
2025
2026 assert_eq!(update(&mut func, &mut names), 0);
2027 }
2028
2029 #[test]
2032 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
2033 let (mut names, mut func, block) = empty();
2034 let base = Reg::physical(rucc_target::x86_64::RAX);
2035 let word = load(&mut func, &mut names, block, base);
2036 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2037 let mov = op(&mut names, "mov_ri_64");
2038 func.build(block, mov).def(base, GPR).imm(0).finish();
2039 store(&mut func, &mut names, block, base, sum);
2040
2041 assert_eq!(update(&mut func, &mut names), 0);
2042 }
2043
2044 #[test]
2046 fn a_constant_written_to_another_address_stays_three_instructions() {
2047 let (mut names, mut func, block) = empty();
2048 let base = func.new_vreg(GPR);
2049 let elsewhere = func.new_vreg(GPR);
2050 let word = load(&mut func, &mut names, block, base);
2051 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2052 store(&mut func, &mut names, block, elsewhere, sum);
2053
2054 assert_eq!(update(&mut func, &mut names), 0);
2055 }
2056
2057 #[test]
2059 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2060 let (mut names, mut func, block) = empty();
2061 let base = func.new_vreg(GPR);
2062 let into = func.new_vreg(GPR);
2063 let narrow = op(&mut names, "mov_rm_32");
2064 func.build(block, narrow)
2065 .def(into, GPR)
2066 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2067 .finish();
2068 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2069 store(&mut func, &mut names, block, base, sum);
2070
2071 assert_eq!(update(&mut func, &mut names), 0);
2072 }
2073
2074 #[test]
2077 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2078 let (mut names, mut func, block) = empty();
2079 let base = func.new_vreg(GPR);
2080 let word = load(&mut func, &mut names, block, base);
2081 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2082 store(&mut func, &mut names, block, base, product);
2083
2084 assert_eq!(update(&mut func, &mut names), 0);
2085 }
2086
2087 #[test]
2090 fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2091 let (mut names, mut func, block) = empty();
2092 let base = func.new_vreg(GPR);
2093 let carry = func.new_vreg(GPR);
2094 let word = load(&mut func, &mut names, block, base);
2095 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2096 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2097 store(&mut func, &mut names, block, base, sum);
2098
2099 assert_eq!(update(&mut func, &mut names), 0);
2100 }
2101
2102 #[test]
2105 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2106 let (mut names, mut func, block) = empty();
2107 let base = Reg::physical(rucc_target::x86_64::RSP);
2108 let mov = op(&mut names, "mov_rm_64");
2109 let word = func.new_vreg(GPR);
2110 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2111 let read = func.insts(block).next().expect("the load");
2112 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2113 let put = op(&mut names, "mov_mr_64");
2114 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2115 let written = func.insts(block).nth(2).expect("the store");
2116
2117 let mut addresses = vec![(read, 3usize), (written, 3usize)];
2118 let mut arguments = Vec::new();
2119 let mut dynamic = Vec::new();
2120 let mut pending =
2121 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2122 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2123
2124 let inst = func.insts(block).next().expect("the addition");
2125 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2126 }
2127
2128 #[test]
2131 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
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, 4usize)];
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), 0);
2149 }
2150
2151 #[test]
2153 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2154 for bump in BUMPS {
2155 for name in [bump.from, bump.into, bump.load, bump.store] {
2156 assert!(MACHINE.has(name), "{name} is not an instruction");
2157 }
2158 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2159 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2160 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2161 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2162 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2163 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2164 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2165 }
2166 }
2167
2168 #[test]
2171 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2172 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2173 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2174 for bump in BUMPS {
2175 let same = bump.from.replace("_ri_", "_rr_");
2176 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2177 }
2178 }
2179
2180 #[test]
2183 fn nothing_is_both_a_register_run_and_a_constant_run() {
2184 for bump in BUMPS {
2185 assert!(
2186 !UPDATES.iter().any(|update| update.from == bump.from),
2187 "{} starts both kinds of run",
2188 bump.from
2189 );
2190 }
2191 }
2192
2193 #[test]
2195 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2196 let (mut names, mut func, block) = empty();
2197 let base = func.new_vreg(GPR);
2198 let other = func.new_vreg(GPR);
2199 let word = load(&mut func, &mut names, block, base);
2200 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2201
2202 assert_eq!(combine(&mut func, &mut names), 1);
2203 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2204 let inst = func.insts(block).next().expect("the addition");
2205 let mem = func[inst].mem.expect("the addition reads memory now");
2206 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2207 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2208 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2209 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2210 }
2211
2212 #[test]
2215 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2216 let (mut names, mut func, block) = empty();
2217 let base = func.new_vreg(GPR);
2218 let other = func.new_vreg(GPR);
2219 let word = load(&mut func, &mut names, block, base);
2220 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2221
2222 assert_eq!(combine(&mut func, &mut names), 1);
2223 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2224 let inst = func.insts(block).next().expect("the addition");
2225 assert_eq!(func[func[inst].operands][1].reg, other);
2226 }
2227
2228 #[test]
2231 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2232 let (mut names, mut func, block) = empty();
2233 let base = func.new_vreg(GPR);
2234 let other = func.new_vreg(GPR);
2235 let word = load(&mut func, &mut names, block, base);
2236 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2237
2238 assert_eq!(combine(&mut func, &mut names), 0);
2239 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2240 }
2241
2242 #[test]
2244 fn a_load_feeding_the_right_of_a_subtraction_folds() {
2245 let (mut names, mut func, block) = empty();
2246 let base = func.new_vreg(GPR);
2247 let other = func.new_vreg(GPR);
2248 let word = load(&mut func, &mut names, block, base);
2249 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2250
2251 assert_eq!(combine(&mut func, &mut names), 1);
2252 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2253 }
2254
2255 #[test]
2258 fn a_load_two_instructions_read_stays_a_load() {
2259 let (mut names, mut func, block) = empty();
2260 let base = func.new_vreg(GPR);
2261 let other = func.new_vreg(GPR);
2262 let word = load(&mut func, &mut names, block, base);
2263 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2264 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2265
2266 assert_eq!(combine(&mut func, &mut names), 0);
2267 assert_eq!(
2268 shape(&func, &names, block),
2269 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2270 );
2271 }
2272
2273 #[test]
2276 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2277 let (mut names, mut func, block) = empty();
2278 let base = func.new_vreg(GPR);
2279 let other = func.new_vreg(GPR);
2280 let word = load(&mut func, &mut names, block, base);
2281 let store = op(&mut names, "mov_mr_64");
2282 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2283 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2284
2285 assert_eq!(combine(&mut func, &mut names), 0);
2286 assert_eq!(
2287 shape(&func, &names, block),
2288 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2289 );
2290 }
2291
2292 #[test]
2298 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2299 let (mut names, mut func, block) = empty();
2300 let base = func.new_vreg(GPR);
2301 let other = func.new_vreg(GPR);
2302 let word = load(&mut func, &mut names, block, base);
2303 load(&mut func, &mut names, block, other);
2304 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2305
2306 assert_eq!(combine(&mut func, &mut names), 0);
2307 assert_eq!(
2308 shape(&func, &names, block),
2309 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2310 );
2311 }
2312
2313 #[test]
2316 fn the_later_of_two_loads_is_the_one_that_folds() {
2317 let (mut names, mut func, block) = empty();
2318 let base = func.new_vreg(GPR);
2319 let other = func.new_vreg(GPR);
2320 let first = load(&mut func, &mut names, block, base);
2321 let second = load(&mut func, &mut names, block, other);
2322 alu(&mut func, &mut names, block, "add_rr_64", first, second);
2323
2324 assert_eq!(combine(&mut func, &mut names), 1);
2325 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2326 let addition = func.insts(block).nth(1).expect("the addition");
2327 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2328 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2329 }
2330
2331 #[test]
2334 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2335 let (mut names, mut func, block) = empty();
2336 let base = func.new_vreg(GPR);
2337 let other = func.new_vreg(GPR);
2338 let word = load(&mut func, &mut names, block, base);
2339 let call = op(&mut names, "call");
2340 func.build(block, call).finish();
2341 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2342
2343 assert_eq!(combine(&mut func, &mut names), 0);
2344 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2345 }
2346
2347 #[test]
2350 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2351 let (mut names, mut func, block) = empty();
2352 let base = Reg::physical(rucc_target::x86_64::RSP);
2353 let other = func.new_vreg(GPR);
2354 let word = load(&mut func, &mut names, block, base);
2355 let sub = op(&mut names, "sub_ri_64");
2356 func.build(block, sub)
2357 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2358 .uses(base, GPR)
2359 .imm(32)
2360 .finish();
2361 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2362
2363 assert_eq!(combine(&mut func, &mut names), 0);
2364 }
2365
2366 #[test]
2369 fn a_load_of_the_wrong_width_stays_a_load() {
2370 let (mut names, mut func, block) = empty();
2371 let base = func.new_vreg(GPR);
2372 let other = func.new_vreg(GPR);
2373 let into = func.new_vreg(GPR);
2374 let narrow = op(&mut names, "mov_rm_32");
2375 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2376 alu(&mut func, &mut names, block, "add_rr_64", other, into);
2377
2378 assert_eq!(combine(&mut func, &mut names), 0);
2379 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2380 }
2381
2382 #[test]
2385 fn a_load_whose_value_an_edge_carries_stays_a_load() {
2386 let (mut names, mut func, block) = empty();
2387 let next = func.create_block();
2388 let base = func.new_vreg(GPR);
2389 let other = func.new_vreg(GPR);
2390 let word = load(&mut func, &mut names, block, base);
2391 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2392 let arrived = func.new_vreg(GPR);
2393 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2394 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2395
2396 assert_eq!(combine(&mut func, &mut names), 0);
2397 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2398 }
2399
2400 #[test]
2402 fn a_reader_in_another_block_stays_where_it_is() {
2403 let (mut names, mut func, block) = empty();
2404 let next = func.create_block();
2405 let base = func.new_vreg(GPR);
2406 let other = func.new_vreg(GPR);
2407 let word = load(&mut func, &mut names, block, base);
2408 alu(&mut func, &mut names, next, "add_rr_64", other, word);
2409
2410 assert_eq!(combine(&mut func, &mut names), 0);
2411 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2412 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2413 }
2414
2415 #[test]
2417 fn a_reader_past_the_window_stays_where_it_is() {
2418 let (mut names, mut func, block) = empty();
2419 let base = func.new_vreg(GPR);
2420 let other = func.new_vreg(GPR);
2421 let word = load(&mut func, &mut names, block, base);
2422 let nop = op(&mut names, "nop");
2423 for _ in 0..WINDOW {
2424 func.build(block, nop).finish();
2425 }
2426 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2427
2428 assert_eq!(combine(&mut func, &mut names), 0);
2429 }
2430
2431 #[test]
2433 fn a_reader_at_the_edge_of_the_window_folds() {
2434 let (mut names, mut func, block) = empty();
2435 let base = func.new_vreg(GPR);
2436 let other = func.new_vreg(GPR);
2437 let word = load(&mut func, &mut names, block, base);
2438 let nop = op(&mut names, "nop");
2439 for _ in 0..WINDOW - 1 {
2440 func.build(block, nop).finish();
2441 }
2442 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2443
2444 assert_eq!(combine(&mut func, &mut names), 1);
2445 }
2446
2447 #[test]
2450 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2451 let (mut names, mut func, block) = empty();
2452 let base = Reg::physical(rucc_target::x86_64::RSP);
2453 let other = func.new_vreg(GPR);
2454 let word = load(&mut func, &mut names, block, base);
2455 let reader = func.insts(block).nth(1);
2456 assert!(reader.is_none(), "the block holds the load alone so far");
2457 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2458 let held = func.insts(block).next().expect("the load");
2459
2460 let mut addresses = vec![(held, 3usize)];
2461 let mut arguments = Vec::new();
2462 let mut dynamic = Vec::new();
2463 let mut pending =
2464 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2465 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2466
2467 let inst = func.insts(block).next().expect("the addition");
2468 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2469 }
2470
2471 #[test]
2475 fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2476 let (mut names, mut func, block) = empty();
2477 let base = func.new_vreg(GPR);
2478 let other = func.new_vreg(GPR);
2479 let word = load(&mut func, &mut names, block, base);
2480 compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2481
2482 assert_eq!(combine(&mut func, &mut names), 1);
2483 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2484 let inst = func.insts(block).next().expect("the comparison");
2485 let mem = func[inst].mem.expect("it reads memory");
2486 assert_eq!(func[mem].disp, 16, "the address came from the load");
2487 assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2488 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2489 assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2490 }
2491
2492 #[test]
2496 fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2497 let (mut names, mut func, block) = empty();
2498 let base = func.new_vreg(GPR);
2499 let other = func.new_vreg(GPR);
2500 let word = load(&mut func, &mut names, block, base);
2501 compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2502
2503 assert_eq!(combine(&mut func, &mut names), 1);
2504 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2505 let inst = func.insts(block).next().expect("the comparison");
2506 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2507 }
2508
2509 #[test]
2513 fn an_equality_folded_on_either_side_is_the_same_comparison() {
2514 for (first, second) in [(true, false), (false, true)] {
2515 let (mut names, mut func, block) = empty();
2516 let base = func.new_vreg(GPR);
2517 let other = func.new_vreg(GPR);
2518 let word = load(&mut func, &mut names, block, base);
2519 let left = if first { word } else { other };
2520 let right = if second { word } else { other };
2521 compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2522
2523 assert_eq!(combine(&mut func, &mut names), 1);
2524 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2525 }
2526 }
2527
2528 #[test]
2533 fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2534 let (mut names, mut func, block) = empty();
2535 let base = func.new_vreg(GPR);
2536 let byte = func.new_vreg(GPR);
2537 let word = load(&mut func, &mut names, block, base);
2538 let opcode = op(&mut names, "cmp_set_l_ri_64");
2539 func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2540
2541 assert_eq!(combine(&mut func, &mut names), 1);
2542 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2543 let inst = func.insts(block).next().expect("the comparison");
2544 let mem = func[inst].mem.expect("it reads memory now");
2545 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2546 assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2547 assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2548 assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2549 let imm = func[inst].imm.expect("the constant is still on it");
2550 assert_eq!(func[imm].0, 7, "and is the one that was written");
2551 }
2552
2553 #[test]
2556 fn a_load_only_a_widening_reads_becomes_a_load_that_widens() {
2557 for row in WIDENINGS {
2558 let (mut names, mut func, block) = empty();
2559 let base = func.new_vreg(GPR);
2560 let narrow = func.new_vreg(GPR);
2561 let wide = func.new_vreg(GPR);
2562 let read = op(&mut names, row.load);
2563 func.build(block, read)
2564 .def(narrow, GPR)
2565 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2566 .finish();
2567 let widen = op(&mut names, row.from);
2568 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2569
2570 assert_eq!(combine(&mut func, &mut names), 1, "{} took no load", row.from);
2571 assert_eq!(shape(&func, &names, block), [format!("x64.{}", row.into)]);
2572 let inst = func.insts(block).next().expect("the widening");
2573 assert_eq!(func[func[inst].operands][0].reg, wide, "{} writes elsewhere", row.from);
2574 assert_eq!(func[func[inst].operands][1].reg, base, "{} lost the address", row.from);
2575 let mem = func[inst].mem.expect("it reads memory now");
2576 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2577 assert_eq!(func[mem].base, Some(1), "and names the operand behind the answer");
2578 }
2579 }
2580
2581 #[test]
2584 fn a_load_read_by_a_widening_and_something_else_stays_where_it_is() {
2585 let (mut names, mut func, block) = empty();
2586 let base = func.new_vreg(GPR);
2587 let other = func.new_vreg(GPR);
2588 let narrow = func.new_vreg(GPR);
2589 let wide = func.new_vreg(GPR);
2590 let read = op(&mut names, "mov_rm_32");
2591 func.build(block, read)
2592 .def(narrow, GPR)
2593 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2594 .finish();
2595 let widen = op(&mut names, "movsxd_32_64");
2596 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2597 alu(&mut func, &mut names, block, "add_rr_32", other, narrow);
2598
2599 assert_eq!(combine(&mut func, &mut names), 0);
2600 assert_eq!(
2601 shape(&func, &names, block),
2602 ["x64.mov_rm_32", "x64.movsxd_32_64", "x64.add_rr_32"]
2603 );
2604 }
2605
2606 #[test]
2609 fn a_volatile_load_is_not_widened_on_the_way_in() {
2610 let (mut names, mut func, block) = empty();
2611 let base = func.new_vreg(GPR);
2612 let narrow = func.new_vreg(GPR);
2613 let wide = func.new_vreg(GPR);
2614 let read = op(&mut names, "mov_rm_16");
2615 func.build(block, read)
2616 .def(narrow, GPR)
2617 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2618 .flags(Flags::VOLATILE)
2619 .finish();
2620 let widen = op(&mut names, "movsx_16_32");
2621 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2622
2623 assert_eq!(combine(&mut func, &mut names), 0);
2624 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_16", "x64.movsx_16_32"]);
2625 }
2626
2627 #[test]
2630 fn every_widening_takes_a_load_of_the_width_it_widens_from() {
2631 let from = |name: &str| {
2632 name.split('_').find(|part| part.parse::<u32>().is_ok()).map(str::to_owned)
2633 };
2634 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2635 for row in WIDENINGS {
2636 assert!(MACHINE.has(row.from), "{} is not an instruction", row.from);
2637 assert!(MACHINE.has(row.into), "{} is not an instruction", row.into);
2638 assert!(MACHINE.has(row.load), "{} is not an instruction", row.load);
2639 assert_eq!(from(row.from), width(row.load), "{} loads another width", row.from);
2640 assert!((MACHINE.takes_mem)(row.into), "{} reads no memory", row.into);
2641 assert!(!(MACHINE.takes_mem)(row.from), "{} already reads memory", row.from);
2642 assert_eq!(row.swapped, None, "{} has nothing to swap", row.from);
2643 }
2644 }
2645
2646 #[test]
2650 fn every_row_of_the_table_is_three_instructions_this_target_has() {
2651 for fold in FOLDS {
2652 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2653 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2654 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2655 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2656 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2657 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2658 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2659 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2660 let Some(swapped) = fold.swapped else { continue };
2661 assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2662 assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2663 assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2664 }
2665 }
2666
2667 #[test]
2671 fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2672 let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2673 assert_eq!(
2674 compares, 80,
2675 "ten conditions at four widths, against a register and a constant"
2676 );
2677 let arithmetic = FOLDS.len() - compares;
2678 assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2679 let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2680 assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2681 }
2682
2683 #[test]
2691 fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2692 let turned = |condition: &str| match condition {
2693 "e" => "e",
2694 "ne" => "ne",
2695 "l" => "g",
2696 "g" => "l",
2697 "le" => "ge",
2698 "ge" => "le",
2699 "b" => "a",
2700 "a" => "b",
2701 "be" => "ae",
2702 "ae" => "be",
2703 other => panic!("{other} is not a condition this machine has"),
2704 };
2705 let compares = FOLDS
2706 .iter()
2707 .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2708 for fold in compares {
2709 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2710 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2711 assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2712 let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2713 assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2714 }
2715 }
2716
2717 #[test]
2722 fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2723 let compares = FOLDS
2724 .iter()
2725 .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2726 let mut rows = 0;
2727 for fold in compares {
2728 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2729 let front = front.strip_suffix("_ri").expect("a name against a constant");
2730 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2731 assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2732 assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2733 assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2734 rows += 1;
2735 }
2736 assert_eq!(rows, 40, "ten conditions at four widths");
2737 }
2738
2739 #[test]
2746 fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2747 let (mut names, mut func, block) = empty();
2748 let base = func.new_vreg(GPR);
2749 let other = func.new_vreg(GPR);
2750 let word = insisted_load(&mut func, &mut names, block, base);
2751 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2752
2753 assert_eq!(combine(&mut func, &mut names), 0);
2754 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2755 }
2756
2757 #[test]
2761 fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2762 let (mut names, mut func, block) = empty();
2763 let base = func.new_vreg(GPR);
2764 let other = func.new_vreg(GPR);
2765 let word = insisted_load(&mut func, &mut names, block, base);
2766 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2767 store(&mut func, &mut names, block, base, sum);
2768
2769 assert_eq!(update(&mut func, &mut names), 0);
2770 }
2771
2772 #[test]
2777 fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2778 let (mut names, mut func, block) = empty();
2779 let base = func.new_vreg(GPR);
2780 let other = func.new_vreg(GPR);
2781 let word = load(&mut func, &mut names, block, base);
2782 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2783 insisted_store(&mut func, &mut names, block, base, sum);
2784
2785 assert_eq!(update(&mut func, &mut names), 0);
2786 }
2787
2788 #[test]
2791 fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2792 let (mut names, mut func, block) = empty();
2793 let base = func.new_vreg(GPR);
2794 let word = insisted_load(&mut func, &mut names, block, base);
2795 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2796 store(&mut func, &mut names, block, base, sum);
2797
2798 assert_eq!(update(&mut func, &mut names), 0);
2799 }
2800
2801 #[test]
2803 fn a_constant_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 word = load(&mut func, &mut names, block, base);
2807 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
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 the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2817 let (mut names, mut func, block) = empty();
2818 let base = func.new_vreg(GPR);
2819 let other = func.new_vreg(GPR);
2820 let word = load(&mut func, &mut names, block, base);
2821 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2822 store(&mut func, &mut names, block, base, sum);
2823
2824 assert_eq!(update(&mut func, &mut names), 1);
2825 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2826 }
2827}