1use rucc_base::Interner;
280use rucc_mir::{Amode, Flags, Func, Inst, Opcode, Operand, Reg};
281use rucc_target::{FlagInsts, MachineInsts};
282
283use crate::changes::{Changes, Plan, Reads};
284use crate::fold::Pending;
285
286pub const WINDOW: usize = 16;
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
312pub struct Fold {
313 pub from: &'static str,
315 pub into: &'static str,
317 pub load: &'static str,
319 pub swapped: Option<&'static str>,
335}
336
337pub static FOLDS: &[Fold] = &[
352 Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", swapped: Some("add_rm_8") },
353 Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", swapped: Some("add_rm_16") },
354 Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", swapped: Some("add_rm_32") },
355 Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", swapped: Some("add_rm_64") },
356 Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", swapped: None },
357 Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", swapped: None },
358 Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", swapped: None },
359 Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", swapped: None },
360 Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", swapped: Some("and_rm_8") },
361 Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", swapped: Some("and_rm_16") },
362 Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", swapped: Some("and_rm_32") },
363 Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", swapped: Some("and_rm_64") },
364 Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", swapped: Some("or_rm_8") },
365 Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", swapped: Some("or_rm_16") },
366 Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", swapped: Some("or_rm_32") },
367 Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", swapped: Some("or_rm_64") },
368 Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", swapped: Some("xor_rm_8") },
369 Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", swapped: Some("xor_rm_16") },
370 Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", swapped: Some("xor_rm_32") },
371 Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", swapped: Some("xor_rm_64") },
372 Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", swapped: Some("imul_rm_16") },
373 Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", swapped: Some("imul_rm_32") },
374 Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", swapped: Some("imul_rm_64") },
375 Fold {
376 from: "cmp_set_e_8",
377 into: "cmp_set_e_rm_8",
378 load: "mov_rm_8",
379 swapped: Some("cmp_set_e_rm_8"),
380 },
381 Fold {
382 from: "cmp_set_e_16",
383 into: "cmp_set_e_rm_16",
384 load: "mov_rm_16",
385 swapped: Some("cmp_set_e_rm_16"),
386 },
387 Fold {
388 from: "cmp_set_e_32",
389 into: "cmp_set_e_rm_32",
390 load: "mov_rm_32",
391 swapped: Some("cmp_set_e_rm_32"),
392 },
393 Fold {
394 from: "cmp_set_e_64",
395 into: "cmp_set_e_rm_64",
396 load: "mov_rm_64",
397 swapped: Some("cmp_set_e_rm_64"),
398 },
399 Fold {
400 from: "cmp_set_ne_8",
401 into: "cmp_set_ne_rm_8",
402 load: "mov_rm_8",
403 swapped: Some("cmp_set_ne_rm_8"),
404 },
405 Fold {
406 from: "cmp_set_ne_16",
407 into: "cmp_set_ne_rm_16",
408 load: "mov_rm_16",
409 swapped: Some("cmp_set_ne_rm_16"),
410 },
411 Fold {
412 from: "cmp_set_ne_32",
413 into: "cmp_set_ne_rm_32",
414 load: "mov_rm_32",
415 swapped: Some("cmp_set_ne_rm_32"),
416 },
417 Fold {
418 from: "cmp_set_ne_64",
419 into: "cmp_set_ne_rm_64",
420 load: "mov_rm_64",
421 swapped: Some("cmp_set_ne_rm_64"),
422 },
423 Fold {
424 from: "cmp_set_l_8",
425 into: "cmp_set_l_rm_8",
426 load: "mov_rm_8",
427 swapped: Some("cmp_set_g_rm_8"),
428 },
429 Fold {
430 from: "cmp_set_l_16",
431 into: "cmp_set_l_rm_16",
432 load: "mov_rm_16",
433 swapped: Some("cmp_set_g_rm_16"),
434 },
435 Fold {
436 from: "cmp_set_l_32",
437 into: "cmp_set_l_rm_32",
438 load: "mov_rm_32",
439 swapped: Some("cmp_set_g_rm_32"),
440 },
441 Fold {
442 from: "cmp_set_l_64",
443 into: "cmp_set_l_rm_64",
444 load: "mov_rm_64",
445 swapped: Some("cmp_set_g_rm_64"),
446 },
447 Fold {
448 from: "cmp_set_le_8",
449 into: "cmp_set_le_rm_8",
450 load: "mov_rm_8",
451 swapped: Some("cmp_set_ge_rm_8"),
452 },
453 Fold {
454 from: "cmp_set_le_16",
455 into: "cmp_set_le_rm_16",
456 load: "mov_rm_16",
457 swapped: Some("cmp_set_ge_rm_16"),
458 },
459 Fold {
460 from: "cmp_set_le_32",
461 into: "cmp_set_le_rm_32",
462 load: "mov_rm_32",
463 swapped: Some("cmp_set_ge_rm_32"),
464 },
465 Fold {
466 from: "cmp_set_le_64",
467 into: "cmp_set_le_rm_64",
468 load: "mov_rm_64",
469 swapped: Some("cmp_set_ge_rm_64"),
470 },
471 Fold {
472 from: "cmp_set_g_8",
473 into: "cmp_set_g_rm_8",
474 load: "mov_rm_8",
475 swapped: Some("cmp_set_l_rm_8"),
476 },
477 Fold {
478 from: "cmp_set_g_16",
479 into: "cmp_set_g_rm_16",
480 load: "mov_rm_16",
481 swapped: Some("cmp_set_l_rm_16"),
482 },
483 Fold {
484 from: "cmp_set_g_32",
485 into: "cmp_set_g_rm_32",
486 load: "mov_rm_32",
487 swapped: Some("cmp_set_l_rm_32"),
488 },
489 Fold {
490 from: "cmp_set_g_64",
491 into: "cmp_set_g_rm_64",
492 load: "mov_rm_64",
493 swapped: Some("cmp_set_l_rm_64"),
494 },
495 Fold {
496 from: "cmp_set_ge_8",
497 into: "cmp_set_ge_rm_8",
498 load: "mov_rm_8",
499 swapped: Some("cmp_set_le_rm_8"),
500 },
501 Fold {
502 from: "cmp_set_ge_16",
503 into: "cmp_set_ge_rm_16",
504 load: "mov_rm_16",
505 swapped: Some("cmp_set_le_rm_16"),
506 },
507 Fold {
508 from: "cmp_set_ge_32",
509 into: "cmp_set_ge_rm_32",
510 load: "mov_rm_32",
511 swapped: Some("cmp_set_le_rm_32"),
512 },
513 Fold {
514 from: "cmp_set_ge_64",
515 into: "cmp_set_ge_rm_64",
516 load: "mov_rm_64",
517 swapped: Some("cmp_set_le_rm_64"),
518 },
519 Fold {
520 from: "cmp_set_b_8",
521 into: "cmp_set_b_rm_8",
522 load: "mov_rm_8",
523 swapped: Some("cmp_set_a_rm_8"),
524 },
525 Fold {
526 from: "cmp_set_b_16",
527 into: "cmp_set_b_rm_16",
528 load: "mov_rm_16",
529 swapped: Some("cmp_set_a_rm_16"),
530 },
531 Fold {
532 from: "cmp_set_b_32",
533 into: "cmp_set_b_rm_32",
534 load: "mov_rm_32",
535 swapped: Some("cmp_set_a_rm_32"),
536 },
537 Fold {
538 from: "cmp_set_b_64",
539 into: "cmp_set_b_rm_64",
540 load: "mov_rm_64",
541 swapped: Some("cmp_set_a_rm_64"),
542 },
543 Fold {
544 from: "cmp_set_be_8",
545 into: "cmp_set_be_rm_8",
546 load: "mov_rm_8",
547 swapped: Some("cmp_set_ae_rm_8"),
548 },
549 Fold {
550 from: "cmp_set_be_16",
551 into: "cmp_set_be_rm_16",
552 load: "mov_rm_16",
553 swapped: Some("cmp_set_ae_rm_16"),
554 },
555 Fold {
556 from: "cmp_set_be_32",
557 into: "cmp_set_be_rm_32",
558 load: "mov_rm_32",
559 swapped: Some("cmp_set_ae_rm_32"),
560 },
561 Fold {
562 from: "cmp_set_be_64",
563 into: "cmp_set_be_rm_64",
564 load: "mov_rm_64",
565 swapped: Some("cmp_set_ae_rm_64"),
566 },
567 Fold {
568 from: "cmp_set_a_8",
569 into: "cmp_set_a_rm_8",
570 load: "mov_rm_8",
571 swapped: Some("cmp_set_b_rm_8"),
572 },
573 Fold {
574 from: "cmp_set_a_16",
575 into: "cmp_set_a_rm_16",
576 load: "mov_rm_16",
577 swapped: Some("cmp_set_b_rm_16"),
578 },
579 Fold {
580 from: "cmp_set_a_32",
581 into: "cmp_set_a_rm_32",
582 load: "mov_rm_32",
583 swapped: Some("cmp_set_b_rm_32"),
584 },
585 Fold {
586 from: "cmp_set_a_64",
587 into: "cmp_set_a_rm_64",
588 load: "mov_rm_64",
589 swapped: Some("cmp_set_b_rm_64"),
590 },
591 Fold {
592 from: "cmp_set_ae_8",
593 into: "cmp_set_ae_rm_8",
594 load: "mov_rm_8",
595 swapped: Some("cmp_set_be_rm_8"),
596 },
597 Fold {
598 from: "cmp_set_ae_16",
599 into: "cmp_set_ae_rm_16",
600 load: "mov_rm_16",
601 swapped: Some("cmp_set_be_rm_16"),
602 },
603 Fold {
604 from: "cmp_set_ae_32",
605 into: "cmp_set_ae_rm_32",
606 load: "mov_rm_32",
607 swapped: Some("cmp_set_be_rm_32"),
608 },
609 Fold {
610 from: "cmp_set_ae_64",
611 into: "cmp_set_ae_rm_64",
612 load: "mov_rm_64",
613 swapped: Some("cmp_set_be_rm_64"),
614 },
615 Fold { from: "cmp_set_e_ri_8", into: "cmp_set_e_mi_8", load: "mov_rm_8", swapped: None },
616 Fold { from: "cmp_set_e_ri_16", into: "cmp_set_e_mi_16", load: "mov_rm_16", swapped: None },
617 Fold { from: "cmp_set_e_ri_32", into: "cmp_set_e_mi_32", load: "mov_rm_32", swapped: None },
618 Fold { from: "cmp_set_e_ri_64", into: "cmp_set_e_mi_64", load: "mov_rm_64", swapped: None },
619 Fold { from: "cmp_set_ne_ri_8", into: "cmp_set_ne_mi_8", load: "mov_rm_8", swapped: None },
620 Fold { from: "cmp_set_ne_ri_16", into: "cmp_set_ne_mi_16", load: "mov_rm_16", swapped: None },
621 Fold { from: "cmp_set_ne_ri_32", into: "cmp_set_ne_mi_32", load: "mov_rm_32", swapped: None },
622 Fold { from: "cmp_set_ne_ri_64", into: "cmp_set_ne_mi_64", load: "mov_rm_64", swapped: None },
623 Fold { from: "cmp_set_l_ri_8", into: "cmp_set_l_mi_8", load: "mov_rm_8", swapped: None },
624 Fold { from: "cmp_set_l_ri_16", into: "cmp_set_l_mi_16", load: "mov_rm_16", swapped: None },
625 Fold { from: "cmp_set_l_ri_32", into: "cmp_set_l_mi_32", load: "mov_rm_32", swapped: None },
626 Fold { from: "cmp_set_l_ri_64", into: "cmp_set_l_mi_64", load: "mov_rm_64", swapped: None },
627 Fold { from: "cmp_set_le_ri_8", into: "cmp_set_le_mi_8", load: "mov_rm_8", swapped: None },
628 Fold { from: "cmp_set_le_ri_16", into: "cmp_set_le_mi_16", load: "mov_rm_16", swapped: None },
629 Fold { from: "cmp_set_le_ri_32", into: "cmp_set_le_mi_32", load: "mov_rm_32", swapped: None },
630 Fold { from: "cmp_set_le_ri_64", into: "cmp_set_le_mi_64", load: "mov_rm_64", swapped: None },
631 Fold { from: "cmp_set_g_ri_8", into: "cmp_set_g_mi_8", load: "mov_rm_8", swapped: None },
632 Fold { from: "cmp_set_g_ri_16", into: "cmp_set_g_mi_16", load: "mov_rm_16", swapped: None },
633 Fold { from: "cmp_set_g_ri_32", into: "cmp_set_g_mi_32", load: "mov_rm_32", swapped: None },
634 Fold { from: "cmp_set_g_ri_64", into: "cmp_set_g_mi_64", load: "mov_rm_64", swapped: None },
635 Fold { from: "cmp_set_ge_ri_8", into: "cmp_set_ge_mi_8", load: "mov_rm_8", swapped: None },
636 Fold { from: "cmp_set_ge_ri_16", into: "cmp_set_ge_mi_16", load: "mov_rm_16", swapped: None },
637 Fold { from: "cmp_set_ge_ri_32", into: "cmp_set_ge_mi_32", load: "mov_rm_32", swapped: None },
638 Fold { from: "cmp_set_ge_ri_64", into: "cmp_set_ge_mi_64", load: "mov_rm_64", swapped: None },
639 Fold { from: "cmp_set_b_ri_8", into: "cmp_set_b_mi_8", load: "mov_rm_8", swapped: None },
640 Fold { from: "cmp_set_b_ri_16", into: "cmp_set_b_mi_16", load: "mov_rm_16", swapped: None },
641 Fold { from: "cmp_set_b_ri_32", into: "cmp_set_b_mi_32", load: "mov_rm_32", swapped: None },
642 Fold { from: "cmp_set_b_ri_64", into: "cmp_set_b_mi_64", load: "mov_rm_64", swapped: None },
643 Fold { from: "cmp_set_be_ri_8", into: "cmp_set_be_mi_8", load: "mov_rm_8", swapped: None },
644 Fold { from: "cmp_set_be_ri_16", into: "cmp_set_be_mi_16", load: "mov_rm_16", swapped: None },
645 Fold { from: "cmp_set_be_ri_32", into: "cmp_set_be_mi_32", load: "mov_rm_32", swapped: None },
646 Fold { from: "cmp_set_be_ri_64", into: "cmp_set_be_mi_64", load: "mov_rm_64", swapped: None },
647 Fold { from: "cmp_set_a_ri_8", into: "cmp_set_a_mi_8", load: "mov_rm_8", swapped: None },
648 Fold { from: "cmp_set_a_ri_16", into: "cmp_set_a_mi_16", load: "mov_rm_16", swapped: None },
649 Fold { from: "cmp_set_a_ri_32", into: "cmp_set_a_mi_32", load: "mov_rm_32", swapped: None },
650 Fold { from: "cmp_set_a_ri_64", into: "cmp_set_a_mi_64", load: "mov_rm_64", swapped: None },
651 Fold { from: "cmp_set_ae_ri_8", into: "cmp_set_ae_mi_8", load: "mov_rm_8", swapped: None },
652 Fold { from: "cmp_set_ae_ri_16", into: "cmp_set_ae_mi_16", load: "mov_rm_16", swapped: None },
653 Fold { from: "cmp_set_ae_ri_32", into: "cmp_set_ae_mi_32", load: "mov_rm_32", swapped: None },
654 Fold { from: "cmp_set_ae_ri_64", into: "cmp_set_ae_mi_64", load: "mov_rm_64", swapped: None },
655];
656
657#[derive(Debug, Clone, Copy, PartialEq, Eq)]
664pub struct Update {
665 pub from: &'static str,
667 pub into: &'static str,
669 pub load: &'static str,
671 pub store: &'static str,
673 pub commutes: bool,
675}
676
677pub static UPDATES: &[Update] = &[
688 Update {
689 from: "add_rr_8",
690 into: "add_mr_8",
691 load: "mov_rm_8",
692 store: "mov_mr_8",
693 commutes: true,
694 },
695 Update {
696 from: "add_rr_16",
697 into: "add_mr_16",
698 load: "mov_rm_16",
699 store: "mov_mr_16",
700 commutes: true,
701 },
702 Update {
703 from: "add_rr_32",
704 into: "add_mr_32",
705 load: "mov_rm_32",
706 store: "mov_mr_32",
707 commutes: true,
708 },
709 Update {
710 from: "add_rr_64",
711 into: "add_mr_64",
712 load: "mov_rm_64",
713 store: "mov_mr_64",
714 commutes: true,
715 },
716 Update {
717 from: "sub_rr_8",
718 into: "sub_mr_8",
719 load: "mov_rm_8",
720 store: "mov_mr_8",
721 commutes: false,
722 },
723 Update {
724 from: "sub_rr_16",
725 into: "sub_mr_16",
726 load: "mov_rm_16",
727 store: "mov_mr_16",
728 commutes: false,
729 },
730 Update {
731 from: "sub_rr_32",
732 into: "sub_mr_32",
733 load: "mov_rm_32",
734 store: "mov_mr_32",
735 commutes: false,
736 },
737 Update {
738 from: "sub_rr_64",
739 into: "sub_mr_64",
740 load: "mov_rm_64",
741 store: "mov_mr_64",
742 commutes: false,
743 },
744 Update {
745 from: "and_rr_8",
746 into: "and_mr_8",
747 load: "mov_rm_8",
748 store: "mov_mr_8",
749 commutes: true,
750 },
751 Update {
752 from: "and_rr_16",
753 into: "and_mr_16",
754 load: "mov_rm_16",
755 store: "mov_mr_16",
756 commutes: true,
757 },
758 Update {
759 from: "and_rr_32",
760 into: "and_mr_32",
761 load: "mov_rm_32",
762 store: "mov_mr_32",
763 commutes: true,
764 },
765 Update {
766 from: "and_rr_64",
767 into: "and_mr_64",
768 load: "mov_rm_64",
769 store: "mov_mr_64",
770 commutes: true,
771 },
772 Update {
773 from: "or_rr_8",
774 into: "or_mr_8",
775 load: "mov_rm_8",
776 store: "mov_mr_8",
777 commutes: true,
778 },
779 Update {
780 from: "or_rr_16",
781 into: "or_mr_16",
782 load: "mov_rm_16",
783 store: "mov_mr_16",
784 commutes: true,
785 },
786 Update {
787 from: "or_rr_32",
788 into: "or_mr_32",
789 load: "mov_rm_32",
790 store: "mov_mr_32",
791 commutes: true,
792 },
793 Update {
794 from: "or_rr_64",
795 into: "or_mr_64",
796 load: "mov_rm_64",
797 store: "mov_mr_64",
798 commutes: true,
799 },
800 Update {
801 from: "xor_rr_8",
802 into: "xor_mr_8",
803 load: "mov_rm_8",
804 store: "mov_mr_8",
805 commutes: true,
806 },
807 Update {
808 from: "xor_rr_16",
809 into: "xor_mr_16",
810 load: "mov_rm_16",
811 store: "mov_mr_16",
812 commutes: true,
813 },
814 Update {
815 from: "xor_rr_32",
816 into: "xor_mr_32",
817 load: "mov_rm_32",
818 store: "mov_mr_32",
819 commutes: true,
820 },
821 Update {
822 from: "xor_rr_64",
823 into: "xor_mr_64",
824 load: "mov_rm_64",
825 store: "mov_mr_64",
826 commutes: true,
827 },
828];
829
830#[derive(Debug, Clone, Copy, PartialEq, Eq)]
839pub struct Bump {
840 pub from: &'static str,
842 pub into: &'static str,
844 pub load: &'static str,
846 pub store: &'static str,
848}
849
850pub static BUMPS: &[Bump] = &[
865 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
866 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
867 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
868 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
869 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
870 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
871 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
872 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
873 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
874 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
875 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
876 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
877 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
878 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
879 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
880 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
881 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
882 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
883 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
884 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
885];
886
887#[derive(Debug, Clone, Copy)]
892struct Waiting {
893 inst: Inst,
895 reg: Reg,
897 load: &'static str,
899 at: usize,
901}
902
903pub fn loads(
914 func: &mut Func,
915 machine: &MachineInsts,
916 names: &mut Interner,
917 pending: &mut Pending<'_>,
918) -> usize {
919 let mut reads = Reads::of(func);
920 let mut done = 0;
921 for block in func.blocks().collect::<Vec<_>>() {
922 let mut waiting: Option<Waiting> = None;
923 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
924 let name = names.resolve(func[inst].opcode.name()).to_owned();
925 let bare = machine.bare(&name).to_owned();
926 let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
932 if let Some(carried) = waiting {
933 if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
934 let mut set = Changes::new();
935 set.rewrite(inst, plan);
936 set.remove(carried.inst);
937 if set.commit(func, &mut reads, names, machine).is_ok() {
938 pending.moved(carried.inst, &[inst]);
939 waiting = None;
940 done += 1;
941 }
942 }
943 }
944 if barrier {
945 waiting = None;
946 }
947 if let Some(carried) = waiting {
948 if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
949 waiting = None;
950 }
951 }
952 if insisted(func, inst) {
958 continue;
959 }
960 if let Some(load) = FOLDS.iter().find(|fold| fold.load == bare).map(|fold| fold.load) {
961 let operands = &func[func[inst].operands];
962 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
963 waiting = Some(Waiting { inst, reg: first.reg, load, at });
964 }
965 }
966 }
967 }
968 done
969}
970
971#[derive(Debug, Clone, Copy)]
973struct Run {
974 load: Inst,
976 alu: Inst,
978 store: Inst,
980 update: &'static Update,
982 kept: Operand,
984}
985
986#[derive(Debug, Clone, Copy)]
992struct Bumped {
993 load: Inst,
995 alu: Inst,
997 store: Inst,
999 bump: &'static Bump,
1001 imm: i64,
1003}
1004
1005pub fn stores(
1022 func: &mut Func,
1023 machine: &MachineInsts,
1024 flags: &FlagInsts,
1025 names: &mut Interner,
1026 pending: &mut Pending<'_>,
1027) -> usize {
1028 let mut reads = Reads::of(func);
1029 let mut done = 0;
1030 for block in func.blocks().collect::<Vec<_>>() {
1031 let insts: Vec<Inst> = func.insts(block).collect();
1032 for at in 0..insts.len() {
1033 let found = match run(func, &reads, machine, flags, names, &insts, at) {
1034 Some(found) => Some((
1035 found.load,
1036 found.alu,
1037 found.store,
1038 updated(func, machine, names, &found),
1039 )),
1040 None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1041 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1042 }),
1043 };
1044 let Some((load, alu, store, plan)) = found else { continue };
1045 if !pending.alike(load, store) {
1046 continue;
1047 }
1048 let mut set = Changes::new();
1049 set.rewrite(store, plan);
1050 set.remove(alu);
1051 set.remove(load);
1052 if set.commit(func, &mut reads, names, machine).is_ok() {
1053 pending.moved(load, &[]);
1054 done += 1;
1055 }
1056 }
1057 }
1058 done
1059}
1060
1061fn run(
1073 func: &Func,
1074 reads: &Reads,
1075 machine: &MachineInsts,
1076 flags: &FlagInsts,
1077 names: &Interner,
1078 insts: &[Inst],
1079 at: usize,
1080) -> Option<Run> {
1081 let store = insts[at];
1082 if insisted(func, store) {
1083 return None;
1084 }
1085 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1086 let value = *func[func[store].operands].first()?;
1087 if value.role.is_def() || reads.count(value.reg) != 1 {
1088 return None;
1089 }
1090 let earliest = at.saturating_sub(WINDOW);
1093 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1094 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1095 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1096 if !quiet(func, flags, names, insts, (alu, at)) {
1097 return None;
1098 }
1099 let operands = func[func[insts[alu]].operands].to_vec();
1100 let [_, first, second] = operands[..] else { return None };
1101 let both = [(first, second), (second, first)];
1106 let tried = if update.commutes { &both[..] } else { &both[..1] };
1107 for &(source, kept) in tried {
1108 if reads.count(source.reg) != 1 {
1109 continue;
1110 }
1111 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1112 continue;
1113 };
1114 let load = insts[from];
1115 if insisted(func, load) {
1116 continue;
1117 }
1118 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1119 continue;
1120 }
1121 if !same_place(func, load, store) {
1122 continue;
1123 }
1124 let mut wanted: Vec<Reg> =
1129 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1130 wanted.push(kept.reg);
1131 if !clear(func, machine, names, insts, (from, at), &wanted) {
1132 continue;
1133 }
1134 return Some(Run { load, alu: insts[alu], store, update, kept });
1135 }
1136 None
1137}
1138
1139fn constant(
1153 func: &Func,
1154 reads: &Reads,
1155 machine: &MachineInsts,
1156 flags: &FlagInsts,
1157 names: &Interner,
1158 insts: &[Inst],
1159 at: usize,
1160) -> Option<Bumped> {
1161 let store = insts[at];
1162 if insisted(func, store) {
1163 return None;
1164 }
1165 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1166 let value = *func[func[store].operands].first()?;
1167 if value.role.is_def() || reads.count(value.reg) != 1 {
1168 return None;
1169 }
1170 let mem = func[func[store].mem?];
1171 if mem.base == Some(0) || mem.index == Some(0) {
1172 return None;
1173 }
1174 let earliest = at.saturating_sub(WINDOW);
1175 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1176 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1177 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1178 if !quiet(func, flags, names, insts, (alu, at)) {
1179 return None;
1180 }
1181 let operands = func[func[insts[alu]].operands].to_vec();
1182 let [_, source] = operands[..] else { return None };
1183 let imm = func[func[insts[alu]].imm?].0;
1184 if reads.count(source.reg) != 1 {
1185 return None;
1186 }
1187 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1188 let load = insts[from];
1189 if insisted(func, load) {
1190 return None;
1191 }
1192 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1193 return None;
1194 }
1195 if !same_place(func, load, store) {
1196 return None;
1197 }
1198 let wanted: Vec<Reg> =
1201 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1202 if !clear(func, machine, names, insts, (from, at), &wanted) {
1203 return None;
1204 }
1205 Some(Bumped { load, alu: insts[alu], store, bump, imm })
1206}
1207
1208fn insisted(func: &Func, inst: Inst) -> bool {
1213 func[inst].flags.contains(Flags::VOLATILE)
1214}
1215
1216fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1218 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1219}
1220
1221fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1228 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1229 let (here, there) = (func[here], func[there]);
1230 if func[one].symbol != func[other].symbol {
1231 return false;
1232 }
1233 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1234 if bare(here) != bare(there) {
1235 return false;
1236 }
1237 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1238 (None, None) => true,
1239 (Some(left), Some(right)) => {
1240 func[func[one].operands][usize::from(left)].reg
1241 == func[func[other].operands][usize::from(right)].reg
1242 }
1243 _ => false,
1244 };
1245 same(here.base, there.base) && same(here.index, there.index)
1246}
1247
1248fn clear(
1255 func: &Func,
1256 machine: &MachineInsts,
1257 names: &Interner,
1258 insts: &[Inst],
1259 span: (usize, usize),
1260 wanted: &[Reg],
1261) -> bool {
1262 let (from, to) = span;
1263 insts[from + 1..to].iter().all(|&inst| {
1264 let name = names.resolve(func[inst].opcode.name());
1265 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1266 return false;
1267 }
1268 !func[func[inst].operands]
1269 .iter()
1270 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1271 })
1272}
1273
1274fn quiet(
1291 func: &Func,
1292 flags: &FlagInsts,
1293 names: &Interner,
1294 insts: &[Inst],
1295 span: (usize, usize),
1296) -> bool {
1297 let (alu, to) = span;
1298 insts[alu + 1..to].iter().all(|&inst| {
1299 let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1300 return false;
1301 };
1302 flags.reads(name).is_none() && !(flags.writes)(name)
1303 })
1304}
1305
1306fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1312 let operands = func[func[run.store].operands].to_vec();
1313 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1314 Plan {
1315 opcode: Opcode::new(into),
1316 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1317 imm: None,
1318 amode: func[run.store].mem.map(|mem| func[mem]),
1319 symbol: func[run.store].symbol,
1320 }
1321}
1322
1323fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1331 let operands = func[func[run.store].operands][1..].to_vec();
1332 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1333 let back = |at: Option<u8>| at.map(|at| at - 1);
1334 Plan {
1335 opcode: Opcode::new(into),
1336 operands,
1337 imm: Some(run.imm),
1338 amode: func[run.store].mem.map(|mem| {
1339 let mem = func[mem];
1340 Amode { base: back(mem.base), index: back(mem.index), ..mem }
1341 }),
1342 symbol: func[run.store].symbol,
1343 }
1344}
1345
1346fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1352 let written: Vec<Reg> = func[func[inst].operands]
1353 .iter()
1354 .filter(|operand| operand.role.is_def())
1355 .map(|operand| operand.reg)
1356 .collect();
1357 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1358}
1359
1360fn joined(
1365 func: &Func,
1366 reads: &Reads,
1367 carried: Waiting,
1368 machine: &MachineInsts,
1369 names: &mut Interner,
1370 inst: Inst,
1371 bare: &str,
1372) -> Option<Plan> {
1373 let fold = FOLDS.iter().find(|fold| fold.from == bare)?;
1374 if carried.load != fold.load || reads.count(carried.reg) != 1 {
1375 return None;
1376 }
1377 let operands = func[func[inst].operands].to_vec();
1378 let (front, into) = match operands[..] {
1389 [answer, first, second] => {
1390 let (kept, into) = if second.reg == carried.reg {
1391 (first, fold.into)
1392 } else if first.reg == carried.reg {
1393 (second, fold.swapped?)
1394 } else {
1395 return None;
1396 };
1397 (vec![answer, kept], into)
1398 }
1399 [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1400 _ => return None,
1401 };
1402 let load = carried.inst;
1403 let address = func[func[load].operands][1..].to_vec();
1404 let mut amode = func[func[load].mem?];
1405 let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1409 amode.base = amode.base.map(|at| at + along);
1410 amode.index = amode.index.map(|at| at + along);
1411 let into = names.intern(&format!("{}{}", machine.prefix, into));
1412 Some(Plan {
1413 opcode: Opcode::new(into),
1414 operands: front.into_iter().chain(address).collect(),
1415 imm: func[inst].imm.map(|at| func[at].0),
1416 amode: Some(amode),
1417 symbol: func[load].symbol,
1418 })
1419}
1420
1421#[cfg(test)]
1422mod tests {
1423 use rucc_mir::{self as mir, Constraint, Mem, Operand};
1424 use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1425
1426 use super::*;
1427
1428 fn empty() -> (Interner, Func, mir::Block) {
1430 let mut names = Interner::new();
1431 let mut func = Func::new(names.intern("f"));
1432 let block = func.create_block();
1433 (names, func, block)
1434 }
1435
1436 fn op(names: &mut Interner, name: &str) -> Opcode {
1438 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1439 }
1440
1441 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1443 let into = func.new_vreg(GPR);
1444 let mov = op(names, "mov_rm_64");
1445 func.build(block, mov)
1446 .def(into, GPR)
1447 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1448 .finish();
1449 into
1450 }
1451
1452 fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1454 let into = func.new_vreg(GPR);
1455 let mov = op(names, "mov_rm_64");
1456 func.build(block, mov)
1457 .def(into, GPR)
1458 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1459 .flags(Flags::VOLATILE)
1460 .finish();
1461 into
1462 }
1463
1464 fn alu(
1466 func: &mut Func,
1467 names: &mut Interner,
1468 block: mir::Block,
1469 name: &str,
1470 first: Reg,
1471 second: Reg,
1472 ) -> Reg {
1473 let answer = func.new_vreg(GPR);
1474 let opcode = op(names, name);
1475 func.build(block, opcode)
1476 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1477 .uses(first, GPR)
1478 .uses(second, GPR)
1479 .finish();
1480 answer
1481 }
1482
1483 fn compare(
1486 func: &mut Func,
1487 names: &mut Interner,
1488 block: mir::Block,
1489 name: &str,
1490 first: Reg,
1491 second: Reg,
1492 ) -> Reg {
1493 let byte = func.new_vreg(GPR);
1494 let opcode = op(names, name);
1495 func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1496 byte
1497 }
1498
1499 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1501 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1502 }
1503
1504 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1506 let mut addresses = Vec::new();
1507 let mut arguments = Vec::new();
1508 let mut dynamic = Vec::new();
1509 let mut pending =
1510 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1511 loads(func, &MACHINE, names, &mut pending)
1512 }
1513
1514 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1516 let mov = op(names, "mov_mr_64");
1517 func.build(block, mov)
1518 .uses(value, GPR)
1519 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1520 .finish();
1521 }
1522
1523 fn insisted_store(
1525 func: &mut Func,
1526 names: &mut Interner,
1527 block: mir::Block,
1528 base: Reg,
1529 value: Reg,
1530 ) {
1531 let mov = op(names, "mov_mr_64");
1532 func.build(block, mov)
1533 .uses(value, GPR)
1534 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1535 .flags(Flags::VOLATILE)
1536 .finish();
1537 }
1538
1539 fn update(func: &mut Func, names: &mut Interner) -> usize {
1541 let mut addresses = Vec::new();
1542 let mut arguments = Vec::new();
1543 let mut dynamic = Vec::new();
1544 let mut pending =
1545 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1546 stores(func, &MACHINE, &FLAGS, names, &mut pending)
1547 }
1548
1549 #[test]
1551 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1552 let (mut names, mut func, block) = empty();
1553 let base = func.new_vreg(GPR);
1554 let other = func.new_vreg(GPR);
1555 let word = load(&mut func, &mut names, block, base);
1556 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1557 store(&mut func, &mut names, block, base, sum);
1558
1559 assert_eq!(update(&mut func, &mut names), 1);
1560 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1561 let inst = func.insts(block).next().expect("the addition");
1562 let mem = func[inst].mem.expect("it writes memory");
1563 assert_eq!(func[mem].disp, 16, "the address came from the store");
1564 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1565 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1566 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1567 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1568 }
1569
1570 #[test]
1574 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1575 let (mut names, mut func, block) = empty();
1576 let base = func.new_vreg(GPR);
1577 let other = func.new_vreg(GPR);
1578 let word = load(&mut func, &mut names, block, base);
1579 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1580 store(&mut func, &mut names, block, base, sum);
1581
1582 assert_eq!(update(&mut func, &mut names), 1);
1583 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1584 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1585 }
1586
1587 #[test]
1590 fn a_subtraction_taking_a_register_away_from_memory_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 left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1596 store(&mut func, &mut names, block, base, left);
1597
1598 assert_eq!(update(&mut func, &mut names), 1);
1599 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1600 }
1601
1602 #[test]
1605 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1606 let (mut names, mut func, block) = empty();
1607 let base = func.new_vreg(GPR);
1608 let other = func.new_vreg(GPR);
1609 let word = load(&mut func, &mut names, block, base);
1610 let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1611 store(&mut func, &mut names, block, base, left);
1612
1613 assert_eq!(update(&mut func, &mut names), 0);
1614 assert_eq!(
1615 shape(&func, &names, block),
1616 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1617 );
1618 }
1619
1620 #[test]
1623 fn a_store_to_another_address_stays_three_instructions() {
1624 let (mut names, mut func, block) = empty();
1625 let base = func.new_vreg(GPR);
1626 let elsewhere = func.new_vreg(GPR);
1627 let other = func.new_vreg(GPR);
1628 let word = load(&mut func, &mut names, block, base);
1629 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1630 store(&mut func, &mut names, block, elsewhere, sum);
1631
1632 assert_eq!(update(&mut func, &mut names), 0);
1633 }
1634
1635 #[test]
1638 fn a_store_at_another_displacement_stays_three_instructions() {
1639 let (mut names, mut func, block) = empty();
1640 let base = func.new_vreg(GPR);
1641 let other = func.new_vreg(GPR);
1642 let word = load(&mut func, &mut names, block, base);
1643 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1644 let mov = op(&mut names, "mov_mr_64");
1645 func.build(block, mov)
1646 .uses(sum, GPR)
1647 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1648 .finish();
1649
1650 assert_eq!(update(&mut func, &mut names), 0);
1651 }
1652
1653 #[test]
1656 fn a_word_two_instructions_read_stays_three_instructions() {
1657 let (mut names, mut func, block) = empty();
1658 let base = func.new_vreg(GPR);
1659 let other = func.new_vreg(GPR);
1660 let word = load(&mut func, &mut names, block, base);
1661 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1662 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1663 store(&mut func, &mut names, block, base, sum);
1664
1665 assert_eq!(update(&mut func, &mut names), 0);
1666 }
1667
1668 #[test]
1671 fn an_answer_something_else_reads_stays_three_instructions() {
1672 let (mut names, mut func, block) = empty();
1673 let base = func.new_vreg(GPR);
1674 let other = func.new_vreg(GPR);
1675 let word = load(&mut func, &mut names, block, base);
1676 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1677 store(&mut func, &mut names, block, base, sum);
1678 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1679
1680 assert_eq!(update(&mut func, &mut names), 0);
1681 }
1682
1683 #[test]
1686 fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1687 let (mut names, mut func, block) = empty();
1688 let base = func.new_vreg(GPR);
1689 let other = func.new_vreg(GPR);
1690 let word = load(&mut func, &mut names, block, base);
1691 load(&mut func, &mut names, block, other);
1692 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1693 store(&mut func, &mut names, block, base, sum);
1694
1695 assert_eq!(update(&mut func, &mut names), 0);
1696 }
1697
1698 #[test]
1701 fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1702 let (mut names, mut func, block) = empty();
1703 let base = Reg::physical(rucc_target::x86_64::RSP);
1704 let other = func.new_vreg(GPR);
1705 let word = load(&mut func, &mut names, block, base);
1706 let sub = op(&mut names, "sub_ri_64");
1707 func.build(block, sub)
1708 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1709 .uses(base, GPR)
1710 .imm(32)
1711 .finish();
1712 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1713 store(&mut func, &mut names, block, base, sum);
1714
1715 assert_eq!(update(&mut func, &mut names), 0);
1716 }
1717
1718 #[test]
1722 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1723 let (mut names, mut func, block) = empty();
1724 let base = Reg::physical(rucc_target::x86_64::RSP);
1725 let other = func.new_vreg(GPR);
1726 let mov = op(&mut names, "mov_rm_64");
1727 let word = func.new_vreg(GPR);
1728 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1729 let read = func.insts(block).next().expect("the load");
1730 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1731 let put = op(&mut names, "mov_mr_64");
1732 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1733 let written = func.insts(block).nth(2).expect("the store");
1734
1735 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1736 let mut arguments = Vec::new();
1737 let mut dynamic = Vec::new();
1738 let mut pending =
1739 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1740 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1741 }
1742
1743 #[test]
1747 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1748 let (mut names, mut func, block) = empty();
1749 let base = Reg::physical(rucc_target::x86_64::RSP);
1750 let other = func.new_vreg(GPR);
1751 let mov = op(&mut names, "mov_rm_64");
1752 let word = func.new_vreg(GPR);
1753 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1754 let read = func.insts(block).next().expect("the load");
1755 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1756 let put = op(&mut names, "mov_mr_64");
1757 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1758 let written = func.insts(block).nth(2).expect("the store");
1759
1760 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1761 let mut arguments = Vec::new();
1762 let mut dynamic = Vec::new();
1763 let mut pending =
1764 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1765 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
1766
1767 let inst = func.insts(block).next().expect("the addition");
1768 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1769 }
1770
1771 #[test]
1773 fn a_run_whose_widths_disagree_stays_three_instructions() {
1774 let (mut names, mut func, block) = empty();
1775 let base = func.new_vreg(GPR);
1776 let other = func.new_vreg(GPR);
1777 let into = func.new_vreg(GPR);
1778 let narrow = op(&mut names, "mov_rm_32");
1779 func.build(block, narrow)
1780 .def(into, GPR)
1781 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1782 .finish();
1783 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1784 store(&mut func, &mut names, block, base, sum);
1785
1786 assert_eq!(update(&mut func, &mut names), 0);
1787 }
1788
1789 #[test]
1793 fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1794 let (mut names, mut func, block) = empty();
1795 let base = func.new_vreg(GPR);
1796 let other = func.new_vreg(GPR);
1797 let carry = func.new_vreg(GPR);
1798 let word = load(&mut func, &mut names, block, base);
1799 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1800 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1801 store(&mut func, &mut names, block, base, sum);
1802
1803 assert_eq!(update(&mut func, &mut names), 0);
1804 }
1805
1806 #[test]
1811 fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1812 let (mut names, mut func, block) = empty();
1813 let base = func.new_vreg(GPR);
1814 let other = func.new_vreg(GPR);
1815 let left = func.new_vreg(GPR);
1816 let right = func.new_vreg(GPR);
1817 let word = load(&mut func, &mut names, block, base);
1818 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1819 alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1820 store(&mut func, &mut names, block, base, sum);
1821
1822 assert_eq!(update(&mut func, &mut names), 0);
1823 }
1824
1825 #[test]
1828 fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1829 let (mut names, mut func, block) = empty();
1830 let base = func.new_vreg(GPR);
1831 let other = func.new_vreg(GPR);
1832 let from = func.new_vreg(GPR);
1833 let into = func.new_vreg(GPR);
1834 let word = load(&mut func, &mut names, block, base);
1835 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1836 let copy = op(&mut names, "mov_rr_64");
1837 func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1838 store(&mut func, &mut names, block, base, sum);
1839
1840 assert_eq!(update(&mut func, &mut names), 1);
1841 assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1842 }
1843
1844 #[test]
1846 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1847 for update in UPDATES {
1848 for name in [update.from, update.into, update.load, update.store] {
1849 assert!(MACHINE.has(name), "{name} is not an instruction");
1850 }
1851 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1852 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1853 assert_eq!(
1854 width(update.from),
1855 width(update.load),
1856 "{} loads another width",
1857 update.from
1858 );
1859 assert_eq!(
1860 width(update.from),
1861 width(update.store),
1862 "{} stores another width",
1863 update.from
1864 );
1865 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1866 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1867 }
1868 }
1869
1870 #[test]
1873 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1874 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1875 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1876 assert_eq!(commuting, 16, "everything but the four subtractions");
1877 }
1878
1879 fn alu_imm(
1881 func: &mut Func,
1882 names: &mut Interner,
1883 block: mir::Block,
1884 name: &str,
1885 source: Reg,
1886 value: i64,
1887 ) -> Reg {
1888 let answer = func.new_vreg(GPR);
1889 let opcode = op(names, name);
1890 func.build(block, opcode)
1891 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1892 .uses(source, GPR)
1893 .imm(value)
1894 .finish();
1895 answer
1896 }
1897
1898 #[test]
1900 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1901 let (mut names, mut func, block) = empty();
1902 let base = func.new_vreg(GPR);
1903 let word = load(&mut func, &mut names, block, base);
1904 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1905 store(&mut func, &mut names, block, base, sum);
1906
1907 assert_eq!(update(&mut func, &mut names), 1);
1908 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1909 let inst = func.insts(block).next().expect("the addition");
1910 let mem = func[inst].mem.expect("it writes memory");
1911 assert_eq!(func[mem].disp, 16, "the address came from the store");
1912 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1913 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1914 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1915 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1916 }
1917
1918 #[test]
1921 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1922 let (mut names, mut func, block) = empty();
1923 let base = func.new_vreg(GPR);
1924 let word = load(&mut func, &mut names, block, base);
1925 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1926 store(&mut func, &mut names, block, base, left);
1927
1928 assert_eq!(update(&mut func, &mut names), 1);
1929 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1930 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1931 }
1932
1933 #[test]
1936 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1937 let (mut names, mut func, block) = empty();
1938 let base = func.new_vreg(GPR);
1939 let word = func.new_vreg(GPR);
1940 let mov = op(&mut names, "mov_rm_8");
1941 func.build(block, mov)
1942 .def(word, GPR)
1943 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1944 .finish();
1945 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1946 let put = op(&mut names, "mov_mr_8");
1947 func.build(block, put)
1948 .uses(sum, GPR)
1949 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1950 .finish();
1951
1952 assert_eq!(update(&mut func, &mut names), 1);
1953 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1954 }
1955
1956 #[test]
1959 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
1960 let (mut names, mut func, block) = empty();
1961 let base = func.new_vreg(GPR);
1962 let other = func.new_vreg(GPR);
1963 let word = load(&mut func, &mut names, block, base);
1964 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1965 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1966 store(&mut func, &mut names, block, base, sum);
1967
1968 assert_eq!(update(&mut func, &mut names), 0);
1969 }
1970
1971 #[test]
1974 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
1975 let (mut names, mut func, block) = empty();
1976 let base = func.new_vreg(GPR);
1977 let elsewhere = func.new_vreg(GPR);
1978 let word = load(&mut func, &mut names, block, base);
1979 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1980 load(&mut func, &mut names, block, elsewhere);
1981 store(&mut func, &mut names, block, base, sum);
1982
1983 assert_eq!(update(&mut func, &mut names), 0);
1984 }
1985
1986 #[test]
1989 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1990 let (mut names, mut func, block) = empty();
1991 let base = Reg::physical(rucc_target::x86_64::RAX);
1992 let word = load(&mut func, &mut names, block, base);
1993 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1994 let mov = op(&mut names, "mov_ri_64");
1995 func.build(block, mov).def(base, GPR).imm(0).finish();
1996 store(&mut func, &mut names, block, base, sum);
1997
1998 assert_eq!(update(&mut func, &mut names), 0);
1999 }
2000
2001 #[test]
2003 fn a_constant_written_to_another_address_stays_three_instructions() {
2004 let (mut names, mut func, block) = empty();
2005 let base = func.new_vreg(GPR);
2006 let elsewhere = func.new_vreg(GPR);
2007 let word = load(&mut func, &mut names, block, base);
2008 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2009 store(&mut func, &mut names, block, elsewhere, sum);
2010
2011 assert_eq!(update(&mut func, &mut names), 0);
2012 }
2013
2014 #[test]
2016 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2017 let (mut names, mut func, block) = empty();
2018 let base = func.new_vreg(GPR);
2019 let into = func.new_vreg(GPR);
2020 let narrow = op(&mut names, "mov_rm_32");
2021 func.build(block, narrow)
2022 .def(into, GPR)
2023 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2024 .finish();
2025 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2026 store(&mut func, &mut names, block, base, sum);
2027
2028 assert_eq!(update(&mut func, &mut names), 0);
2029 }
2030
2031 #[test]
2034 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2035 let (mut names, mut func, block) = empty();
2036 let base = func.new_vreg(GPR);
2037 let word = load(&mut func, &mut names, block, base);
2038 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2039 store(&mut func, &mut names, block, base, product);
2040
2041 assert_eq!(update(&mut func, &mut names), 0);
2042 }
2043
2044 #[test]
2047 fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2048 let (mut names, mut func, block) = empty();
2049 let base = func.new_vreg(GPR);
2050 let carry = func.new_vreg(GPR);
2051 let word = load(&mut func, &mut names, block, base);
2052 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2053 alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2054 store(&mut func, &mut names, block, base, sum);
2055
2056 assert_eq!(update(&mut func, &mut names), 0);
2057 }
2058
2059 #[test]
2062 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2063 let (mut names, mut func, block) = empty();
2064 let base = Reg::physical(rucc_target::x86_64::RSP);
2065 let mov = op(&mut names, "mov_rm_64");
2066 let word = func.new_vreg(GPR);
2067 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2068 let read = func.insts(block).next().expect("the load");
2069 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2070 let put = op(&mut names, "mov_mr_64");
2071 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2072 let written = func.insts(block).nth(2).expect("the store");
2073
2074 let mut addresses = vec![(read, 3usize), (written, 3usize)];
2075 let mut arguments = Vec::new();
2076 let mut dynamic = Vec::new();
2077 let mut pending =
2078 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2079 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2080
2081 let inst = func.insts(block).next().expect("the addition");
2082 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2083 }
2084
2085 #[test]
2088 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
2089 let (mut names, mut func, block) = empty();
2090 let base = Reg::physical(rucc_target::x86_64::RSP);
2091 let mov = op(&mut names, "mov_rm_64");
2092 let word = func.new_vreg(GPR);
2093 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2094 let read = func.insts(block).next().expect("the load");
2095 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2096 let put = op(&mut names, "mov_mr_64");
2097 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2098 let written = func.insts(block).nth(2).expect("the store");
2099
2100 let mut addresses = vec![(read, 3usize), (written, 4usize)];
2101 let mut arguments = Vec::new();
2102 let mut dynamic = Vec::new();
2103 let mut pending =
2104 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2105 assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
2106 }
2107
2108 #[test]
2110 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2111 for bump in BUMPS {
2112 for name in [bump.from, bump.into, bump.load, bump.store] {
2113 assert!(MACHINE.has(name), "{name} is not an instruction");
2114 }
2115 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2116 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2117 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2118 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2119 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2120 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2121 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2122 }
2123 }
2124
2125 #[test]
2128 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2129 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2130 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2131 for bump in BUMPS {
2132 let same = bump.from.replace("_ri_", "_rr_");
2133 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2134 }
2135 }
2136
2137 #[test]
2140 fn nothing_is_both_a_register_run_and_a_constant_run() {
2141 for bump in BUMPS {
2142 assert!(
2143 !UPDATES.iter().any(|update| update.from == bump.from),
2144 "{} starts both kinds of run",
2145 bump.from
2146 );
2147 }
2148 }
2149
2150 #[test]
2152 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2153 let (mut names, mut func, block) = empty();
2154 let base = func.new_vreg(GPR);
2155 let other = func.new_vreg(GPR);
2156 let word = load(&mut func, &mut names, block, base);
2157 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2158
2159 assert_eq!(combine(&mut func, &mut names), 1);
2160 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2161 let inst = func.insts(block).next().expect("the addition");
2162 let mem = func[inst].mem.expect("the addition reads memory now");
2163 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2164 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2165 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2166 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2167 }
2168
2169 #[test]
2172 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2173 let (mut names, mut func, block) = empty();
2174 let base = func.new_vreg(GPR);
2175 let other = func.new_vreg(GPR);
2176 let word = load(&mut func, &mut names, block, base);
2177 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2178
2179 assert_eq!(combine(&mut func, &mut names), 1);
2180 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2181 let inst = func.insts(block).next().expect("the addition");
2182 assert_eq!(func[func[inst].operands][1].reg, other);
2183 }
2184
2185 #[test]
2188 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2189 let (mut names, mut func, block) = empty();
2190 let base = func.new_vreg(GPR);
2191 let other = func.new_vreg(GPR);
2192 let word = load(&mut func, &mut names, block, base);
2193 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2194
2195 assert_eq!(combine(&mut func, &mut names), 0);
2196 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2197 }
2198
2199 #[test]
2201 fn a_load_feeding_the_right_of_a_subtraction_folds() {
2202 let (mut names, mut func, block) = empty();
2203 let base = func.new_vreg(GPR);
2204 let other = func.new_vreg(GPR);
2205 let word = load(&mut func, &mut names, block, base);
2206 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2207
2208 assert_eq!(combine(&mut func, &mut names), 1);
2209 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2210 }
2211
2212 #[test]
2215 fn a_load_two_instructions_read_stays_a_load() {
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", other, word);
2221 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2222
2223 assert_eq!(combine(&mut func, &mut names), 0);
2224 assert_eq!(
2225 shape(&func, &names, block),
2226 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2227 );
2228 }
2229
2230 #[test]
2233 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2234 let (mut names, mut func, block) = empty();
2235 let base = func.new_vreg(GPR);
2236 let other = func.new_vreg(GPR);
2237 let word = load(&mut func, &mut names, block, base);
2238 let store = op(&mut names, "mov_mr_64");
2239 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2240 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2241
2242 assert_eq!(combine(&mut func, &mut names), 0);
2243 assert_eq!(
2244 shape(&func, &names, block),
2245 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2246 );
2247 }
2248
2249 #[test]
2255 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2256 let (mut names, mut func, block) = empty();
2257 let base = func.new_vreg(GPR);
2258 let other = func.new_vreg(GPR);
2259 let word = load(&mut func, &mut names, block, base);
2260 load(&mut func, &mut names, block, other);
2261 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2262
2263 assert_eq!(combine(&mut func, &mut names), 0);
2264 assert_eq!(
2265 shape(&func, &names, block),
2266 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2267 );
2268 }
2269
2270 #[test]
2273 fn the_later_of_two_loads_is_the_one_that_folds() {
2274 let (mut names, mut func, block) = empty();
2275 let base = func.new_vreg(GPR);
2276 let other = func.new_vreg(GPR);
2277 let first = load(&mut func, &mut names, block, base);
2278 let second = load(&mut func, &mut names, block, other);
2279 alu(&mut func, &mut names, block, "add_rr_64", first, second);
2280
2281 assert_eq!(combine(&mut func, &mut names), 1);
2282 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2283 let addition = func.insts(block).nth(1).expect("the addition");
2284 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2285 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2286 }
2287
2288 #[test]
2291 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2292 let (mut names, mut func, block) = empty();
2293 let base = func.new_vreg(GPR);
2294 let other = func.new_vreg(GPR);
2295 let word = load(&mut func, &mut names, block, base);
2296 let call = op(&mut names, "call");
2297 func.build(block, call).finish();
2298 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2299
2300 assert_eq!(combine(&mut func, &mut names), 0);
2301 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2302 }
2303
2304 #[test]
2307 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2308 let (mut names, mut func, block) = empty();
2309 let base = Reg::physical(rucc_target::x86_64::RSP);
2310 let other = func.new_vreg(GPR);
2311 let word = load(&mut func, &mut names, block, base);
2312 let sub = op(&mut names, "sub_ri_64");
2313 func.build(block, sub)
2314 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2315 .uses(base, GPR)
2316 .imm(32)
2317 .finish();
2318 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2319
2320 assert_eq!(combine(&mut func, &mut names), 0);
2321 }
2322
2323 #[test]
2326 fn a_load_of_the_wrong_width_stays_a_load() {
2327 let (mut names, mut func, block) = empty();
2328 let base = func.new_vreg(GPR);
2329 let other = func.new_vreg(GPR);
2330 let into = func.new_vreg(GPR);
2331 let narrow = op(&mut names, "mov_rm_32");
2332 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2333 alu(&mut func, &mut names, block, "add_rr_64", other, into);
2334
2335 assert_eq!(combine(&mut func, &mut names), 0);
2336 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2337 }
2338
2339 #[test]
2342 fn a_load_whose_value_an_edge_carries_stays_a_load() {
2343 let (mut names, mut func, block) = empty();
2344 let next = func.create_block();
2345 let base = func.new_vreg(GPR);
2346 let other = func.new_vreg(GPR);
2347 let word = load(&mut func, &mut names, block, base);
2348 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2349 let arrived = func.new_vreg(GPR);
2350 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2351 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2352
2353 assert_eq!(combine(&mut func, &mut names), 0);
2354 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2355 }
2356
2357 #[test]
2359 fn a_reader_in_another_block_stays_where_it_is() {
2360 let (mut names, mut func, block) = empty();
2361 let next = func.create_block();
2362 let base = func.new_vreg(GPR);
2363 let other = func.new_vreg(GPR);
2364 let word = load(&mut func, &mut names, block, base);
2365 alu(&mut func, &mut names, next, "add_rr_64", other, word);
2366
2367 assert_eq!(combine(&mut func, &mut names), 0);
2368 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2369 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2370 }
2371
2372 #[test]
2374 fn a_reader_past_the_window_stays_where_it_is() {
2375 let (mut names, mut func, block) = empty();
2376 let base = func.new_vreg(GPR);
2377 let other = func.new_vreg(GPR);
2378 let word = load(&mut func, &mut names, block, base);
2379 let nop = op(&mut names, "nop");
2380 for _ in 0..WINDOW {
2381 func.build(block, nop).finish();
2382 }
2383 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2384
2385 assert_eq!(combine(&mut func, &mut names), 0);
2386 }
2387
2388 #[test]
2390 fn a_reader_at_the_edge_of_the_window_folds() {
2391 let (mut names, mut func, block) = empty();
2392 let base = func.new_vreg(GPR);
2393 let other = func.new_vreg(GPR);
2394 let word = load(&mut func, &mut names, block, base);
2395 let nop = op(&mut names, "nop");
2396 for _ in 0..WINDOW - 1 {
2397 func.build(block, nop).finish();
2398 }
2399 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2400
2401 assert_eq!(combine(&mut func, &mut names), 1);
2402 }
2403
2404 #[test]
2407 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2408 let (mut names, mut func, block) = empty();
2409 let base = Reg::physical(rucc_target::x86_64::RSP);
2410 let other = func.new_vreg(GPR);
2411 let word = load(&mut func, &mut names, block, base);
2412 let reader = func.insts(block).nth(1);
2413 assert!(reader.is_none(), "the block holds the load alone so far");
2414 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2415 let held = func.insts(block).next().expect("the load");
2416
2417 let mut addresses = vec![(held, 3usize)];
2418 let mut arguments = Vec::new();
2419 let mut dynamic = Vec::new();
2420 let mut pending =
2421 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2422 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2423
2424 let inst = func.insts(block).next().expect("the addition");
2425 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2426 }
2427
2428 #[test]
2432 fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2433 let (mut names, mut func, block) = empty();
2434 let base = func.new_vreg(GPR);
2435 let other = func.new_vreg(GPR);
2436 let word = load(&mut func, &mut names, block, base);
2437 compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2438
2439 assert_eq!(combine(&mut func, &mut names), 1);
2440 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2441 let inst = func.insts(block).next().expect("the comparison");
2442 let mem = func[inst].mem.expect("it reads memory");
2443 assert_eq!(func[mem].disp, 16, "the address came from the load");
2444 assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2445 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2446 assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2447 }
2448
2449 #[test]
2453 fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2454 let (mut names, mut func, block) = empty();
2455 let base = func.new_vreg(GPR);
2456 let other = func.new_vreg(GPR);
2457 let word = load(&mut func, &mut names, block, base);
2458 compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2459
2460 assert_eq!(combine(&mut func, &mut names), 1);
2461 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2462 let inst = func.insts(block).next().expect("the comparison");
2463 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2464 }
2465
2466 #[test]
2470 fn an_equality_folded_on_either_side_is_the_same_comparison() {
2471 for (first, second) in [(true, false), (false, true)] {
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 let left = if first { word } else { other };
2477 let right = if second { word } else { other };
2478 compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2479
2480 assert_eq!(combine(&mut func, &mut names), 1);
2481 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2482 }
2483 }
2484
2485 #[test]
2490 fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2491 let (mut names, mut func, block) = empty();
2492 let base = func.new_vreg(GPR);
2493 let byte = func.new_vreg(GPR);
2494 let word = load(&mut func, &mut names, block, base);
2495 let opcode = op(&mut names, "cmp_set_l_ri_64");
2496 func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2497
2498 assert_eq!(combine(&mut func, &mut names), 1);
2499 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2500 let inst = func.insts(block).next().expect("the comparison");
2501 let mem = func[inst].mem.expect("it reads memory now");
2502 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2503 assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2504 assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2505 assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2506 let imm = func[inst].imm.expect("the constant is still on it");
2507 assert_eq!(func[imm].0, 7, "and is the one that was written");
2508 }
2509
2510 #[test]
2514 fn every_row_of_the_table_is_three_instructions_this_target_has() {
2515 for fold in FOLDS {
2516 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2517 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2518 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2519 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2520 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2521 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2522 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2523 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2524 let Some(swapped) = fold.swapped else { continue };
2525 assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2526 assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2527 assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2528 }
2529 }
2530
2531 #[test]
2535 fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2536 let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2537 assert_eq!(
2538 compares, 80,
2539 "ten conditions at four widths, against a register and a constant"
2540 );
2541 let arithmetic = FOLDS.len() - compares;
2542 assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2543 let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2544 assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2545 }
2546
2547 #[test]
2555 fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2556 let turned = |condition: &str| match condition {
2557 "e" => "e",
2558 "ne" => "ne",
2559 "l" => "g",
2560 "g" => "l",
2561 "le" => "ge",
2562 "ge" => "le",
2563 "b" => "a",
2564 "a" => "b",
2565 "be" => "ae",
2566 "ae" => "be",
2567 other => panic!("{other} is not a condition this machine has"),
2568 };
2569 let compares = FOLDS
2570 .iter()
2571 .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2572 for fold in compares {
2573 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2574 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2575 assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2576 let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2577 assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2578 }
2579 }
2580
2581 #[test]
2586 fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2587 let compares = FOLDS
2588 .iter()
2589 .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2590 let mut rows = 0;
2591 for fold in compares {
2592 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2593 let front = front.strip_suffix("_ri").expect("a name against a constant");
2594 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2595 assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2596 assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2597 assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2598 rows += 1;
2599 }
2600 assert_eq!(rows, 40, "ten conditions at four widths");
2601 }
2602
2603 #[test]
2610 fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2611 let (mut names, mut func, block) = empty();
2612 let base = func.new_vreg(GPR);
2613 let other = func.new_vreg(GPR);
2614 let word = insisted_load(&mut func, &mut names, block, base);
2615 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2616
2617 assert_eq!(combine(&mut func, &mut names), 0);
2618 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2619 }
2620
2621 #[test]
2625 fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2626 let (mut names, mut func, block) = empty();
2627 let base = func.new_vreg(GPR);
2628 let other = func.new_vreg(GPR);
2629 let word = insisted_load(&mut func, &mut names, block, base);
2630 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2631 store(&mut func, &mut names, block, base, sum);
2632
2633 assert_eq!(update(&mut func, &mut names), 0);
2634 }
2635
2636 #[test]
2641 fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2642 let (mut names, mut func, block) = empty();
2643 let base = func.new_vreg(GPR);
2644 let other = func.new_vreg(GPR);
2645 let word = load(&mut func, &mut names, block, base);
2646 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2647 insisted_store(&mut func, &mut names, block, base, sum);
2648
2649 assert_eq!(update(&mut func, &mut names), 0);
2650 }
2651
2652 #[test]
2655 fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2656 let (mut names, mut func, block) = empty();
2657 let base = func.new_vreg(GPR);
2658 let word = insisted_load(&mut func, &mut names, block, base);
2659 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2660 store(&mut func, &mut names, block, base, sum);
2661
2662 assert_eq!(update(&mut func, &mut names), 0);
2663 }
2664
2665 #[test]
2667 fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2668 let (mut names, mut func, block) = empty();
2669 let base = func.new_vreg(GPR);
2670 let word = load(&mut func, &mut names, block, base);
2671 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2672 insisted_store(&mut func, &mut names, block, base, sum);
2673
2674 assert_eq!(update(&mut func, &mut names), 0);
2675 }
2676
2677 #[test]
2680 fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2681 let (mut names, mut func, block) = empty();
2682 let base = func.new_vreg(GPR);
2683 let other = func.new_vreg(GPR);
2684 let word = load(&mut func, &mut names, block, base);
2685 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2686 store(&mut func, &mut names, block, base, sum);
2687
2688 assert_eq!(update(&mut func, &mut names), 1);
2689 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2690 }
2691}