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] = &[
903 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
904 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
905 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
906 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
907 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
908 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
909 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
910 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
911 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
912 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
913 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
914 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
915 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
916 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
917 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
918 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
919 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
920 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
921 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
922 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
923];
924
925#[derive(Debug, Clone, Copy)]
930struct Waiting {
931 inst: Inst,
933 reg: Reg,
935 load: &'static str,
937 at: usize,
939}
940
941pub fn loads(
952 func: &mut Func,
953 machine: &MachineInsts,
954 names: &mut Interner,
955 pending: &mut Pending<'_>,
956) -> usize {
957 let mut reads = Reads::of(func);
958 let mut done = 0;
959 for block in func.blocks().collect::<Vec<_>>() {
960 let mut waiting: Option<Waiting> = None;
961 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
962 let name = names.resolve(func[inst].opcode.name()).to_owned();
963 let bare = machine.bare(&name).to_owned();
964 let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
970 if let Some(carried) = waiting {
971 if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
972 let mut set = Changes::new();
973 set.rewrite(inst, plan);
974 set.remove(carried.inst);
975 if set.commit(func, &mut reads, names, machine).is_ok() {
976 pending.moved(carried.inst, &[inst]);
977 waiting = None;
978 done += 1;
979 }
980 }
981 }
982 if barrier {
983 waiting = None;
984 }
985 if let Some(carried) = waiting {
986 if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
987 waiting = None;
988 }
989 }
990 if insisted(func, inst) {
996 continue;
997 }
998 let mut rows = FOLDS.iter().chain(WIDENINGS);
999 if let Some(load) = rows.find(|fold| fold.load == bare).map(|fold| fold.load) {
1000 let operands = &func[func[inst].operands];
1001 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
1002 waiting = Some(Waiting { inst, reg: first.reg, load, at });
1003 }
1004 }
1005 }
1006 }
1007 done
1008}
1009
1010#[derive(Debug, Clone, Copy)]
1012struct Run {
1013 load: Inst,
1015 alu: Inst,
1017 store: Inst,
1019 update: &'static Update,
1021 kept: Operand,
1023}
1024
1025#[derive(Debug, Clone, Copy)]
1031struct Bumped {
1032 load: Inst,
1034 alu: Inst,
1036 store: Inst,
1038 bump: &'static Bump,
1040 imm: i64,
1042}
1043
1044pub fn stores(
1061 func: &mut Func,
1062 machine: &MachineInsts,
1063 flags: &FlagInsts,
1064 names: &mut Interner,
1065 pending: &mut Pending<'_>,
1066) -> usize {
1067 let mut reads = Reads::of(func);
1068 let mut done = 0;
1069 for block in func.blocks().collect::<Vec<_>>() {
1070 let insts: Vec<Inst> = func.insts(block).collect();
1071 for at in 0..insts.len() {
1072 let found = match run(func, &reads, machine, flags, names, &insts, at) {
1073 Some(found) => Some((
1074 found.load,
1075 found.alu,
1076 found.store,
1077 updated(func, machine, names, &found),
1078 )),
1079 None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1080 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1081 }),
1082 };
1083 let Some((load, alu, store, plan)) = found else { continue };
1084 if !pending.alike(load, store) {
1085 continue;
1086 }
1087 let mut set = Changes::new();
1088 set.rewrite(store, plan);
1089 set.remove(alu);
1090 set.remove(load);
1091 if set.commit(func, &mut reads, names, machine).is_ok() {
1092 pending.moved(load, &[]);
1093 done += 1;
1094 }
1095 }
1096 }
1097 done
1098}
1099
1100fn run(
1112 func: &Func,
1113 reads: &Reads,
1114 machine: &MachineInsts,
1115 flags: &FlagInsts,
1116 names: &Interner,
1117 insts: &[Inst],
1118 at: usize,
1119) -> Option<Run> {
1120 let store = insts[at];
1121 if insisted(func, store) {
1122 return None;
1123 }
1124 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1125 let value = *func[func[store].operands].first()?;
1126 if value.role.is_def() || reads.count(value.reg) != 1 {
1127 return None;
1128 }
1129 let earliest = at.saturating_sub(WINDOW);
1132 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1133 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1134 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1135 if !quiet(func, flags, names, insts, (alu, at)) {
1136 return None;
1137 }
1138 let operands = func[func[insts[alu]].operands].to_vec();
1139 let [_, first, second] = operands[..] else { return None };
1140 let both = [(first, second), (second, first)];
1145 let tried = if update.commutes { &both[..] } else { &both[..1] };
1146 for &(source, kept) in tried {
1147 if reads.count(source.reg) != 1 {
1148 continue;
1149 }
1150 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1151 continue;
1152 };
1153 let load = insts[from];
1154 if insisted(func, load) {
1155 continue;
1156 }
1157 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1158 continue;
1159 }
1160 if !same_place(func, load, store) {
1161 continue;
1162 }
1163 let mut wanted: Vec<Reg> =
1168 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1169 wanted.push(kept.reg);
1170 if !clear(func, machine, names, insts, (from, at), &wanted) {
1171 continue;
1172 }
1173 return Some(Run { load, alu: insts[alu], store, update, kept });
1174 }
1175 None
1176}
1177
1178fn constant(
1192 func: &Func,
1193 reads: &Reads,
1194 machine: &MachineInsts,
1195 flags: &FlagInsts,
1196 names: &Interner,
1197 insts: &[Inst],
1198 at: usize,
1199) -> Option<Bumped> {
1200 let store = insts[at];
1201 if insisted(func, store) {
1202 return None;
1203 }
1204 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1205 let value = *func[func[store].operands].first()?;
1206 if value.role.is_def() || reads.count(value.reg) != 1 {
1207 return None;
1208 }
1209 let mem = func[func[store].mem?];
1210 if mem.base == Some(0) || mem.index == Some(0) {
1211 return None;
1212 }
1213 let earliest = at.saturating_sub(WINDOW);
1214 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1215 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1216 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1217 if !quiet(func, flags, names, insts, (alu, at)) {
1218 return None;
1219 }
1220 let operands = func[func[insts[alu]].operands].to_vec();
1221 let [_, source] = operands[..] else { return None };
1222 let imm = func[func[insts[alu]].imm?].0;
1223 if reads.count(source.reg) != 1 {
1224 return None;
1225 }
1226 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1227 let load = insts[from];
1228 if insisted(func, load) {
1229 return None;
1230 }
1231 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1232 return None;
1233 }
1234 if !same_place(func, load, store) {
1235 return None;
1236 }
1237 let wanted: Vec<Reg> =
1240 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1241 if !clear(func, machine, names, insts, (from, at), &wanted) {
1242 return None;
1243 }
1244 Some(Bumped { load, alu: insts[alu], store, bump, imm })
1245}
1246
1247fn insisted(func: &Func, inst: Inst) -> bool {
1252 func[inst].flags.contains(Flags::VOLATILE)
1253}
1254
1255fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1257 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1258}
1259
1260fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1267 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1268 let (here, there) = (func[here], func[there]);
1269 if func[one].symbol != func[other].symbol {
1270 return false;
1271 }
1272 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1273 if bare(here) != bare(there) {
1274 return false;
1275 }
1276 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1277 (None, None) => true,
1278 (Some(left), Some(right)) => {
1279 func[func[one].operands][usize::from(left)].reg
1280 == func[func[other].operands][usize::from(right)].reg
1281 }
1282 _ => false,
1283 };
1284 same(here.base, there.base) && same(here.index, there.index)
1285}
1286
1287fn clear(
1294 func: &Func,
1295 machine: &MachineInsts,
1296 names: &Interner,
1297 insts: &[Inst],
1298 span: (usize, usize),
1299 wanted: &[Reg],
1300) -> bool {
1301 let (from, to) = span;
1302 insts[from + 1..to].iter().all(|&inst| {
1303 let name = names.resolve(func[inst].opcode.name());
1304 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1305 return false;
1306 }
1307 !func[func[inst].operands]
1308 .iter()
1309 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1310 })
1311}
1312
1313fn quiet(
1330 func: &Func,
1331 flags: &FlagInsts,
1332 names: &Interner,
1333 insts: &[Inst],
1334 span: (usize, usize),
1335) -> bool {
1336 let (alu, to) = span;
1337 insts[alu + 1..to].iter().all(|&inst| {
1338 let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1339 return false;
1340 };
1341 flags.reads(name).is_none() && !(flags.writes)(name)
1342 })
1343}
1344
1345fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1351 let operands = func[func[run.store].operands].to_vec();
1352 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1353 Plan {
1354 opcode: Opcode::new(into),
1355 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1356 imm: None,
1357 amode: func[run.store].mem.map(|mem| func[mem]),
1358 symbol: func[run.store].symbol,
1359 }
1360}
1361
1362fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1370 let operands = func[func[run.store].operands][1..].to_vec();
1371 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1372 let back = |at: Option<u8>| at.map(|at| at - 1);
1373 Plan {
1374 opcode: Opcode::new(into),
1375 operands,
1376 imm: Some(run.imm),
1377 amode: func[run.store].mem.map(|mem| {
1378 let mem = func[mem];
1379 Amode { base: back(mem.base), index: back(mem.index), ..mem }
1380 }),
1381 symbol: func[run.store].symbol,
1382 }
1383}
1384
1385fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1391 let written: Vec<Reg> = func[func[inst].operands]
1392 .iter()
1393 .filter(|operand| operand.role.is_def())
1394 .map(|operand| operand.reg)
1395 .collect();
1396 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1397}
1398
1399fn joined(
1404 func: &Func,
1405 reads: &Reads,
1406 carried: Waiting,
1407 machine: &MachineInsts,
1408 names: &mut Interner,
1409 inst: Inst,
1410 bare: &str,
1411) -> Option<Plan> {
1412 let fold = FOLDS.iter().chain(WIDENINGS).find(|fold| fold.from == bare)?;
1413 if carried.load != fold.load || reads.count(carried.reg) != 1 {
1414 return None;
1415 }
1416 let operands = func[func[inst].operands].to_vec();
1417 let (front, into) = match operands[..] {
1428 [answer, first, second] => {
1429 let (kept, into) = if second.reg == carried.reg {
1430 (first, fold.into)
1431 } else if first.reg == carried.reg {
1432 (second, fold.swapped?)
1433 } else {
1434 return None;
1435 };
1436 (vec![answer, kept], into)
1437 }
1438 [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1439 _ => return None,
1440 };
1441 let load = carried.inst;
1442 let address = func[func[load].operands][1..].to_vec();
1443 let mut amode = func[func[load].mem?];
1444 let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1448 amode.base = amode.base.map(|at| at + along);
1449 amode.index = amode.index.map(|at| at + along);
1450 let into = names.intern(&format!("{}{}", machine.prefix, into));
1451 Some(Plan {
1452 opcode: Opcode::new(into),
1453 operands: front.into_iter().chain(address).collect(),
1454 imm: func[inst].imm.map(|at| func[at].0),
1455 amode: Some(amode),
1456 symbol: func[load].symbol,
1457 })
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462 use rucc_mir::{self as mir, Constraint, Mem, Operand};
1463 use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1464
1465 use super::*;
1466
1467 fn empty() -> (Interner, Func, mir::Block) {
1469 let mut names = Interner::new();
1470 let mut func = Func::new(names.intern("f"));
1471 let block = func.create_block();
1472 (names, func, block)
1473 }
1474
1475 fn op(names: &mut Interner, name: &str) -> Opcode {
1477 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1478 }
1479
1480 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1482 let into = func.new_vreg(GPR);
1483 let mov = op(names, "mov_rm_64");
1484 func.build(block, mov)
1485 .def(into, GPR)
1486 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1487 .finish();
1488 into
1489 }
1490
1491 fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1493 let into = func.new_vreg(GPR);
1494 let mov = op(names, "mov_rm_64");
1495 func.build(block, mov)
1496 .def(into, GPR)
1497 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1498 .flags(Flags::VOLATILE)
1499 .finish();
1500 into
1501 }
1502
1503 fn alu(
1505 func: &mut Func,
1506 names: &mut Interner,
1507 block: mir::Block,
1508 name: &str,
1509 first: Reg,
1510 second: Reg,
1511 ) -> Reg {
1512 let answer = func.new_vreg(GPR);
1513 let opcode = op(names, name);
1514 func.build(block, opcode)
1515 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1516 .uses(first, GPR)
1517 .uses(second, GPR)
1518 .finish();
1519 answer
1520 }
1521
1522 fn compare(
1525 func: &mut Func,
1526 names: &mut Interner,
1527 block: mir::Block,
1528 name: &str,
1529 first: Reg,
1530 second: Reg,
1531 ) -> Reg {
1532 let byte = func.new_vreg(GPR);
1533 let opcode = op(names, name);
1534 func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1535 byte
1536 }
1537
1538 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1540 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1541 }
1542
1543 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1545 let mut addresses = Vec::new();
1546 let mut arguments = Vec::new();
1547 let mut dynamic = Vec::new();
1548 let mut pending =
1549 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1550 loads(func, &MACHINE, names, &mut pending)
1551 }
1552
1553 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1555 let mov = op(names, "mov_mr_64");
1556 func.build(block, mov)
1557 .uses(value, GPR)
1558 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1559 .finish();
1560 }
1561
1562 fn insisted_store(
1564 func: &mut Func,
1565 names: &mut Interner,
1566 block: mir::Block,
1567 base: Reg,
1568 value: Reg,
1569 ) {
1570 let mov = op(names, "mov_mr_64");
1571 func.build(block, mov)
1572 .uses(value, GPR)
1573 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1574 .flags(Flags::VOLATILE)
1575 .finish();
1576 }
1577
1578 fn update(func: &mut Func, names: &mut Interner) -> usize {
1580 let mut addresses = Vec::new();
1581 let mut arguments = Vec::new();
1582 let mut dynamic = Vec::new();
1583 let mut pending =
1584 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1585 stores(func, &MACHINE, &FLAGS, names, &mut pending)
1586 }
1587
1588 #[test]
1590 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1591 let (mut names, mut func, block) = empty();
1592 let base = func.new_vreg(GPR);
1593 let other = func.new_vreg(GPR);
1594 let word = load(&mut func, &mut names, block, base);
1595 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1596 store(&mut func, &mut names, block, base, sum);
1597
1598 assert_eq!(update(&mut func, &mut names), 1);
1599 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1600 let inst = func.insts(block).next().expect("the addition");
1601 let mem = func[inst].mem.expect("it writes memory");
1602 assert_eq!(func[mem].disp, 16, "the address came from the store");
1603 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1604 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1605 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1606 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1607 }
1608
1609 #[test]
1613 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1614 let (mut names, mut func, block) = empty();
1615 let base = func.new_vreg(GPR);
1616 let other = func.new_vreg(GPR);
1617 let word = load(&mut func, &mut names, block, base);
1618 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1619 store(&mut func, &mut names, block, base, sum);
1620
1621 assert_eq!(update(&mut func, &mut names), 1);
1622 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1623 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1624 }
1625
1626 #[test]
1629 fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1630 let (mut names, mut func, block) = empty();
1631 let base = func.new_vreg(GPR);
1632 let other = func.new_vreg(GPR);
1633 let word = load(&mut func, &mut names, block, base);
1634 let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1635 store(&mut func, &mut names, block, base, left);
1636
1637 assert_eq!(update(&mut func, &mut names), 1);
1638 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1639 }
1640
1641 #[test]
1644 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1645 let (mut names, mut func, block) = empty();
1646 let base = func.new_vreg(GPR);
1647 let other = func.new_vreg(GPR);
1648 let word = load(&mut func, &mut names, block, base);
1649 let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1650 store(&mut func, &mut names, block, base, left);
1651
1652 assert_eq!(update(&mut func, &mut names), 0);
1653 assert_eq!(
1654 shape(&func, &names, block),
1655 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1656 );
1657 }
1658
1659 #[test]
1662 fn a_store_to_another_address_stays_three_instructions() {
1663 let (mut names, mut func, block) = empty();
1664 let base = func.new_vreg(GPR);
1665 let elsewhere = func.new_vreg(GPR);
1666 let other = func.new_vreg(GPR);
1667 let word = load(&mut func, &mut names, block, base);
1668 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1669 store(&mut func, &mut names, block, elsewhere, sum);
1670
1671 assert_eq!(update(&mut func, &mut names), 0);
1672 }
1673
1674 #[test]
1677 fn a_store_at_another_displacement_stays_three_instructions() {
1678 let (mut names, mut func, block) = empty();
1679 let base = func.new_vreg(GPR);
1680 let other = func.new_vreg(GPR);
1681 let word = load(&mut func, &mut names, block, base);
1682 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1683 let mov = op(&mut names, "mov_mr_64");
1684 func.build(block, mov)
1685 .uses(sum, GPR)
1686 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1687 .finish();
1688
1689 assert_eq!(update(&mut func, &mut names), 0);
1690 }
1691
1692 #[test]
1695 fn a_word_two_instructions_read_stays_three_instructions() {
1696 let (mut names, mut func, block) = empty();
1697 let base = func.new_vreg(GPR);
1698 let other = func.new_vreg(GPR);
1699 let word = load(&mut func, &mut names, block, base);
1700 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1701 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1702 store(&mut func, &mut names, block, base, sum);
1703
1704 assert_eq!(update(&mut func, &mut names), 0);
1705 }
1706
1707 #[test]
1710 fn an_answer_something_else_reads_stays_three_instructions() {
1711 let (mut names, mut func, block) = empty();
1712 let base = func.new_vreg(GPR);
1713 let other = func.new_vreg(GPR);
1714 let word = load(&mut func, &mut names, block, base);
1715 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1716 store(&mut func, &mut names, block, base, sum);
1717 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1718
1719 assert_eq!(update(&mut func, &mut names), 0);
1720 }
1721
1722 #[test]
1725 fn a_run_with_another_access_in_the_middle_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 load(&mut func, &mut names, block, other);
1731 let sum = alu(&mut func, &mut names, block, "add_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 a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1741 let (mut names, mut func, block) = empty();
1742 let base = Reg::physical(rucc_target::x86_64::RSP);
1743 let other = func.new_vreg(GPR);
1744 let word = load(&mut func, &mut names, block, base);
1745 let sub = op(&mut names, "sub_ri_64");
1746 func.build(block, sub)
1747 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1748 .uses(base, GPR)
1749 .imm(32)
1750 .finish();
1751 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1752 store(&mut func, &mut names, block, base, sum);
1753
1754 assert_eq!(update(&mut func, &mut names), 0);
1755 }
1756
1757 #[test]
1761 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1762 let (mut names, mut func, block) = empty();
1763 let base = Reg::physical(rucc_target::x86_64::RSP);
1764 let other = func.new_vreg(GPR);
1765 let mov = op(&mut names, "mov_rm_64");
1766 let word = func.new_vreg(GPR);
1767 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1768 let read = func.insts(block).next().expect("the load");
1769 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1770 let put = op(&mut names, "mov_mr_64");
1771 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1772 let written = func.insts(block).nth(2).expect("the store");
1773
1774 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1775 let mut arguments = Vec::new();
1776 let mut dynamic = Vec::new();
1777 let mut pending =
1778 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1779 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1780 }
1781
1782 #[test]
1786 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1787 let (mut names, mut func, block) = empty();
1788 let base = Reg::physical(rucc_target::x86_64::RSP);
1789 let other = func.new_vreg(GPR);
1790 let mov = op(&mut names, "mov_rm_64");
1791 let word = func.new_vreg(GPR);
1792 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1793 let read = func.insts(block).next().expect("the load");
1794 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1795 let put = op(&mut names, "mov_mr_64");
1796 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1797 let written = func.insts(block).nth(2).expect("the store");
1798
1799 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1800 let mut arguments = Vec::new();
1801 let mut dynamic = Vec::new();
1802 let mut pending =
1803 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1804 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
1805
1806 let inst = func.insts(block).next().expect("the addition");
1807 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1808 }
1809
1810 #[test]
1812 fn a_run_whose_widths_disagree_stays_three_instructions() {
1813 let (mut names, mut func, block) = empty();
1814 let base = func.new_vreg(GPR);
1815 let other = func.new_vreg(GPR);
1816 let into = func.new_vreg(GPR);
1817 let narrow = op(&mut names, "mov_rm_32");
1818 func.build(block, narrow)
1819 .def(into, GPR)
1820 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1821 .finish();
1822 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1823 store(&mut func, &mut names, block, base, sum);
1824
1825 assert_eq!(update(&mut func, &mut names), 0);
1826 }
1827
1828 #[test]
1832 fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1833 let (mut names, mut func, block) = empty();
1834 let base = func.new_vreg(GPR);
1835 let other = func.new_vreg(GPR);
1836 let carry = func.new_vreg(GPR);
1837 let word = load(&mut func, &mut names, block, base);
1838 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1839 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1840 store(&mut func, &mut names, block, base, sum);
1841
1842 assert_eq!(update(&mut func, &mut names), 0);
1843 }
1844
1845 #[test]
1850 fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1851 let (mut names, mut func, block) = empty();
1852 let base = func.new_vreg(GPR);
1853 let other = func.new_vreg(GPR);
1854 let left = func.new_vreg(GPR);
1855 let right = func.new_vreg(GPR);
1856 let word = load(&mut func, &mut names, block, base);
1857 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1858 alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1859 store(&mut func, &mut names, block, base, sum);
1860
1861 assert_eq!(update(&mut func, &mut names), 0);
1862 }
1863
1864 #[test]
1867 fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1868 let (mut names, mut func, block) = empty();
1869 let base = func.new_vreg(GPR);
1870 let other = func.new_vreg(GPR);
1871 let from = func.new_vreg(GPR);
1872 let into = func.new_vreg(GPR);
1873 let word = load(&mut func, &mut names, block, base);
1874 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1875 let copy = op(&mut names, "mov_rr_64");
1876 func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1877 store(&mut func, &mut names, block, base, sum);
1878
1879 assert_eq!(update(&mut func, &mut names), 1);
1880 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1881 }
1882
1883 #[test]
1885 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1886 for update in UPDATES {
1887 for name in [update.from, update.into, update.load, update.store] {
1888 assert!(MACHINE.has(name), "{name} is not an instruction");
1889 }
1890 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1891 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1892 assert_eq!(
1893 width(update.from),
1894 width(update.load),
1895 "{} loads another width",
1896 update.from
1897 );
1898 assert_eq!(
1899 width(update.from),
1900 width(update.store),
1901 "{} stores another width",
1902 update.from
1903 );
1904 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1905 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1906 }
1907 }
1908
1909 #[test]
1912 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1913 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1914 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1915 assert_eq!(commuting, 16, "everything but the four subtractions");
1916 }
1917
1918 fn alu_imm(
1920 func: &mut Func,
1921 names: &mut Interner,
1922 block: mir::Block,
1923 name: &str,
1924 source: Reg,
1925 value: i64,
1926 ) -> Reg {
1927 let answer = func.new_vreg(GPR);
1928 let opcode = op(names, name);
1929 func.build(block, opcode)
1930 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1931 .uses(source, GPR)
1932 .imm(value)
1933 .finish();
1934 answer
1935 }
1936
1937 #[test]
1939 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1940 let (mut names, mut func, block) = empty();
1941 let base = func.new_vreg(GPR);
1942 let word = load(&mut func, &mut names, block, base);
1943 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1944 store(&mut func, &mut names, block, base, sum);
1945
1946 assert_eq!(update(&mut func, &mut names), 1);
1947 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1948 let inst = func.insts(block).next().expect("the addition");
1949 let mem = func[inst].mem.expect("it writes memory");
1950 assert_eq!(func[mem].disp, 16, "the address came from the store");
1951 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1952 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1953 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1954 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1955 }
1956
1957 #[test]
1960 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1961 let (mut names, mut func, block) = empty();
1962 let base = func.new_vreg(GPR);
1963 let word = load(&mut func, &mut names, block, base);
1964 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1965 store(&mut func, &mut names, block, base, left);
1966
1967 assert_eq!(update(&mut func, &mut names), 1);
1968 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1969 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1970 }
1971
1972 #[test]
1975 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1976 let (mut names, mut func, block) = empty();
1977 let base = func.new_vreg(GPR);
1978 let word = func.new_vreg(GPR);
1979 let mov = op(&mut names, "mov_rm_8");
1980 func.build(block, mov)
1981 .def(word, GPR)
1982 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1983 .finish();
1984 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1985 let put = op(&mut names, "mov_mr_8");
1986 func.build(block, put)
1987 .uses(sum, GPR)
1988 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1989 .finish();
1990
1991 assert_eq!(update(&mut func, &mut names), 1);
1992 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1993 }
1994
1995 #[test]
1998 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
1999 let (mut names, mut func, block) = empty();
2000 let base = func.new_vreg(GPR);
2001 let other = func.new_vreg(GPR);
2002 let word = load(&mut func, &mut names, block, base);
2003 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2004 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
2005 store(&mut func, &mut names, block, base, sum);
2006
2007 assert_eq!(update(&mut func, &mut names), 0);
2008 }
2009
2010 #[test]
2013 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
2014 let (mut names, mut func, block) = empty();
2015 let base = func.new_vreg(GPR);
2016 let elsewhere = func.new_vreg(GPR);
2017 let word = load(&mut func, &mut names, block, base);
2018 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2019 load(&mut func, &mut names, block, elsewhere);
2020 store(&mut func, &mut names, block, base, sum);
2021
2022 assert_eq!(update(&mut func, &mut names), 0);
2023 }
2024
2025 #[test]
2028 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
2029 let (mut names, mut func, block) = empty();
2030 let base = Reg::physical(rucc_target::x86_64::RAX);
2031 let word = load(&mut func, &mut names, block, base);
2032 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2033 let mov = op(&mut names, "mov_ri_64");
2034 func.build(block, mov).def(base, GPR).imm(0).finish();
2035 store(&mut func, &mut names, block, base, sum);
2036
2037 assert_eq!(update(&mut func, &mut names), 0);
2038 }
2039
2040 #[test]
2042 fn a_constant_written_to_another_address_stays_three_instructions() {
2043 let (mut names, mut func, block) = empty();
2044 let base = func.new_vreg(GPR);
2045 let elsewhere = func.new_vreg(GPR);
2046 let word = load(&mut func, &mut names, block, base);
2047 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2048 store(&mut func, &mut names, block, elsewhere, sum);
2049
2050 assert_eq!(update(&mut func, &mut names), 0);
2051 }
2052
2053 #[test]
2055 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2056 let (mut names, mut func, block) = empty();
2057 let base = func.new_vreg(GPR);
2058 let into = func.new_vreg(GPR);
2059 let narrow = op(&mut names, "mov_rm_32");
2060 func.build(block, narrow)
2061 .def(into, GPR)
2062 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2063 .finish();
2064 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2065 store(&mut func, &mut names, block, base, sum);
2066
2067 assert_eq!(update(&mut func, &mut names), 0);
2068 }
2069
2070 #[test]
2073 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2074 let (mut names, mut func, block) = empty();
2075 let base = func.new_vreg(GPR);
2076 let word = load(&mut func, &mut names, block, base);
2077 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2078 store(&mut func, &mut names, block, base, product);
2079
2080 assert_eq!(update(&mut func, &mut names), 0);
2081 }
2082
2083 #[test]
2086 fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2087 let (mut names, mut func, block) = empty();
2088 let base = func.new_vreg(GPR);
2089 let carry = func.new_vreg(GPR);
2090 let word = load(&mut func, &mut names, block, base);
2091 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2092 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2093 store(&mut func, &mut names, block, base, sum);
2094
2095 assert_eq!(update(&mut func, &mut names), 0);
2096 }
2097
2098 #[test]
2101 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2102 let (mut names, mut func, block) = empty();
2103 let base = Reg::physical(rucc_target::x86_64::RSP);
2104 let mov = op(&mut names, "mov_rm_64");
2105 let word = func.new_vreg(GPR);
2106 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2107 let read = func.insts(block).next().expect("the load");
2108 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2109 let put = op(&mut names, "mov_mr_64");
2110 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2111 let written = func.insts(block).nth(2).expect("the store");
2112
2113 let mut addresses = vec![(read, 3usize), (written, 3usize)];
2114 let mut arguments = Vec::new();
2115 let mut dynamic = Vec::new();
2116 let mut pending =
2117 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2118 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2119
2120 let inst = func.insts(block).next().expect("the addition");
2121 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2122 }
2123
2124 #[test]
2127 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
2128 let (mut names, mut func, block) = empty();
2129 let base = Reg::physical(rucc_target::x86_64::RSP);
2130 let mov = op(&mut names, "mov_rm_64");
2131 let word = func.new_vreg(GPR);
2132 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2133 let read = func.insts(block).next().expect("the load");
2134 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2135 let put = op(&mut names, "mov_mr_64");
2136 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2137 let written = func.insts(block).nth(2).expect("the store");
2138
2139 let mut addresses = vec![(read, 3usize), (written, 4usize)];
2140 let mut arguments = Vec::new();
2141 let mut dynamic = Vec::new();
2142 let mut pending =
2143 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2144 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
2145 }
2146
2147 #[test]
2149 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2150 for bump in BUMPS {
2151 for name in [bump.from, bump.into, bump.load, bump.store] {
2152 assert!(MACHINE.has(name), "{name} is not an instruction");
2153 }
2154 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2155 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2156 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2157 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2158 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2159 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2160 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2161 }
2162 }
2163
2164 #[test]
2167 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2168 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2169 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2170 for bump in BUMPS {
2171 let same = bump.from.replace("_ri_", "_rr_");
2172 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2173 }
2174 }
2175
2176 #[test]
2179 fn nothing_is_both_a_register_run_and_a_constant_run() {
2180 for bump in BUMPS {
2181 assert!(
2182 !UPDATES.iter().any(|update| update.from == bump.from),
2183 "{} starts both kinds of run",
2184 bump.from
2185 );
2186 }
2187 }
2188
2189 #[test]
2191 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2192 let (mut names, mut func, block) = empty();
2193 let base = func.new_vreg(GPR);
2194 let other = func.new_vreg(GPR);
2195 let word = load(&mut func, &mut names, block, base);
2196 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2197
2198 assert_eq!(combine(&mut func, &mut names), 1);
2199 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2200 let inst = func.insts(block).next().expect("the addition");
2201 let mem = func[inst].mem.expect("the addition reads memory now");
2202 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2203 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2204 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2205 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2206 }
2207
2208 #[test]
2211 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2212 let (mut names, mut func, block) = empty();
2213 let base = func.new_vreg(GPR);
2214 let other = func.new_vreg(GPR);
2215 let word = load(&mut func, &mut names, block, base);
2216 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2217
2218 assert_eq!(combine(&mut func, &mut names), 1);
2219 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2220 let inst = func.insts(block).next().expect("the addition");
2221 assert_eq!(func[func[inst].operands][1].reg, other);
2222 }
2223
2224 #[test]
2227 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2228 let (mut names, mut func, block) = empty();
2229 let base = func.new_vreg(GPR);
2230 let other = func.new_vreg(GPR);
2231 let word = load(&mut func, &mut names, block, base);
2232 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2233
2234 assert_eq!(combine(&mut func, &mut names), 0);
2235 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2236 }
2237
2238 #[test]
2240 fn a_load_feeding_the_right_of_a_subtraction_folds() {
2241 let (mut names, mut func, block) = empty();
2242 let base = func.new_vreg(GPR);
2243 let other = func.new_vreg(GPR);
2244 let word = load(&mut func, &mut names, block, base);
2245 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2246
2247 assert_eq!(combine(&mut func, &mut names), 1);
2248 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2249 }
2250
2251 #[test]
2254 fn a_load_two_instructions_read_stays_a_load() {
2255 let (mut names, mut func, block) = empty();
2256 let base = func.new_vreg(GPR);
2257 let other = func.new_vreg(GPR);
2258 let word = load(&mut func, &mut names, block, base);
2259 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2260 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2261
2262 assert_eq!(combine(&mut func, &mut names), 0);
2263 assert_eq!(
2264 shape(&func, &names, block),
2265 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2266 );
2267 }
2268
2269 #[test]
2272 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2273 let (mut names, mut func, block) = empty();
2274 let base = func.new_vreg(GPR);
2275 let other = func.new_vreg(GPR);
2276 let word = load(&mut func, &mut names, block, base);
2277 let store = op(&mut names, "mov_mr_64");
2278 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2279 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2280
2281 assert_eq!(combine(&mut func, &mut names), 0);
2282 assert_eq!(
2283 shape(&func, &names, block),
2284 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2285 );
2286 }
2287
2288 #[test]
2294 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2295 let (mut names, mut func, block) = empty();
2296 let base = func.new_vreg(GPR);
2297 let other = func.new_vreg(GPR);
2298 let word = load(&mut func, &mut names, block, base);
2299 load(&mut func, &mut names, block, other);
2300 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2301
2302 assert_eq!(combine(&mut func, &mut names), 0);
2303 assert_eq!(
2304 shape(&func, &names, block),
2305 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2306 );
2307 }
2308
2309 #[test]
2312 fn the_later_of_two_loads_is_the_one_that_folds() {
2313 let (mut names, mut func, block) = empty();
2314 let base = func.new_vreg(GPR);
2315 let other = func.new_vreg(GPR);
2316 let first = load(&mut func, &mut names, block, base);
2317 let second = load(&mut func, &mut names, block, other);
2318 alu(&mut func, &mut names, block, "add_rr_64", first, second);
2319
2320 assert_eq!(combine(&mut func, &mut names), 1);
2321 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2322 let addition = func.insts(block).nth(1).expect("the addition");
2323 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2324 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2325 }
2326
2327 #[test]
2330 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2331 let (mut names, mut func, block) = empty();
2332 let base = func.new_vreg(GPR);
2333 let other = func.new_vreg(GPR);
2334 let word = load(&mut func, &mut names, block, base);
2335 let call = op(&mut names, "call");
2336 func.build(block, call).finish();
2337 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2338
2339 assert_eq!(combine(&mut func, &mut names), 0);
2340 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2341 }
2342
2343 #[test]
2346 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2347 let (mut names, mut func, block) = empty();
2348 let base = Reg::physical(rucc_target::x86_64::RSP);
2349 let other = func.new_vreg(GPR);
2350 let word = load(&mut func, &mut names, block, base);
2351 let sub = op(&mut names, "sub_ri_64");
2352 func.build(block, sub)
2353 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2354 .uses(base, GPR)
2355 .imm(32)
2356 .finish();
2357 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2358
2359 assert_eq!(combine(&mut func, &mut names), 0);
2360 }
2361
2362 #[test]
2365 fn a_load_of_the_wrong_width_stays_a_load() {
2366 let (mut names, mut func, block) = empty();
2367 let base = func.new_vreg(GPR);
2368 let other = func.new_vreg(GPR);
2369 let into = func.new_vreg(GPR);
2370 let narrow = op(&mut names, "mov_rm_32");
2371 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2372 alu(&mut func, &mut names, block, "add_rr_64", other, into);
2373
2374 assert_eq!(combine(&mut func, &mut names), 0);
2375 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2376 }
2377
2378 #[test]
2381 fn a_load_whose_value_an_edge_carries_stays_a_load() {
2382 let (mut names, mut func, block) = empty();
2383 let next = func.create_block();
2384 let base = func.new_vreg(GPR);
2385 let other = func.new_vreg(GPR);
2386 let word = load(&mut func, &mut names, block, base);
2387 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2388 let arrived = func.new_vreg(GPR);
2389 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2390 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2391
2392 assert_eq!(combine(&mut func, &mut names), 0);
2393 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2394 }
2395
2396 #[test]
2398 fn a_reader_in_another_block_stays_where_it_is() {
2399 let (mut names, mut func, block) = empty();
2400 let next = func.create_block();
2401 let base = func.new_vreg(GPR);
2402 let other = func.new_vreg(GPR);
2403 let word = load(&mut func, &mut names, block, base);
2404 alu(&mut func, &mut names, next, "add_rr_64", other, word);
2405
2406 assert_eq!(combine(&mut func, &mut names), 0);
2407 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2408 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2409 }
2410
2411 #[test]
2413 fn a_reader_past_the_window_stays_where_it_is() {
2414 let (mut names, mut func, block) = empty();
2415 let base = func.new_vreg(GPR);
2416 let other = func.new_vreg(GPR);
2417 let word = load(&mut func, &mut names, block, base);
2418 let nop = op(&mut names, "nop");
2419 for _ in 0..WINDOW {
2420 func.build(block, nop).finish();
2421 }
2422 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2423
2424 assert_eq!(combine(&mut func, &mut names), 0);
2425 }
2426
2427 #[test]
2429 fn a_reader_at_the_edge_of_the_window_folds() {
2430 let (mut names, mut func, block) = empty();
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 let nop = op(&mut names, "nop");
2435 for _ in 0..WINDOW - 1 {
2436 func.build(block, nop).finish();
2437 }
2438 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2439
2440 assert_eq!(combine(&mut func, &mut names), 1);
2441 }
2442
2443 #[test]
2446 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2447 let (mut names, mut func, block) = empty();
2448 let base = Reg::physical(rucc_target::x86_64::RSP);
2449 let other = func.new_vreg(GPR);
2450 let word = load(&mut func, &mut names, block, base);
2451 let reader = func.insts(block).nth(1);
2452 assert!(reader.is_none(), "the block holds the load alone so far");
2453 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2454 let held = func.insts(block).next().expect("the load");
2455
2456 let mut addresses = vec![(held, 3usize)];
2457 let mut arguments = Vec::new();
2458 let mut dynamic = Vec::new();
2459 let mut pending =
2460 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2461 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2462
2463 let inst = func.insts(block).next().expect("the addition");
2464 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2465 }
2466
2467 #[test]
2471 fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2472 let (mut names, mut func, block) = empty();
2473 let base = func.new_vreg(GPR);
2474 let other = func.new_vreg(GPR);
2475 let word = load(&mut func, &mut names, block, base);
2476 compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2477
2478 assert_eq!(combine(&mut func, &mut names), 1);
2479 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2480 let inst = func.insts(block).next().expect("the comparison");
2481 let mem = func[inst].mem.expect("it reads memory");
2482 assert_eq!(func[mem].disp, 16, "the address came from the load");
2483 assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2484 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2485 assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2486 }
2487
2488 #[test]
2492 fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2493 let (mut names, mut func, block) = empty();
2494 let base = func.new_vreg(GPR);
2495 let other = func.new_vreg(GPR);
2496 let word = load(&mut func, &mut names, block, base);
2497 compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2498
2499 assert_eq!(combine(&mut func, &mut names), 1);
2500 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2501 let inst = func.insts(block).next().expect("the comparison");
2502 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2503 }
2504
2505 #[test]
2509 fn an_equality_folded_on_either_side_is_the_same_comparison() {
2510 for (first, second) in [(true, false), (false, true)] {
2511 let (mut names, mut func, block) = empty();
2512 let base = func.new_vreg(GPR);
2513 let other = func.new_vreg(GPR);
2514 let word = load(&mut func, &mut names, block, base);
2515 let left = if first { word } else { other };
2516 let right = if second { word } else { other };
2517 compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2518
2519 assert_eq!(combine(&mut func, &mut names), 1);
2520 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2521 }
2522 }
2523
2524 #[test]
2529 fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2530 let (mut names, mut func, block) = empty();
2531 let base = func.new_vreg(GPR);
2532 let byte = func.new_vreg(GPR);
2533 let word = load(&mut func, &mut names, block, base);
2534 let opcode = op(&mut names, "cmp_set_l_ri_64");
2535 func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2536
2537 assert_eq!(combine(&mut func, &mut names), 1);
2538 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2539 let inst = func.insts(block).next().expect("the comparison");
2540 let mem = func[inst].mem.expect("it reads memory now");
2541 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2542 assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2543 assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2544 assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2545 let imm = func[inst].imm.expect("the constant is still on it");
2546 assert_eq!(func[imm].0, 7, "and is the one that was written");
2547 }
2548
2549 #[test]
2552 fn a_load_only_a_widening_reads_becomes_a_load_that_widens() {
2553 for row in WIDENINGS {
2554 let (mut names, mut func, block) = empty();
2555 let base = func.new_vreg(GPR);
2556 let narrow = func.new_vreg(GPR);
2557 let wide = func.new_vreg(GPR);
2558 let read = op(&mut names, row.load);
2559 func.build(block, read)
2560 .def(narrow, GPR)
2561 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2562 .finish();
2563 let widen = op(&mut names, row.from);
2564 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2565
2566 assert_eq!(combine(&mut func, &mut names), 1, "{} took no load", row.from);
2567 assert_eq!(shape(&func, &names, block), [format!("x64.{}", row.into)]);
2568 let inst = func.insts(block).next().expect("the widening");
2569 assert_eq!(func[func[inst].operands][0].reg, wide, "{} writes elsewhere", row.from);
2570 assert_eq!(func[func[inst].operands][1].reg, base, "{} lost the address", row.from);
2571 let mem = func[inst].mem.expect("it reads memory now");
2572 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2573 assert_eq!(func[mem].base, Some(1), "and names the operand behind the answer");
2574 }
2575 }
2576
2577 #[test]
2580 fn a_load_read_by_a_widening_and_something_else_stays_where_it_is() {
2581 let (mut names, mut func, block) = empty();
2582 let base = func.new_vreg(GPR);
2583 let other = func.new_vreg(GPR);
2584 let narrow = func.new_vreg(GPR);
2585 let wide = func.new_vreg(GPR);
2586 let read = op(&mut names, "mov_rm_32");
2587 func.build(block, read)
2588 .def(narrow, GPR)
2589 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2590 .finish();
2591 let widen = op(&mut names, "movsxd_32_64");
2592 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2593 alu(&mut func, &mut names, block, "add_rr_32", other, narrow);
2594
2595 assert_eq!(combine(&mut func, &mut names), 0);
2596 assert_eq!(
2597 shape(&func, &names, block),
2598 ["x64.mov_rm_32", "x64.movsxd_32_64", "x64.add_rr_32"]
2599 );
2600 }
2601
2602 #[test]
2605 fn a_volatile_load_is_not_widened_on_the_way_in() {
2606 let (mut names, mut func, block) = empty();
2607 let base = func.new_vreg(GPR);
2608 let narrow = func.new_vreg(GPR);
2609 let wide = func.new_vreg(GPR);
2610 let read = op(&mut names, "mov_rm_16");
2611 func.build(block, read)
2612 .def(narrow, GPR)
2613 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2614 .flags(Flags::VOLATILE)
2615 .finish();
2616 let widen = op(&mut names, "movsx_16_32");
2617 func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2618
2619 assert_eq!(combine(&mut func, &mut names), 0);
2620 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_16", "x64.movsx_16_32"]);
2621 }
2622
2623 #[test]
2626 fn every_widening_takes_a_load_of_the_width_it_widens_from() {
2627 let from = |name: &str| {
2628 name.split('_').find(|part| part.parse::<u32>().is_ok()).map(str::to_owned)
2629 };
2630 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2631 for row in WIDENINGS {
2632 assert!(MACHINE.has(row.from), "{} is not an instruction", row.from);
2633 assert!(MACHINE.has(row.into), "{} is not an instruction", row.into);
2634 assert!(MACHINE.has(row.load), "{} is not an instruction", row.load);
2635 assert_eq!(from(row.from), width(row.load), "{} loads another width", row.from);
2636 assert!((MACHINE.takes_mem)(row.into), "{} reads no memory", row.into);
2637 assert!(!(MACHINE.takes_mem)(row.from), "{} already reads memory", row.from);
2638 assert_eq!(row.swapped, None, "{} has nothing to swap", row.from);
2639 }
2640 }
2641
2642 #[test]
2646 fn every_row_of_the_table_is_three_instructions_this_target_has() {
2647 for fold in FOLDS {
2648 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2649 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2650 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2651 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2652 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2653 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2654 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2655 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2656 let Some(swapped) = fold.swapped else { continue };
2657 assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2658 assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2659 assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2660 }
2661 }
2662
2663 #[test]
2667 fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2668 let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2669 assert_eq!(
2670 compares, 80,
2671 "ten conditions at four widths, against a register and a constant"
2672 );
2673 let arithmetic = FOLDS.len() - compares;
2674 assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2675 let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2676 assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2677 }
2678
2679 #[test]
2687 fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2688 let turned = |condition: &str| match condition {
2689 "e" => "e",
2690 "ne" => "ne",
2691 "l" => "g",
2692 "g" => "l",
2693 "le" => "ge",
2694 "ge" => "le",
2695 "b" => "a",
2696 "a" => "b",
2697 "be" => "ae",
2698 "ae" => "be",
2699 other => panic!("{other} is not a condition this machine has"),
2700 };
2701 let compares = FOLDS
2702 .iter()
2703 .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2704 for fold in compares {
2705 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2706 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2707 assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2708 let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2709 assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2710 }
2711 }
2712
2713 #[test]
2718 fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2719 let compares = FOLDS
2720 .iter()
2721 .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2722 let mut rows = 0;
2723 for fold in compares {
2724 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2725 let front = front.strip_suffix("_ri").expect("a name against a constant");
2726 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2727 assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2728 assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2729 assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2730 rows += 1;
2731 }
2732 assert_eq!(rows, 40, "ten conditions at four widths");
2733 }
2734
2735 #[test]
2742 fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2743 let (mut names, mut func, block) = empty();
2744 let base = func.new_vreg(GPR);
2745 let other = func.new_vreg(GPR);
2746 let word = insisted_load(&mut func, &mut names, block, base);
2747 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2748
2749 assert_eq!(combine(&mut func, &mut names), 0);
2750 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2751 }
2752
2753 #[test]
2757 fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2758 let (mut names, mut func, block) = empty();
2759 let base = func.new_vreg(GPR);
2760 let other = func.new_vreg(GPR);
2761 let word = insisted_load(&mut func, &mut names, block, base);
2762 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2763 store(&mut func, &mut names, block, base, sum);
2764
2765 assert_eq!(update(&mut func, &mut names), 0);
2766 }
2767
2768 #[test]
2773 fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2774 let (mut names, mut func, block) = empty();
2775 let base = func.new_vreg(GPR);
2776 let other = func.new_vreg(GPR);
2777 let word = load(&mut func, &mut names, block, base);
2778 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2779 insisted_store(&mut func, &mut names, block, base, sum);
2780
2781 assert_eq!(update(&mut func, &mut names), 0);
2782 }
2783
2784 #[test]
2787 fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2788 let (mut names, mut func, block) = empty();
2789 let base = func.new_vreg(GPR);
2790 let word = insisted_load(&mut func, &mut names, block, base);
2791 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2792 store(&mut func, &mut names, block, base, sum);
2793
2794 assert_eq!(update(&mut func, &mut names), 0);
2795 }
2796
2797 #[test]
2799 fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2800 let (mut names, mut func, block) = empty();
2801 let base = func.new_vreg(GPR);
2802 let word = load(&mut func, &mut names, block, base);
2803 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2804 insisted_store(&mut func, &mut names, block, base, sum);
2805
2806 assert_eq!(update(&mut func, &mut names), 0);
2807 }
2808
2809 #[test]
2812 fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2813 let (mut names, mut func, block) = empty();
2814 let base = func.new_vreg(GPR);
2815 let other = func.new_vreg(GPR);
2816 let word = load(&mut func, &mut names, block, base);
2817 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2818 store(&mut func, &mut names, block, base, sum);
2819
2820 assert_eq!(update(&mut func, &mut names), 1);
2821 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2822 }
2823}