1use rucc_base::Interner;
253use rucc_mir::{Amode, Flags, Func, Inst, Opcode, Operand, Reg};
254use rucc_target::MachineInsts;
255
256use crate::changes::{Changes, Plan, Reads};
257use crate::fold::Pending;
258
259pub const WINDOW: usize = 16;
276
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub struct Fold {
286 pub from: &'static str,
288 pub into: &'static str,
290 pub load: &'static str,
292 pub swapped: Option<&'static str>,
308}
309
310pub static FOLDS: &[Fold] = &[
325 Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", swapped: Some("add_rm_8") },
326 Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", swapped: Some("add_rm_16") },
327 Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", swapped: Some("add_rm_32") },
328 Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", swapped: Some("add_rm_64") },
329 Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", swapped: None },
330 Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", swapped: None },
331 Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", swapped: None },
332 Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", swapped: None },
333 Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", swapped: Some("and_rm_8") },
334 Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", swapped: Some("and_rm_16") },
335 Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", swapped: Some("and_rm_32") },
336 Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", swapped: Some("and_rm_64") },
337 Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", swapped: Some("or_rm_8") },
338 Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", swapped: Some("or_rm_16") },
339 Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", swapped: Some("or_rm_32") },
340 Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", swapped: Some("or_rm_64") },
341 Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", swapped: Some("xor_rm_8") },
342 Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", swapped: Some("xor_rm_16") },
343 Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", swapped: Some("xor_rm_32") },
344 Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", swapped: Some("xor_rm_64") },
345 Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", swapped: Some("imul_rm_16") },
346 Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", swapped: Some("imul_rm_32") },
347 Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", swapped: Some("imul_rm_64") },
348 Fold {
349 from: "cmp_set_e_8",
350 into: "cmp_set_e_rm_8",
351 load: "mov_rm_8",
352 swapped: Some("cmp_set_e_rm_8"),
353 },
354 Fold {
355 from: "cmp_set_e_16",
356 into: "cmp_set_e_rm_16",
357 load: "mov_rm_16",
358 swapped: Some("cmp_set_e_rm_16"),
359 },
360 Fold {
361 from: "cmp_set_e_32",
362 into: "cmp_set_e_rm_32",
363 load: "mov_rm_32",
364 swapped: Some("cmp_set_e_rm_32"),
365 },
366 Fold {
367 from: "cmp_set_e_64",
368 into: "cmp_set_e_rm_64",
369 load: "mov_rm_64",
370 swapped: Some("cmp_set_e_rm_64"),
371 },
372 Fold {
373 from: "cmp_set_ne_8",
374 into: "cmp_set_ne_rm_8",
375 load: "mov_rm_8",
376 swapped: Some("cmp_set_ne_rm_8"),
377 },
378 Fold {
379 from: "cmp_set_ne_16",
380 into: "cmp_set_ne_rm_16",
381 load: "mov_rm_16",
382 swapped: Some("cmp_set_ne_rm_16"),
383 },
384 Fold {
385 from: "cmp_set_ne_32",
386 into: "cmp_set_ne_rm_32",
387 load: "mov_rm_32",
388 swapped: Some("cmp_set_ne_rm_32"),
389 },
390 Fold {
391 from: "cmp_set_ne_64",
392 into: "cmp_set_ne_rm_64",
393 load: "mov_rm_64",
394 swapped: Some("cmp_set_ne_rm_64"),
395 },
396 Fold {
397 from: "cmp_set_l_8",
398 into: "cmp_set_l_rm_8",
399 load: "mov_rm_8",
400 swapped: Some("cmp_set_g_rm_8"),
401 },
402 Fold {
403 from: "cmp_set_l_16",
404 into: "cmp_set_l_rm_16",
405 load: "mov_rm_16",
406 swapped: Some("cmp_set_g_rm_16"),
407 },
408 Fold {
409 from: "cmp_set_l_32",
410 into: "cmp_set_l_rm_32",
411 load: "mov_rm_32",
412 swapped: Some("cmp_set_g_rm_32"),
413 },
414 Fold {
415 from: "cmp_set_l_64",
416 into: "cmp_set_l_rm_64",
417 load: "mov_rm_64",
418 swapped: Some("cmp_set_g_rm_64"),
419 },
420 Fold {
421 from: "cmp_set_le_8",
422 into: "cmp_set_le_rm_8",
423 load: "mov_rm_8",
424 swapped: Some("cmp_set_ge_rm_8"),
425 },
426 Fold {
427 from: "cmp_set_le_16",
428 into: "cmp_set_le_rm_16",
429 load: "mov_rm_16",
430 swapped: Some("cmp_set_ge_rm_16"),
431 },
432 Fold {
433 from: "cmp_set_le_32",
434 into: "cmp_set_le_rm_32",
435 load: "mov_rm_32",
436 swapped: Some("cmp_set_ge_rm_32"),
437 },
438 Fold {
439 from: "cmp_set_le_64",
440 into: "cmp_set_le_rm_64",
441 load: "mov_rm_64",
442 swapped: Some("cmp_set_ge_rm_64"),
443 },
444 Fold {
445 from: "cmp_set_g_8",
446 into: "cmp_set_g_rm_8",
447 load: "mov_rm_8",
448 swapped: Some("cmp_set_l_rm_8"),
449 },
450 Fold {
451 from: "cmp_set_g_16",
452 into: "cmp_set_g_rm_16",
453 load: "mov_rm_16",
454 swapped: Some("cmp_set_l_rm_16"),
455 },
456 Fold {
457 from: "cmp_set_g_32",
458 into: "cmp_set_g_rm_32",
459 load: "mov_rm_32",
460 swapped: Some("cmp_set_l_rm_32"),
461 },
462 Fold {
463 from: "cmp_set_g_64",
464 into: "cmp_set_g_rm_64",
465 load: "mov_rm_64",
466 swapped: Some("cmp_set_l_rm_64"),
467 },
468 Fold {
469 from: "cmp_set_ge_8",
470 into: "cmp_set_ge_rm_8",
471 load: "mov_rm_8",
472 swapped: Some("cmp_set_le_rm_8"),
473 },
474 Fold {
475 from: "cmp_set_ge_16",
476 into: "cmp_set_ge_rm_16",
477 load: "mov_rm_16",
478 swapped: Some("cmp_set_le_rm_16"),
479 },
480 Fold {
481 from: "cmp_set_ge_32",
482 into: "cmp_set_ge_rm_32",
483 load: "mov_rm_32",
484 swapped: Some("cmp_set_le_rm_32"),
485 },
486 Fold {
487 from: "cmp_set_ge_64",
488 into: "cmp_set_ge_rm_64",
489 load: "mov_rm_64",
490 swapped: Some("cmp_set_le_rm_64"),
491 },
492 Fold {
493 from: "cmp_set_b_8",
494 into: "cmp_set_b_rm_8",
495 load: "mov_rm_8",
496 swapped: Some("cmp_set_a_rm_8"),
497 },
498 Fold {
499 from: "cmp_set_b_16",
500 into: "cmp_set_b_rm_16",
501 load: "mov_rm_16",
502 swapped: Some("cmp_set_a_rm_16"),
503 },
504 Fold {
505 from: "cmp_set_b_32",
506 into: "cmp_set_b_rm_32",
507 load: "mov_rm_32",
508 swapped: Some("cmp_set_a_rm_32"),
509 },
510 Fold {
511 from: "cmp_set_b_64",
512 into: "cmp_set_b_rm_64",
513 load: "mov_rm_64",
514 swapped: Some("cmp_set_a_rm_64"),
515 },
516 Fold {
517 from: "cmp_set_be_8",
518 into: "cmp_set_be_rm_8",
519 load: "mov_rm_8",
520 swapped: Some("cmp_set_ae_rm_8"),
521 },
522 Fold {
523 from: "cmp_set_be_16",
524 into: "cmp_set_be_rm_16",
525 load: "mov_rm_16",
526 swapped: Some("cmp_set_ae_rm_16"),
527 },
528 Fold {
529 from: "cmp_set_be_32",
530 into: "cmp_set_be_rm_32",
531 load: "mov_rm_32",
532 swapped: Some("cmp_set_ae_rm_32"),
533 },
534 Fold {
535 from: "cmp_set_be_64",
536 into: "cmp_set_be_rm_64",
537 load: "mov_rm_64",
538 swapped: Some("cmp_set_ae_rm_64"),
539 },
540 Fold {
541 from: "cmp_set_a_8",
542 into: "cmp_set_a_rm_8",
543 load: "mov_rm_8",
544 swapped: Some("cmp_set_b_rm_8"),
545 },
546 Fold {
547 from: "cmp_set_a_16",
548 into: "cmp_set_a_rm_16",
549 load: "mov_rm_16",
550 swapped: Some("cmp_set_b_rm_16"),
551 },
552 Fold {
553 from: "cmp_set_a_32",
554 into: "cmp_set_a_rm_32",
555 load: "mov_rm_32",
556 swapped: Some("cmp_set_b_rm_32"),
557 },
558 Fold {
559 from: "cmp_set_a_64",
560 into: "cmp_set_a_rm_64",
561 load: "mov_rm_64",
562 swapped: Some("cmp_set_b_rm_64"),
563 },
564 Fold {
565 from: "cmp_set_ae_8",
566 into: "cmp_set_ae_rm_8",
567 load: "mov_rm_8",
568 swapped: Some("cmp_set_be_rm_8"),
569 },
570 Fold {
571 from: "cmp_set_ae_16",
572 into: "cmp_set_ae_rm_16",
573 load: "mov_rm_16",
574 swapped: Some("cmp_set_be_rm_16"),
575 },
576 Fold {
577 from: "cmp_set_ae_32",
578 into: "cmp_set_ae_rm_32",
579 load: "mov_rm_32",
580 swapped: Some("cmp_set_be_rm_32"),
581 },
582 Fold {
583 from: "cmp_set_ae_64",
584 into: "cmp_set_ae_rm_64",
585 load: "mov_rm_64",
586 swapped: Some("cmp_set_be_rm_64"),
587 },
588 Fold { from: "cmp_set_e_ri_8", into: "cmp_set_e_mi_8", load: "mov_rm_8", swapped: None },
589 Fold { from: "cmp_set_e_ri_16", into: "cmp_set_e_mi_16", load: "mov_rm_16", swapped: None },
590 Fold { from: "cmp_set_e_ri_32", into: "cmp_set_e_mi_32", load: "mov_rm_32", swapped: None },
591 Fold { from: "cmp_set_e_ri_64", into: "cmp_set_e_mi_64", load: "mov_rm_64", swapped: None },
592 Fold { from: "cmp_set_ne_ri_8", into: "cmp_set_ne_mi_8", load: "mov_rm_8", swapped: None },
593 Fold { from: "cmp_set_ne_ri_16", into: "cmp_set_ne_mi_16", load: "mov_rm_16", swapped: None },
594 Fold { from: "cmp_set_ne_ri_32", into: "cmp_set_ne_mi_32", load: "mov_rm_32", swapped: None },
595 Fold { from: "cmp_set_ne_ri_64", into: "cmp_set_ne_mi_64", load: "mov_rm_64", swapped: None },
596 Fold { from: "cmp_set_l_ri_8", into: "cmp_set_l_mi_8", load: "mov_rm_8", swapped: None },
597 Fold { from: "cmp_set_l_ri_16", into: "cmp_set_l_mi_16", load: "mov_rm_16", swapped: None },
598 Fold { from: "cmp_set_l_ri_32", into: "cmp_set_l_mi_32", load: "mov_rm_32", swapped: None },
599 Fold { from: "cmp_set_l_ri_64", into: "cmp_set_l_mi_64", load: "mov_rm_64", swapped: None },
600 Fold { from: "cmp_set_le_ri_8", into: "cmp_set_le_mi_8", load: "mov_rm_8", swapped: None },
601 Fold { from: "cmp_set_le_ri_16", into: "cmp_set_le_mi_16", load: "mov_rm_16", swapped: None },
602 Fold { from: "cmp_set_le_ri_32", into: "cmp_set_le_mi_32", load: "mov_rm_32", swapped: None },
603 Fold { from: "cmp_set_le_ri_64", into: "cmp_set_le_mi_64", load: "mov_rm_64", swapped: None },
604 Fold { from: "cmp_set_g_ri_8", into: "cmp_set_g_mi_8", load: "mov_rm_8", swapped: None },
605 Fold { from: "cmp_set_g_ri_16", into: "cmp_set_g_mi_16", load: "mov_rm_16", swapped: None },
606 Fold { from: "cmp_set_g_ri_32", into: "cmp_set_g_mi_32", load: "mov_rm_32", swapped: None },
607 Fold { from: "cmp_set_g_ri_64", into: "cmp_set_g_mi_64", load: "mov_rm_64", swapped: None },
608 Fold { from: "cmp_set_ge_ri_8", into: "cmp_set_ge_mi_8", load: "mov_rm_8", swapped: None },
609 Fold { from: "cmp_set_ge_ri_16", into: "cmp_set_ge_mi_16", load: "mov_rm_16", swapped: None },
610 Fold { from: "cmp_set_ge_ri_32", into: "cmp_set_ge_mi_32", load: "mov_rm_32", swapped: None },
611 Fold { from: "cmp_set_ge_ri_64", into: "cmp_set_ge_mi_64", load: "mov_rm_64", swapped: None },
612 Fold { from: "cmp_set_b_ri_8", into: "cmp_set_b_mi_8", load: "mov_rm_8", swapped: None },
613 Fold { from: "cmp_set_b_ri_16", into: "cmp_set_b_mi_16", load: "mov_rm_16", swapped: None },
614 Fold { from: "cmp_set_b_ri_32", into: "cmp_set_b_mi_32", load: "mov_rm_32", swapped: None },
615 Fold { from: "cmp_set_b_ri_64", into: "cmp_set_b_mi_64", load: "mov_rm_64", swapped: None },
616 Fold { from: "cmp_set_be_ri_8", into: "cmp_set_be_mi_8", load: "mov_rm_8", swapped: None },
617 Fold { from: "cmp_set_be_ri_16", into: "cmp_set_be_mi_16", load: "mov_rm_16", swapped: None },
618 Fold { from: "cmp_set_be_ri_32", into: "cmp_set_be_mi_32", load: "mov_rm_32", swapped: None },
619 Fold { from: "cmp_set_be_ri_64", into: "cmp_set_be_mi_64", load: "mov_rm_64", swapped: None },
620 Fold { from: "cmp_set_a_ri_8", into: "cmp_set_a_mi_8", load: "mov_rm_8", swapped: None },
621 Fold { from: "cmp_set_a_ri_16", into: "cmp_set_a_mi_16", load: "mov_rm_16", swapped: None },
622 Fold { from: "cmp_set_a_ri_32", into: "cmp_set_a_mi_32", load: "mov_rm_32", swapped: None },
623 Fold { from: "cmp_set_a_ri_64", into: "cmp_set_a_mi_64", load: "mov_rm_64", swapped: None },
624 Fold { from: "cmp_set_ae_ri_8", into: "cmp_set_ae_mi_8", load: "mov_rm_8", swapped: None },
625 Fold { from: "cmp_set_ae_ri_16", into: "cmp_set_ae_mi_16", load: "mov_rm_16", swapped: None },
626 Fold { from: "cmp_set_ae_ri_32", into: "cmp_set_ae_mi_32", load: "mov_rm_32", swapped: None },
627 Fold { from: "cmp_set_ae_ri_64", into: "cmp_set_ae_mi_64", load: "mov_rm_64", swapped: None },
628];
629
630#[derive(Debug, Clone, Copy, PartialEq, Eq)]
637pub struct Update {
638 pub from: &'static str,
640 pub into: &'static str,
642 pub load: &'static str,
644 pub store: &'static str,
646 pub commutes: bool,
648}
649
650pub static UPDATES: &[Update] = &[
661 Update {
662 from: "add_rr_8",
663 into: "add_mr_8",
664 load: "mov_rm_8",
665 store: "mov_mr_8",
666 commutes: true,
667 },
668 Update {
669 from: "add_rr_16",
670 into: "add_mr_16",
671 load: "mov_rm_16",
672 store: "mov_mr_16",
673 commutes: true,
674 },
675 Update {
676 from: "add_rr_32",
677 into: "add_mr_32",
678 load: "mov_rm_32",
679 store: "mov_mr_32",
680 commutes: true,
681 },
682 Update {
683 from: "add_rr_64",
684 into: "add_mr_64",
685 load: "mov_rm_64",
686 store: "mov_mr_64",
687 commutes: true,
688 },
689 Update {
690 from: "sub_rr_8",
691 into: "sub_mr_8",
692 load: "mov_rm_8",
693 store: "mov_mr_8",
694 commutes: false,
695 },
696 Update {
697 from: "sub_rr_16",
698 into: "sub_mr_16",
699 load: "mov_rm_16",
700 store: "mov_mr_16",
701 commutes: false,
702 },
703 Update {
704 from: "sub_rr_32",
705 into: "sub_mr_32",
706 load: "mov_rm_32",
707 store: "mov_mr_32",
708 commutes: false,
709 },
710 Update {
711 from: "sub_rr_64",
712 into: "sub_mr_64",
713 load: "mov_rm_64",
714 store: "mov_mr_64",
715 commutes: false,
716 },
717 Update {
718 from: "and_rr_8",
719 into: "and_mr_8",
720 load: "mov_rm_8",
721 store: "mov_mr_8",
722 commutes: true,
723 },
724 Update {
725 from: "and_rr_16",
726 into: "and_mr_16",
727 load: "mov_rm_16",
728 store: "mov_mr_16",
729 commutes: true,
730 },
731 Update {
732 from: "and_rr_32",
733 into: "and_mr_32",
734 load: "mov_rm_32",
735 store: "mov_mr_32",
736 commutes: true,
737 },
738 Update {
739 from: "and_rr_64",
740 into: "and_mr_64",
741 load: "mov_rm_64",
742 store: "mov_mr_64",
743 commutes: true,
744 },
745 Update {
746 from: "or_rr_8",
747 into: "or_mr_8",
748 load: "mov_rm_8",
749 store: "mov_mr_8",
750 commutes: true,
751 },
752 Update {
753 from: "or_rr_16",
754 into: "or_mr_16",
755 load: "mov_rm_16",
756 store: "mov_mr_16",
757 commutes: true,
758 },
759 Update {
760 from: "or_rr_32",
761 into: "or_mr_32",
762 load: "mov_rm_32",
763 store: "mov_mr_32",
764 commutes: true,
765 },
766 Update {
767 from: "or_rr_64",
768 into: "or_mr_64",
769 load: "mov_rm_64",
770 store: "mov_mr_64",
771 commutes: true,
772 },
773 Update {
774 from: "xor_rr_8",
775 into: "xor_mr_8",
776 load: "mov_rm_8",
777 store: "mov_mr_8",
778 commutes: true,
779 },
780 Update {
781 from: "xor_rr_16",
782 into: "xor_mr_16",
783 load: "mov_rm_16",
784 store: "mov_mr_16",
785 commutes: true,
786 },
787 Update {
788 from: "xor_rr_32",
789 into: "xor_mr_32",
790 load: "mov_rm_32",
791 store: "mov_mr_32",
792 commutes: true,
793 },
794 Update {
795 from: "xor_rr_64",
796 into: "xor_mr_64",
797 load: "mov_rm_64",
798 store: "mov_mr_64",
799 commutes: true,
800 },
801];
802
803#[derive(Debug, Clone, Copy, PartialEq, Eq)]
812pub struct Bump {
813 pub from: &'static str,
815 pub into: &'static str,
817 pub load: &'static str,
819 pub store: &'static str,
821}
822
823pub static BUMPS: &[Bump] = &[
838 Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
839 Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
840 Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
841 Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
842 Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
843 Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
844 Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
845 Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
846 Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
847 Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
848 Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
849 Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
850 Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
851 Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
852 Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
853 Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
854 Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
855 Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
856 Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
857 Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
858];
859
860#[derive(Debug, Clone, Copy)]
865struct Waiting {
866 inst: Inst,
868 reg: Reg,
870 load: &'static str,
872 at: usize,
874}
875
876pub fn loads(
887 func: &mut Func,
888 machine: &MachineInsts,
889 names: &mut Interner,
890 pending: &mut Pending<'_>,
891) -> usize {
892 let mut reads = Reads::of(func);
893 let mut done = 0;
894 for block in func.blocks().collect::<Vec<_>>() {
895 let mut waiting: Option<Waiting> = None;
896 for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
897 let name = names.resolve(func[inst].opcode.name()).to_owned();
898 let bare = machine.bare(&name).to_owned();
899 let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
905 if let Some(carried) = waiting {
906 if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
907 let mut set = Changes::new();
908 set.rewrite(inst, plan);
909 set.remove(carried.inst);
910 if set.commit(func, &mut reads, names, machine).is_ok() {
911 pending.moved(carried.inst, &[inst]);
912 waiting = None;
913 done += 1;
914 }
915 }
916 }
917 if barrier {
918 waiting = None;
919 }
920 if let Some(carried) = waiting {
921 if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
922 waiting = None;
923 }
924 }
925 if insisted(func, inst) {
931 continue;
932 }
933 if let Some(load) = FOLDS.iter().find(|fold| fold.load == bare).map(|fold| fold.load) {
934 let operands = &func[func[inst].operands];
935 if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
936 waiting = Some(Waiting { inst, reg: first.reg, load, at });
937 }
938 }
939 }
940 }
941 done
942}
943
944#[derive(Debug, Clone, Copy)]
946struct Run {
947 load: Inst,
949 alu: Inst,
951 store: Inst,
953 update: &'static Update,
955 kept: Operand,
957}
958
959#[derive(Debug, Clone, Copy)]
965struct Bumped {
966 load: Inst,
968 alu: Inst,
970 store: Inst,
972 bump: &'static Bump,
974 imm: i64,
976}
977
978pub fn stores(
995 func: &mut Func,
996 machine: &MachineInsts,
997 names: &mut Interner,
998 pending: &mut Pending<'_>,
999) -> usize {
1000 let mut reads = Reads::of(func);
1001 let mut done = 0;
1002 for block in func.blocks().collect::<Vec<_>>() {
1003 let insts: Vec<Inst> = func.insts(block).collect();
1004 for at in 0..insts.len() {
1005 let found = match run(func, &reads, machine, names, &insts, at) {
1006 Some(found) => Some((
1007 found.load,
1008 found.alu,
1009 found.store,
1010 updated(func, machine, names, &found),
1011 )),
1012 None => constant(func, &reads, machine, names, &insts, at).map(|found| {
1013 (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1014 }),
1015 };
1016 let Some((load, alu, store, plan)) = found else { continue };
1017 if !pending.alike(load, store) {
1018 continue;
1019 }
1020 let mut set = Changes::new();
1021 set.rewrite(store, plan);
1022 set.remove(alu);
1023 set.remove(load);
1024 if set.commit(func, &mut reads, names, machine).is_ok() {
1025 pending.moved(load, &[]);
1026 done += 1;
1027 }
1028 }
1029 }
1030 done
1031}
1032
1033fn run(
1045 func: &Func,
1046 reads: &Reads,
1047 machine: &MachineInsts,
1048 names: &Interner,
1049 insts: &[Inst],
1050 at: usize,
1051) -> Option<Run> {
1052 let store = insts[at];
1053 if insisted(func, store) {
1054 return None;
1055 }
1056 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1057 let value = *func[func[store].operands].first()?;
1058 if value.role.is_def() || reads.count(value.reg) != 1 {
1059 return None;
1060 }
1061 let earliest = at.saturating_sub(WINDOW);
1064 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1065 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1066 let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1067 let operands = func[func[insts[alu]].operands].to_vec();
1068 let [_, first, second] = operands[..] else { return None };
1069 let both = [(first, second), (second, first)];
1074 let tried = if update.commutes { &both[..] } else { &both[..1] };
1075 for &(source, kept) in tried {
1076 if reads.count(source.reg) != 1 {
1077 continue;
1078 }
1079 let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1080 continue;
1081 };
1082 let load = insts[from];
1083 if insisted(func, load) {
1084 continue;
1085 }
1086 if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1087 continue;
1088 }
1089 if !same_place(func, load, store) {
1090 continue;
1091 }
1092 let mut wanted: Vec<Reg> =
1097 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1098 wanted.push(kept.reg);
1099 if !clear(func, machine, names, insts, (from, at), &wanted) {
1100 continue;
1101 }
1102 return Some(Run { load, alu: insts[alu], store, update, kept });
1103 }
1104 None
1105}
1106
1107fn constant(
1121 func: &Func,
1122 reads: &Reads,
1123 machine: &MachineInsts,
1124 names: &Interner,
1125 insts: &[Inst],
1126 at: usize,
1127) -> Option<Bumped> {
1128 let store = insts[at];
1129 if insisted(func, store) {
1130 return None;
1131 }
1132 let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1133 let value = *func[func[store].operands].first()?;
1134 if value.role.is_def() || reads.count(value.reg) != 1 {
1135 return None;
1136 }
1137 let mem = func[func[store].mem?];
1138 if mem.base == Some(0) || mem.index == Some(0) {
1139 return None;
1140 }
1141 let earliest = at.saturating_sub(WINDOW);
1142 let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1143 let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1144 let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1145 let operands = func[func[insts[alu]].operands].to_vec();
1146 let [_, source] = operands[..] else { return None };
1147 let imm = func[func[insts[alu]].imm?].0;
1148 if reads.count(source.reg) != 1 {
1149 return None;
1150 }
1151 let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1152 let load = insts[from];
1153 if insisted(func, load) {
1154 return None;
1155 }
1156 if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1157 return None;
1158 }
1159 if !same_place(func, load, store) {
1160 return None;
1161 }
1162 let wanted: Vec<Reg> =
1165 func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1166 if !clear(func, machine, names, insts, (from, at), &wanted) {
1167 return None;
1168 }
1169 Some(Bumped { load, alu: insts[alu], store, bump, imm })
1170}
1171
1172fn insisted(func: &Func, inst: Inst) -> bool {
1177 func[inst].flags.contains(Flags::VOLATILE)
1178}
1179
1180fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1182 func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1183}
1184
1185fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1192 let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1193 let (here, there) = (func[here], func[there]);
1194 if func[one].symbol != func[other].symbol {
1195 return false;
1196 }
1197 let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1198 if bare(here) != bare(there) {
1199 return false;
1200 }
1201 let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1202 (None, None) => true,
1203 (Some(left), Some(right)) => {
1204 func[func[one].operands][usize::from(left)].reg
1205 == func[func[other].operands][usize::from(right)].reg
1206 }
1207 _ => false,
1208 };
1209 same(here.base, there.base) && same(here.index, there.index)
1210}
1211
1212fn clear(
1219 func: &Func,
1220 machine: &MachineInsts,
1221 names: &Interner,
1222 insts: &[Inst],
1223 span: (usize, usize),
1224 wanted: &[Reg],
1225) -> bool {
1226 let (from, to) = span;
1227 insts[from + 1..to].iter().all(|&inst| {
1228 let name = names.resolve(func[inst].opcode.name());
1229 if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1230 return false;
1231 }
1232 !func[func[inst].operands]
1233 .iter()
1234 .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1235 })
1236}
1237
1238fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1244 let operands = func[func[run.store].operands].to_vec();
1245 let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1246 Plan {
1247 opcode: Opcode::new(into),
1248 operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1249 imm: None,
1250 amode: func[run.store].mem.map(|mem| func[mem]),
1251 symbol: func[run.store].symbol,
1252 }
1253}
1254
1255fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1263 let operands = func[func[run.store].operands][1..].to_vec();
1264 let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1265 let back = |at: Option<u8>| at.map(|at| at - 1);
1266 Plan {
1267 opcode: Opcode::new(into),
1268 operands,
1269 imm: Some(run.imm),
1270 amode: func[run.store].mem.map(|mem| {
1271 let mem = func[mem];
1272 Amode { base: back(mem.base), index: back(mem.index), ..mem }
1273 }),
1274 symbol: func[run.store].symbol,
1275 }
1276}
1277
1278fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1284 let written: Vec<Reg> = func[func[inst].operands]
1285 .iter()
1286 .filter(|operand| operand.role.is_def())
1287 .map(|operand| operand.reg)
1288 .collect();
1289 func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1290}
1291
1292fn joined(
1297 func: &Func,
1298 reads: &Reads,
1299 carried: Waiting,
1300 machine: &MachineInsts,
1301 names: &mut Interner,
1302 inst: Inst,
1303 bare: &str,
1304) -> Option<Plan> {
1305 let fold = FOLDS.iter().find(|fold| fold.from == bare)?;
1306 if carried.load != fold.load || reads.count(carried.reg) != 1 {
1307 return None;
1308 }
1309 let operands = func[func[inst].operands].to_vec();
1310 let (front, into) = match operands[..] {
1321 [answer, first, second] => {
1322 let (kept, into) = if second.reg == carried.reg {
1323 (first, fold.into)
1324 } else if first.reg == carried.reg {
1325 (second, fold.swapped?)
1326 } else {
1327 return None;
1328 };
1329 (vec![answer, kept], into)
1330 }
1331 [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1332 _ => return None,
1333 };
1334 let load = carried.inst;
1335 let address = func[func[load].operands][1..].to_vec();
1336 let mut amode = func[func[load].mem?];
1337 let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1341 amode.base = amode.base.map(|at| at + along);
1342 amode.index = amode.index.map(|at| at + along);
1343 let into = names.intern(&format!("{}{}", machine.prefix, into));
1344 Some(Plan {
1345 opcode: Opcode::new(into),
1346 operands: front.into_iter().chain(address).collect(),
1347 imm: func[inst].imm.map(|at| func[at].0),
1348 amode: Some(amode),
1349 symbol: func[load].symbol,
1350 })
1351}
1352
1353#[cfg(test)]
1354mod tests {
1355 use rucc_mir::{self as mir, Constraint, Mem, Operand};
1356 use rucc_target::x86_64::{GPR, MACHINE};
1357
1358 use super::*;
1359
1360 fn empty() -> (Interner, Func, mir::Block) {
1362 let mut names = Interner::new();
1363 let mut func = Func::new(names.intern("f"));
1364 let block = func.create_block();
1365 (names, func, block)
1366 }
1367
1368 fn op(names: &mut Interner, name: &str) -> Opcode {
1370 Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1371 }
1372
1373 fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1375 let into = func.new_vreg(GPR);
1376 let mov = op(names, "mov_rm_64");
1377 func.build(block, mov)
1378 .def(into, GPR)
1379 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1380 .finish();
1381 into
1382 }
1383
1384 fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1386 let into = func.new_vreg(GPR);
1387 let mov = op(names, "mov_rm_64");
1388 func.build(block, mov)
1389 .def(into, GPR)
1390 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1391 .flags(Flags::VOLATILE)
1392 .finish();
1393 into
1394 }
1395
1396 fn alu(
1398 func: &mut Func,
1399 names: &mut Interner,
1400 block: mir::Block,
1401 name: &str,
1402 first: Reg,
1403 second: Reg,
1404 ) -> Reg {
1405 let answer = func.new_vreg(GPR);
1406 let opcode = op(names, name);
1407 func.build(block, opcode)
1408 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1409 .uses(first, GPR)
1410 .uses(second, GPR)
1411 .finish();
1412 answer
1413 }
1414
1415 fn compare(
1418 func: &mut Func,
1419 names: &mut Interner,
1420 block: mir::Block,
1421 name: &str,
1422 first: Reg,
1423 second: Reg,
1424 ) -> Reg {
1425 let byte = func.new_vreg(GPR);
1426 let opcode = op(names, name);
1427 func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1428 byte
1429 }
1430
1431 fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1433 func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1434 }
1435
1436 fn combine(func: &mut Func, names: &mut Interner) -> usize {
1438 let mut addresses = Vec::new();
1439 let mut arguments = Vec::new();
1440 let mut dynamic = Vec::new();
1441 let mut pending =
1442 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1443 loads(func, &MACHINE, names, &mut pending)
1444 }
1445
1446 fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1448 let mov = op(names, "mov_mr_64");
1449 func.build(block, mov)
1450 .uses(value, GPR)
1451 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1452 .finish();
1453 }
1454
1455 fn insisted_store(
1457 func: &mut Func,
1458 names: &mut Interner,
1459 block: mir::Block,
1460 base: Reg,
1461 value: Reg,
1462 ) {
1463 let mov = op(names, "mov_mr_64");
1464 func.build(block, mov)
1465 .uses(value, GPR)
1466 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1467 .flags(Flags::VOLATILE)
1468 .finish();
1469 }
1470
1471 fn update(func: &mut Func, names: &mut Interner) -> usize {
1473 let mut addresses = Vec::new();
1474 let mut arguments = Vec::new();
1475 let mut dynamic = Vec::new();
1476 let mut pending =
1477 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1478 stores(func, &MACHINE, names, &mut pending)
1479 }
1480
1481 #[test]
1483 fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1484 let (mut names, mut func, block) = empty();
1485 let base = func.new_vreg(GPR);
1486 let other = func.new_vreg(GPR);
1487 let word = load(&mut func, &mut names, block, base);
1488 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1489 store(&mut func, &mut names, block, base, sum);
1490
1491 assert_eq!(update(&mut func, &mut names), 1);
1492 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1493 let inst = func.insts(block).next().expect("the addition");
1494 let mem = func[inst].mem.expect("it writes memory");
1495 assert_eq!(func[mem].disp, 16, "the address came from the store");
1496 assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1497 assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1498 assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1499 assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1500 }
1501
1502 #[test]
1506 fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1507 let (mut names, mut func, block) = empty();
1508 let base = func.new_vreg(GPR);
1509 let other = func.new_vreg(GPR);
1510 let word = load(&mut func, &mut names, block, base);
1511 let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1512 store(&mut func, &mut names, block, base, sum);
1513
1514 assert_eq!(update(&mut func, &mut names), 1);
1515 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1516 assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1517 }
1518
1519 #[test]
1522 fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1523 let (mut names, mut func, block) = empty();
1524 let base = func.new_vreg(GPR);
1525 let other = func.new_vreg(GPR);
1526 let word = load(&mut func, &mut names, block, base);
1527 let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1528 store(&mut func, &mut names, block, base, left);
1529
1530 assert_eq!(update(&mut func, &mut names), 1);
1531 assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1532 }
1533
1534 #[test]
1537 fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1538 let (mut names, mut func, block) = empty();
1539 let base = func.new_vreg(GPR);
1540 let other = func.new_vreg(GPR);
1541 let word = load(&mut func, &mut names, block, base);
1542 let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1543 store(&mut func, &mut names, block, base, left);
1544
1545 assert_eq!(update(&mut func, &mut names), 0);
1546 assert_eq!(
1547 shape(&func, &names, block),
1548 ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1549 );
1550 }
1551
1552 #[test]
1555 fn a_store_to_another_address_stays_three_instructions() {
1556 let (mut names, mut func, block) = empty();
1557 let base = func.new_vreg(GPR);
1558 let elsewhere = func.new_vreg(GPR);
1559 let other = func.new_vreg(GPR);
1560 let word = load(&mut func, &mut names, block, base);
1561 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1562 store(&mut func, &mut names, block, elsewhere, sum);
1563
1564 assert_eq!(update(&mut func, &mut names), 0);
1565 }
1566
1567 #[test]
1570 fn a_store_at_another_displacement_stays_three_instructions() {
1571 let (mut names, mut func, block) = empty();
1572 let base = func.new_vreg(GPR);
1573 let other = func.new_vreg(GPR);
1574 let word = load(&mut func, &mut names, block, base);
1575 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1576 let mov = op(&mut names, "mov_mr_64");
1577 func.build(block, mov)
1578 .uses(sum, GPR)
1579 .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1580 .finish();
1581
1582 assert_eq!(update(&mut func, &mut names), 0);
1583 }
1584
1585 #[test]
1588 fn a_word_two_instructions_read_stays_three_instructions() {
1589 let (mut names, mut func, block) = empty();
1590 let base = func.new_vreg(GPR);
1591 let other = func.new_vreg(GPR);
1592 let word = load(&mut func, &mut names, block, base);
1593 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1594 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1595 store(&mut func, &mut names, block, base, sum);
1596
1597 assert_eq!(update(&mut func, &mut names), 0);
1598 }
1599
1600 #[test]
1603 fn an_answer_something_else_reads_stays_three_instructions() {
1604 let (mut names, mut func, block) = empty();
1605 let base = func.new_vreg(GPR);
1606 let other = func.new_vreg(GPR);
1607 let word = load(&mut func, &mut names, block, base);
1608 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1609 store(&mut func, &mut names, block, base, sum);
1610 alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1611
1612 assert_eq!(update(&mut func, &mut names), 0);
1613 }
1614
1615 #[test]
1618 fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1619 let (mut names, mut func, block) = empty();
1620 let base = func.new_vreg(GPR);
1621 let other = func.new_vreg(GPR);
1622 let word = load(&mut func, &mut names, block, base);
1623 load(&mut func, &mut names, block, other);
1624 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1625 store(&mut func, &mut names, block, base, sum);
1626
1627 assert_eq!(update(&mut func, &mut names), 0);
1628 }
1629
1630 #[test]
1633 fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1634 let (mut names, mut func, block) = empty();
1635 let base = Reg::physical(rucc_target::x86_64::RSP);
1636 let other = func.new_vreg(GPR);
1637 let word = load(&mut func, &mut names, block, base);
1638 let sub = op(&mut names, "sub_ri_64");
1639 func.build(block, sub)
1640 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1641 .uses(base, GPR)
1642 .imm(32)
1643 .finish();
1644 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1645 store(&mut func, &mut names, block, base, sum);
1646
1647 assert_eq!(update(&mut func, &mut names), 0);
1648 }
1649
1650 #[test]
1654 fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1655 let (mut names, mut func, block) = empty();
1656 let base = Reg::physical(rucc_target::x86_64::RSP);
1657 let other = func.new_vreg(GPR);
1658 let mov = op(&mut names, "mov_rm_64");
1659 let word = func.new_vreg(GPR);
1660 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1661 let read = func.insts(block).next().expect("the load");
1662 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1663 let put = op(&mut names, "mov_mr_64");
1664 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1665 let written = func.insts(block).nth(2).expect("the store");
1666
1667 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1668 let mut arguments = Vec::new();
1669 let mut dynamic = Vec::new();
1670 let mut pending =
1671 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1672 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 0);
1673 }
1674
1675 #[test]
1679 fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1680 let (mut names, mut func, block) = empty();
1681 let base = Reg::physical(rucc_target::x86_64::RSP);
1682 let other = func.new_vreg(GPR);
1683 let mov = op(&mut names, "mov_rm_64");
1684 let word = func.new_vreg(GPR);
1685 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1686 let read = func.insts(block).next().expect("the load");
1687 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1688 let put = op(&mut names, "mov_mr_64");
1689 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1690 let written = func.insts(block).nth(2).expect("the store");
1691
1692 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1693 let mut arguments = Vec::new();
1694 let mut dynamic = Vec::new();
1695 let mut pending =
1696 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1697 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 1);
1698
1699 let inst = func.insts(block).next().expect("the addition");
1700 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1701 }
1702
1703 #[test]
1705 fn a_run_whose_widths_disagree_stays_three_instructions() {
1706 let (mut names, mut func, block) = empty();
1707 let base = func.new_vreg(GPR);
1708 let other = func.new_vreg(GPR);
1709 let into = func.new_vreg(GPR);
1710 let narrow = op(&mut names, "mov_rm_32");
1711 func.build(block, narrow)
1712 .def(into, GPR)
1713 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1714 .finish();
1715 let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1716 store(&mut func, &mut names, block, base, sum);
1717
1718 assert_eq!(update(&mut func, &mut names), 0);
1719 }
1720
1721 #[test]
1723 fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1724 for update in UPDATES {
1725 for name in [update.from, update.into, update.load, update.store] {
1726 assert!(MACHINE.has(name), "{name} is not an instruction");
1727 }
1728 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1729 assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1730 assert_eq!(
1731 width(update.from),
1732 width(update.load),
1733 "{} loads another width",
1734 update.from
1735 );
1736 assert_eq!(
1737 width(update.from),
1738 width(update.store),
1739 "{} stores another width",
1740 update.from
1741 );
1742 assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1743 assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1744 }
1745 }
1746
1747 #[test]
1750 fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1751 assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1752 let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1753 assert_eq!(commuting, 16, "everything but the four subtractions");
1754 }
1755
1756 fn alu_imm(
1758 func: &mut Func,
1759 names: &mut Interner,
1760 block: mir::Block,
1761 name: &str,
1762 source: Reg,
1763 value: i64,
1764 ) -> Reg {
1765 let answer = func.new_vreg(GPR);
1766 let opcode = op(names, name);
1767 func.build(block, opcode)
1768 .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1769 .uses(source, GPR)
1770 .imm(value)
1771 .finish();
1772 answer
1773 }
1774
1775 #[test]
1777 fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1778 let (mut names, mut func, block) = empty();
1779 let base = func.new_vreg(GPR);
1780 let word = load(&mut func, &mut names, block, base);
1781 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1782 store(&mut func, &mut names, block, base, sum);
1783
1784 assert_eq!(update(&mut func, &mut names), 1);
1785 assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1786 let inst = func.insts(block).next().expect("the addition");
1787 let mem = func[inst].mem.expect("it writes memory");
1788 assert_eq!(func[mem].disp, 16, "the address came from the store");
1789 assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1790 assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1791 assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1792 assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1793 }
1794
1795 #[test]
1798 fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1799 let (mut names, mut func, block) = empty();
1800 let base = func.new_vreg(GPR);
1801 let word = load(&mut func, &mut names, block, base);
1802 let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1803 store(&mut func, &mut names, block, base, left);
1804
1805 assert_eq!(update(&mut func, &mut names), 1);
1806 assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1807 assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1808 }
1809
1810 #[test]
1813 fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1814 let (mut names, mut func, block) = empty();
1815 let base = func.new_vreg(GPR);
1816 let word = func.new_vreg(GPR);
1817 let mov = op(&mut names, "mov_rm_8");
1818 func.build(block, mov)
1819 .def(word, GPR)
1820 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1821 .finish();
1822 let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1823 let put = op(&mut names, "mov_mr_8");
1824 func.build(block, put)
1825 .uses(sum, GPR)
1826 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1827 .finish();
1828
1829 assert_eq!(update(&mut func, &mut names), 1);
1830 assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1831 }
1832
1833 #[test]
1836 fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
1837 let (mut names, mut func, block) = empty();
1838 let base = func.new_vreg(GPR);
1839 let other = func.new_vreg(GPR);
1840 let word = load(&mut func, &mut names, block, base);
1841 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1842 alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1843 store(&mut func, &mut names, block, base, sum);
1844
1845 assert_eq!(update(&mut func, &mut names), 0);
1846 }
1847
1848 #[test]
1851 fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
1852 let (mut names, mut func, block) = empty();
1853 let base = func.new_vreg(GPR);
1854 let elsewhere = func.new_vreg(GPR);
1855 let word = load(&mut func, &mut names, block, base);
1856 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1857 load(&mut func, &mut names, block, elsewhere);
1858 store(&mut func, &mut names, block, base, sum);
1859
1860 assert_eq!(update(&mut func, &mut names), 0);
1861 }
1862
1863 #[test]
1866 fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1867 let (mut names, mut func, block) = empty();
1868 let base = Reg::physical(rucc_target::x86_64::RAX);
1869 let word = load(&mut func, &mut names, block, base);
1870 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1871 let mov = op(&mut names, "mov_ri_64");
1872 func.build(block, mov).def(base, GPR).imm(0).finish();
1873 store(&mut func, &mut names, block, base, sum);
1874
1875 assert_eq!(update(&mut func, &mut names), 0);
1876 }
1877
1878 #[test]
1880 fn a_constant_written_to_another_address_stays_three_instructions() {
1881 let (mut names, mut func, block) = empty();
1882 let base = func.new_vreg(GPR);
1883 let elsewhere = func.new_vreg(GPR);
1884 let word = load(&mut func, &mut names, block, base);
1885 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1886 store(&mut func, &mut names, block, elsewhere, sum);
1887
1888 assert_eq!(update(&mut func, &mut names), 0);
1889 }
1890
1891 #[test]
1893 fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
1894 let (mut names, mut func, block) = empty();
1895 let base = func.new_vreg(GPR);
1896 let into = func.new_vreg(GPR);
1897 let narrow = op(&mut names, "mov_rm_32");
1898 func.build(block, narrow)
1899 .def(into, GPR)
1900 .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1901 .finish();
1902 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
1903 store(&mut func, &mut names, block, base, sum);
1904
1905 assert_eq!(update(&mut func, &mut names), 0);
1906 }
1907
1908 #[test]
1911 fn a_place_multiplied_by_a_constant_stays_three_instructions() {
1912 let (mut names, mut func, block) = empty();
1913 let base = func.new_vreg(GPR);
1914 let word = load(&mut func, &mut names, block, base);
1915 let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
1916 store(&mut func, &mut names, block, base, product);
1917
1918 assert_eq!(update(&mut func, &mut names), 0);
1919 }
1920
1921 #[test]
1924 fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
1925 let (mut names, mut func, block) = empty();
1926 let base = Reg::physical(rucc_target::x86_64::RSP);
1927 let mov = op(&mut names, "mov_rm_64");
1928 let word = func.new_vreg(GPR);
1929 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1930 let read = func.insts(block).next().expect("the load");
1931 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1932 let put = op(&mut names, "mov_mr_64");
1933 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1934 let written = func.insts(block).nth(2).expect("the store");
1935
1936 let mut addresses = vec![(read, 3usize), (written, 3usize)];
1937 let mut arguments = Vec::new();
1938 let mut dynamic = Vec::new();
1939 let mut pending =
1940 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1941 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 1);
1942
1943 let inst = func.insts(block).next().expect("the addition");
1944 assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1945 }
1946
1947 #[test]
1950 fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
1951 let (mut names, mut func, block) = empty();
1952 let base = Reg::physical(rucc_target::x86_64::RSP);
1953 let mov = op(&mut names, "mov_rm_64");
1954 let word = func.new_vreg(GPR);
1955 func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1956 let read = func.insts(block).next().expect("the load");
1957 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1958 let put = op(&mut names, "mov_mr_64");
1959 func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1960 let written = func.insts(block).nth(2).expect("the store");
1961
1962 let mut addresses = vec![(read, 3usize), (written, 4usize)];
1963 let mut arguments = Vec::new();
1964 let mut dynamic = Vec::new();
1965 let mut pending =
1966 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1967 assert_eq!(stores(&mut func, &MACHINE, &mut names, &mut pending), 0);
1968 }
1969
1970 #[test]
1972 fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
1973 for bump in BUMPS {
1974 for name in [bump.from, bump.into, bump.load, bump.store] {
1975 assert!(MACHINE.has(name), "{name} is not an instruction");
1976 }
1977 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1978 assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
1979 assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
1980 assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
1981 assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
1982 assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
1983 assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
1984 }
1985 }
1986
1987 #[test]
1990 fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
1991 assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
1992 let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
1993 for bump in BUMPS {
1994 let same = bump.from.replace("_ri_", "_rr_");
1995 assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
1996 }
1997 }
1998
1999 #[test]
2002 fn nothing_is_both_a_register_run_and_a_constant_run() {
2003 for bump in BUMPS {
2004 assert!(
2005 !UPDATES.iter().any(|update| update.from == bump.from),
2006 "{} starts both kinds of run",
2007 bump.from
2008 );
2009 }
2010 }
2011
2012 #[test]
2014 fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2015 let (mut names, mut func, block) = empty();
2016 let base = func.new_vreg(GPR);
2017 let other = func.new_vreg(GPR);
2018 let word = load(&mut func, &mut names, block, base);
2019 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2020
2021 assert_eq!(combine(&mut func, &mut names), 1);
2022 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2023 let inst = func.insts(block).next().expect("the addition");
2024 let mem = func[inst].mem.expect("the addition reads memory now");
2025 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2026 assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2027 assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2028 assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2029 }
2030
2031 #[test]
2034 fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2035 let (mut names, mut func, block) = empty();
2036 let base = func.new_vreg(GPR);
2037 let other = func.new_vreg(GPR);
2038 let word = load(&mut func, &mut names, block, base);
2039 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2040
2041 assert_eq!(combine(&mut func, &mut names), 1);
2042 assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2043 let inst = func.insts(block).next().expect("the addition");
2044 assert_eq!(func[func[inst].operands][1].reg, other);
2045 }
2046
2047 #[test]
2050 fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2051 let (mut names, mut func, block) = empty();
2052 let base = func.new_vreg(GPR);
2053 let other = func.new_vreg(GPR);
2054 let word = load(&mut func, &mut names, block, base);
2055 alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2056
2057 assert_eq!(combine(&mut func, &mut names), 0);
2058 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2059 }
2060
2061 #[test]
2063 fn a_load_feeding_the_right_of_a_subtraction_folds() {
2064 let (mut names, mut func, block) = empty();
2065 let base = func.new_vreg(GPR);
2066 let other = func.new_vreg(GPR);
2067 let word = load(&mut func, &mut names, block, base);
2068 alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2069
2070 assert_eq!(combine(&mut func, &mut names), 1);
2071 assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2072 }
2073
2074 #[test]
2077 fn a_load_two_instructions_read_stays_a_load() {
2078 let (mut names, mut func, block) = empty();
2079 let base = func.new_vreg(GPR);
2080 let other = func.new_vreg(GPR);
2081 let word = load(&mut func, &mut names, block, base);
2082 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2083 alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2084
2085 assert_eq!(combine(&mut func, &mut names), 0);
2086 assert_eq!(
2087 shape(&func, &names, block),
2088 ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2089 );
2090 }
2091
2092 #[test]
2095 fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2096 let (mut names, mut func, block) = empty();
2097 let base = func.new_vreg(GPR);
2098 let other = func.new_vreg(GPR);
2099 let word = load(&mut func, &mut names, block, base);
2100 let store = op(&mut names, "mov_mr_64");
2101 func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2102 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2103
2104 assert_eq!(combine(&mut func, &mut names), 0);
2105 assert_eq!(
2106 shape(&func, &names, block),
2107 ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2108 );
2109 }
2110
2111 #[test]
2117 fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2118 let (mut names, mut func, block) = empty();
2119 let base = func.new_vreg(GPR);
2120 let other = func.new_vreg(GPR);
2121 let word = load(&mut func, &mut names, block, base);
2122 load(&mut func, &mut names, block, other);
2123 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2124
2125 assert_eq!(combine(&mut func, &mut names), 0);
2126 assert_eq!(
2127 shape(&func, &names, block),
2128 ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2129 );
2130 }
2131
2132 #[test]
2135 fn the_later_of_two_loads_is_the_one_that_folds() {
2136 let (mut names, mut func, block) = empty();
2137 let base = func.new_vreg(GPR);
2138 let other = func.new_vreg(GPR);
2139 let first = load(&mut func, &mut names, block, base);
2140 let second = load(&mut func, &mut names, block, other);
2141 alu(&mut func, &mut names, block, "add_rr_64", first, second);
2142
2143 assert_eq!(combine(&mut func, &mut names), 1);
2144 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2145 let addition = func.insts(block).nth(1).expect("the addition");
2146 assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2147 assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2148 }
2149
2150 #[test]
2153 fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2154 let (mut names, mut func, block) = empty();
2155 let base = func.new_vreg(GPR);
2156 let other = func.new_vreg(GPR);
2157 let word = load(&mut func, &mut names, block, base);
2158 let call = op(&mut names, "call");
2159 func.build(block, call).finish();
2160 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2161
2162 assert_eq!(combine(&mut func, &mut names), 0);
2163 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2164 }
2165
2166 #[test]
2169 fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2170 let (mut names, mut func, block) = empty();
2171 let base = Reg::physical(rucc_target::x86_64::RSP);
2172 let other = func.new_vreg(GPR);
2173 let word = load(&mut func, &mut names, block, base);
2174 let sub = op(&mut names, "sub_ri_64");
2175 func.build(block, sub)
2176 .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2177 .uses(base, GPR)
2178 .imm(32)
2179 .finish();
2180 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2181
2182 assert_eq!(combine(&mut func, &mut names), 0);
2183 }
2184
2185 #[test]
2188 fn a_load_of_the_wrong_width_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 into = func.new_vreg(GPR);
2193 let narrow = op(&mut names, "mov_rm_32");
2194 func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2195 alu(&mut func, &mut names, block, "add_rr_64", other, into);
2196
2197 assert_eq!(combine(&mut func, &mut names), 0);
2198 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2199 }
2200
2201 #[test]
2204 fn a_load_whose_value_an_edge_carries_stays_a_load() {
2205 let (mut names, mut func, block) = empty();
2206 let next = func.create_block();
2207 let base = func.new_vreg(GPR);
2208 let other = func.new_vreg(GPR);
2209 let word = load(&mut func, &mut names, block, base);
2210 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2211 let arrived = func.new_vreg(GPR);
2212 func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2213 *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2214
2215 assert_eq!(combine(&mut func, &mut names), 0);
2216 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2217 }
2218
2219 #[test]
2221 fn a_reader_in_another_block_stays_where_it_is() {
2222 let (mut names, mut func, block) = empty();
2223 let next = func.create_block();
2224 let base = func.new_vreg(GPR);
2225 let other = func.new_vreg(GPR);
2226 let word = load(&mut func, &mut names, block, base);
2227 alu(&mut func, &mut names, next, "add_rr_64", other, word);
2228
2229 assert_eq!(combine(&mut func, &mut names), 0);
2230 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2231 assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2232 }
2233
2234 #[test]
2236 fn a_reader_past_the_window_stays_where_it_is() {
2237 let (mut names, mut func, block) = empty();
2238 let base = func.new_vreg(GPR);
2239 let other = func.new_vreg(GPR);
2240 let word = load(&mut func, &mut names, block, base);
2241 let nop = op(&mut names, "nop");
2242 for _ in 0..WINDOW {
2243 func.build(block, nop).finish();
2244 }
2245 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2246
2247 assert_eq!(combine(&mut func, &mut names), 0);
2248 }
2249
2250 #[test]
2252 fn a_reader_at_the_edge_of_the_window_folds() {
2253 let (mut names, mut func, block) = empty();
2254 let base = func.new_vreg(GPR);
2255 let other = func.new_vreg(GPR);
2256 let word = load(&mut func, &mut names, block, base);
2257 let nop = op(&mut names, "nop");
2258 for _ in 0..WINDOW - 1 {
2259 func.build(block, nop).finish();
2260 }
2261 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2262
2263 assert_eq!(combine(&mut func, &mut names), 1);
2264 }
2265
2266 #[test]
2269 fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2270 let (mut names, mut func, block) = empty();
2271 let base = Reg::physical(rucc_target::x86_64::RSP);
2272 let other = func.new_vreg(GPR);
2273 let word = load(&mut func, &mut names, block, base);
2274 let reader = func.insts(block).nth(1);
2275 assert!(reader.is_none(), "the block holds the load alone so far");
2276 alu(&mut func, &mut names, block, "add_rr_64", other, word);
2277 let held = func.insts(block).next().expect("the load");
2278
2279 let mut addresses = vec![(held, 3usize)];
2280 let mut arguments = Vec::new();
2281 let mut dynamic = Vec::new();
2282 let mut pending =
2283 Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2284 assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2285
2286 let inst = func.insts(block).next().expect("the addition");
2287 assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2288 }
2289
2290 #[test]
2294 fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
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 compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2300
2301 assert_eq!(combine(&mut func, &mut names), 1);
2302 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2303 let inst = func.insts(block).next().expect("the comparison");
2304 let mem = func[inst].mem.expect("it reads memory");
2305 assert_eq!(func[mem].disp, 16, "the address came from the load");
2306 assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2307 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2308 assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2309 }
2310
2311 #[test]
2315 fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2316 let (mut names, mut func, block) = empty();
2317 let base = func.new_vreg(GPR);
2318 let other = func.new_vreg(GPR);
2319 let word = load(&mut func, &mut names, block, base);
2320 compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2321
2322 assert_eq!(combine(&mut func, &mut names), 1);
2323 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2324 let inst = func.insts(block).next().expect("the comparison");
2325 assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2326 }
2327
2328 #[test]
2332 fn an_equality_folded_on_either_side_is_the_same_comparison() {
2333 for (first, second) in [(true, false), (false, true)] {
2334 let (mut names, mut func, block) = empty();
2335 let base = func.new_vreg(GPR);
2336 let other = func.new_vreg(GPR);
2337 let word = load(&mut func, &mut names, block, base);
2338 let left = if first { word } else { other };
2339 let right = if second { word } else { other };
2340 compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2341
2342 assert_eq!(combine(&mut func, &mut names), 1);
2343 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2344 }
2345 }
2346
2347 #[test]
2352 fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2353 let (mut names, mut func, block) = empty();
2354 let base = func.new_vreg(GPR);
2355 let byte = func.new_vreg(GPR);
2356 let word = load(&mut func, &mut names, block, base);
2357 let opcode = op(&mut names, "cmp_set_l_ri_64");
2358 func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2359
2360 assert_eq!(combine(&mut func, &mut names), 1);
2361 assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2362 let inst = func.insts(block).next().expect("the comparison");
2363 let mem = func[inst].mem.expect("it reads memory now");
2364 assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2365 assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2366 assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2367 assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2368 let imm = func[inst].imm.expect("the constant is still on it");
2369 assert_eq!(func[imm].0, 7, "and is the one that was written");
2370 }
2371
2372 #[test]
2376 fn every_row_of_the_table_is_three_instructions_this_target_has() {
2377 for fold in FOLDS {
2378 assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2379 assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2380 assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2381 let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2382 assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2383 assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2384 assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2385 assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2386 let Some(swapped) = fold.swapped else { continue };
2387 assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2388 assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2389 assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2390 }
2391 }
2392
2393 #[test]
2397 fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2398 let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2399 assert_eq!(
2400 compares, 80,
2401 "ten conditions at four widths, against a register and a constant"
2402 );
2403 let arithmetic = FOLDS.len() - compares;
2404 assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2405 let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2406 assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2407 }
2408
2409 #[test]
2417 fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2418 let turned = |condition: &str| match condition {
2419 "e" => "e",
2420 "ne" => "ne",
2421 "l" => "g",
2422 "g" => "l",
2423 "le" => "ge",
2424 "ge" => "le",
2425 "b" => "a",
2426 "a" => "b",
2427 "be" => "ae",
2428 "ae" => "be",
2429 other => panic!("{other} is not a condition this machine has"),
2430 };
2431 let compares = FOLDS
2432 .iter()
2433 .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2434 for fold in compares {
2435 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2436 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2437 assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2438 let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2439 assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2440 }
2441 }
2442
2443 #[test]
2448 fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2449 let compares = FOLDS
2450 .iter()
2451 .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2452 let mut rows = 0;
2453 for fold in compares {
2454 let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2455 let front = front.strip_suffix("_ri").expect("a name against a constant");
2456 let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2457 assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2458 assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2459 assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2460 rows += 1;
2461 }
2462 assert_eq!(rows, 40, "ten conditions at four widths");
2463 }
2464
2465 #[test]
2472 fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2473 let (mut names, mut func, block) = empty();
2474 let base = func.new_vreg(GPR);
2475 let other = func.new_vreg(GPR);
2476 let word = insisted_load(&mut func, &mut names, block, base);
2477 alu(&mut func, &mut names, block, "add_rr_64", word, other);
2478
2479 assert_eq!(combine(&mut func, &mut names), 0);
2480 assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2481 }
2482
2483 #[test]
2487 fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2488 let (mut names, mut func, block) = empty();
2489 let base = func.new_vreg(GPR);
2490 let other = func.new_vreg(GPR);
2491 let word = insisted_load(&mut func, &mut names, block, base);
2492 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2493 store(&mut func, &mut names, block, base, sum);
2494
2495 assert_eq!(update(&mut func, &mut names), 0);
2496 }
2497
2498 #[test]
2503 fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2504 let (mut names, mut func, block) = empty();
2505 let base = func.new_vreg(GPR);
2506 let other = func.new_vreg(GPR);
2507 let word = load(&mut func, &mut names, block, base);
2508 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2509 insisted_store(&mut func, &mut names, block, base, sum);
2510
2511 assert_eq!(update(&mut func, &mut names), 0);
2512 }
2513
2514 #[test]
2517 fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2518 let (mut names, mut func, block) = empty();
2519 let base = func.new_vreg(GPR);
2520 let word = insisted_load(&mut func, &mut names, block, base);
2521 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2522 store(&mut func, &mut names, block, base, sum);
2523
2524 assert_eq!(update(&mut func, &mut names), 0);
2525 }
2526
2527 #[test]
2529 fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2530 let (mut names, mut func, block) = empty();
2531 let base = func.new_vreg(GPR);
2532 let word = load(&mut func, &mut names, block, base);
2533 let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2534 insisted_store(&mut func, &mut names, block, base, sum);
2535
2536 assert_eq!(update(&mut func, &mut names), 0);
2537 }
2538
2539 #[test]
2542 fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2543 let (mut names, mut func, block) = empty();
2544 let base = func.new_vreg(GPR);
2545 let other = func.new_vreg(GPR);
2546 let word = load(&mut func, &mut names, block, base);
2547 let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2548 store(&mut func, &mut names, block, base, sum);
2549
2550 assert_eq!(update(&mut func, &mut names), 1);
2551 assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2552 }
2553}