Capture compiler updates and formatting sweep
- SHA
f9a892d12375393e56514233185e348f72f5698b- Parents
-
b37c32f - Tree
564df24
f9a892d
f9a892d12375393e56514233185e348f72f5698bb37c32f
564df24afs-asmodified@@ -1,1 +1,1 @@ | ||
| 1 | -Subproject commit 67061bd8688fcbe3a633c87557913bed9eeddba6 | |
| 1 | +Subproject commit 36a2e46e175f530b8884242fed09c8b2b79bfda8 | |
afs-ldmodified@@ -1,1 +1,1 @@ | ||
| 1 | -Subproject commit 5e99aa0c7b24dd10a3ad09de3d11823e07bc4839 | |
| 1 | +Subproject commit 20d5047b0a9875d243f48f48b5ecfd70cc5d046a | |
runtime/src/array.rsmodified@@ -611,7 +611,9 @@ pub extern "C" fn afs_fill_i32(dest: *mut i32, n: i64, value: i32) { | ||
| 611 | 611 | if len == 0 { |
| 612 | 612 | return; |
| 613 | 613 | } |
| 614 | - unsafe { fill_i32_impl(dest, len, value); } | |
| 614 | + unsafe { | |
| 615 | + fill_i32_impl(dest, len, value); | |
| 616 | + } | |
| 615 | 617 | } |
| 616 | 618 | |
| 617 | 619 | #[no_mangle] |
@@ -623,7 +625,9 @@ pub extern "C" fn afs_fill_f32(dest: *mut f32, n: i64, value: f32) { | ||
| 623 | 625 | if len == 0 { |
| 624 | 626 | return; |
| 625 | 627 | } |
| 626 | - unsafe { fill_f32_impl(dest, len, value); } | |
| 628 | + unsafe { | |
| 629 | + fill_f32_impl(dest, len, value); | |
| 630 | + } | |
| 627 | 631 | } |
| 628 | 632 | |
| 629 | 633 | #[no_mangle] |
@@ -635,7 +639,9 @@ pub extern "C" fn afs_fill_f64(dest: *mut f64, n: i64, value: f64) { | ||
| 635 | 639 | if len == 0 { |
| 636 | 640 | return; |
| 637 | 641 | } |
| 638 | - unsafe { fill_f64_impl(dest, len, value); } | |
| 642 | + unsafe { | |
| 643 | + fill_f64_impl(dest, len, value); | |
| 644 | + } | |
| 639 | 645 | } |
| 640 | 646 | |
| 641 | 647 | #[no_mangle] |
@@ -647,7 +653,9 @@ pub extern "C" fn afs_array_add_i32(dest: *mut i32, lhs: *const i32, rhs: *const | ||
| 647 | 653 | if len == 0 { |
| 648 | 654 | return; |
| 649 | 655 | } |
| 650 | - unsafe { add_i32_impl(dest, lhs, rhs, len); } | |
| 656 | + unsafe { | |
| 657 | + add_i32_impl(dest, lhs, rhs, len); | |
| 658 | + } | |
| 651 | 659 | } |
| 652 | 660 | |
| 653 | 661 | #[no_mangle] |
@@ -659,7 +667,9 @@ pub extern "C" fn afs_array_add_f32(dest: *mut f32, lhs: *const f32, rhs: *const | ||
| 659 | 667 | if len == 0 { |
| 660 | 668 | return; |
| 661 | 669 | } |
| 662 | - unsafe { add_f32_impl(dest, lhs, rhs, len); } | |
| 670 | + unsafe { | |
| 671 | + add_f32_impl(dest, lhs, rhs, len); | |
| 672 | + } | |
| 663 | 673 | } |
| 664 | 674 | |
| 665 | 675 | #[no_mangle] |
@@ -671,7 +681,9 @@ pub extern "C" fn afs_array_add_f64(dest: *mut f64, lhs: *const f64, rhs: *const | ||
| 671 | 681 | if len == 0 { |
| 672 | 682 | return; |
| 673 | 683 | } |
| 674 | - unsafe { add_f64_impl(dest, lhs, rhs, len); } | |
| 684 | + unsafe { | |
| 685 | + add_f64_impl(dest, lhs, rhs, len); | |
| 686 | + } | |
| 675 | 687 | } |
| 676 | 688 | |
| 677 | 689 | #[no_mangle] |
@@ -683,7 +695,9 @@ pub extern "C" fn afs_array_sub_i32(dest: *mut i32, lhs: *const i32, rhs: *const | ||
| 683 | 695 | if len == 0 { |
| 684 | 696 | return; |
| 685 | 697 | } |
| 686 | - unsafe { sub_i32_impl(dest, lhs, rhs, len); } | |
| 698 | + unsafe { | |
| 699 | + sub_i32_impl(dest, lhs, rhs, len); | |
| 700 | + } | |
| 687 | 701 | } |
| 688 | 702 | |
| 689 | 703 | #[no_mangle] |
@@ -695,7 +709,9 @@ pub extern "C" fn afs_array_sub_f32(dest: *mut f32, lhs: *const f32, rhs: *const | ||
| 695 | 709 | if len == 0 { |
| 696 | 710 | return; |
| 697 | 711 | } |
| 698 | - unsafe { sub_f32_impl(dest, lhs, rhs, len); } | |
| 712 | + unsafe { | |
| 713 | + sub_f32_impl(dest, lhs, rhs, len); | |
| 714 | + } | |
| 699 | 715 | } |
| 700 | 716 | |
| 701 | 717 | #[no_mangle] |
@@ -707,7 +723,9 @@ pub extern "C" fn afs_array_sub_f64(dest: *mut f64, lhs: *const f64, rhs: *const | ||
| 707 | 723 | if len == 0 { |
| 708 | 724 | return; |
| 709 | 725 | } |
| 710 | - unsafe { sub_f64_impl(dest, lhs, rhs, len); } | |
| 726 | + unsafe { | |
| 727 | + sub_f64_impl(dest, lhs, rhs, len); | |
| 728 | + } | |
| 711 | 729 | } |
| 712 | 730 | |
| 713 | 731 | #[no_mangle] |
@@ -719,7 +737,9 @@ pub extern "C" fn afs_array_mul_i32(dest: *mut i32, lhs: *const i32, rhs: *const | ||
| 719 | 737 | if len == 0 { |
| 720 | 738 | return; |
| 721 | 739 | } |
| 722 | - unsafe { mul_i32_impl(dest, lhs, rhs, len); } | |
| 740 | + unsafe { | |
| 741 | + mul_i32_impl(dest, lhs, rhs, len); | |
| 742 | + } | |
| 723 | 743 | } |
| 724 | 744 | |
| 725 | 745 | #[no_mangle] |
@@ -731,7 +751,9 @@ pub extern "C" fn afs_array_mul_f32(dest: *mut f32, lhs: *const f32, rhs: *const | ||
| 731 | 751 | if len == 0 { |
| 732 | 752 | return; |
| 733 | 753 | } |
| 734 | - unsafe { mul_f32_impl(dest, lhs, rhs, len); } | |
| 754 | + unsafe { | |
| 755 | + mul_f32_impl(dest, lhs, rhs, len); | |
| 756 | + } | |
| 735 | 757 | } |
| 736 | 758 | |
| 737 | 759 | #[no_mangle] |
@@ -743,7 +765,9 @@ pub extern "C" fn afs_array_mul_f64(dest: *mut f64, lhs: *const f64, rhs: *const | ||
| 743 | 765 | if len == 0 { |
| 744 | 766 | return; |
| 745 | 767 | } |
| 746 | - unsafe { mul_f64_impl(dest, lhs, rhs, len); } | |
| 768 | + unsafe { | |
| 769 | + mul_f64_impl(dest, lhs, rhs, len); | |
| 770 | + } | |
| 747 | 771 | } |
| 748 | 772 | |
| 749 | 773 | #[no_mangle] |
@@ -755,7 +779,9 @@ pub extern "C" fn afs_array_add_scalar_i32(dest: *mut i32, src: *const i32, scal | ||
| 755 | 779 | if len == 0 { |
| 756 | 780 | return; |
| 757 | 781 | } |
| 758 | - unsafe { add_scalar_i32_impl(dest, src, scalar, len); } | |
| 782 | + unsafe { | |
| 783 | + add_scalar_i32_impl(dest, src, scalar, len); | |
| 784 | + } | |
| 759 | 785 | } |
| 760 | 786 | |
| 761 | 787 | #[no_mangle] |
@@ -767,7 +793,9 @@ pub extern "C" fn afs_array_add_scalar_f32(dest: *mut f32, src: *const f32, scal | ||
| 767 | 793 | if len == 0 { |
| 768 | 794 | return; |
| 769 | 795 | } |
| 770 | - unsafe { add_scalar_f32_impl(dest, src, scalar, len); } | |
| 796 | + unsafe { | |
| 797 | + add_scalar_f32_impl(dest, src, scalar, len); | |
| 798 | + } | |
| 771 | 799 | } |
| 772 | 800 | |
| 773 | 801 | #[no_mangle] |
@@ -779,7 +807,9 @@ pub extern "C" fn afs_array_add_scalar_f64(dest: *mut f64, src: *const f64, scal | ||
| 779 | 807 | if len == 0 { |
| 780 | 808 | return; |
| 781 | 809 | } |
| 782 | - unsafe { add_scalar_f64_impl(dest, src, scalar, len); } | |
| 810 | + unsafe { | |
| 811 | + add_scalar_f64_impl(dest, src, scalar, len); | |
| 812 | + } | |
| 783 | 813 | } |
| 784 | 814 | |
| 785 | 815 | #[no_mangle] |
@@ -791,7 +821,9 @@ pub extern "C" fn afs_array_sub_scalar_i32(dest: *mut i32, src: *const i32, scal | ||
| 791 | 821 | if len == 0 { |
| 792 | 822 | return; |
| 793 | 823 | } |
| 794 | - unsafe { sub_scalar_i32_impl(dest, src, scalar, len); } | |
| 824 | + unsafe { | |
| 825 | + sub_scalar_i32_impl(dest, src, scalar, len); | |
| 826 | + } | |
| 795 | 827 | } |
| 796 | 828 | |
| 797 | 829 | #[no_mangle] |
@@ -803,7 +835,9 @@ pub extern "C" fn afs_array_sub_scalar_f32(dest: *mut f32, src: *const f32, scal | ||
| 803 | 835 | if len == 0 { |
| 804 | 836 | return; |
| 805 | 837 | } |
| 806 | - unsafe { sub_scalar_f32_impl(dest, src, scalar, len); } | |
| 838 | + unsafe { | |
| 839 | + sub_scalar_f32_impl(dest, src, scalar, len); | |
| 840 | + } | |
| 807 | 841 | } |
| 808 | 842 | |
| 809 | 843 | #[no_mangle] |
@@ -815,7 +849,9 @@ pub extern "C" fn afs_array_sub_scalar_f64(dest: *mut f64, src: *const f64, scal | ||
| 815 | 849 | if len == 0 { |
| 816 | 850 | return; |
| 817 | 851 | } |
| 818 | - unsafe { sub_scalar_f64_impl(dest, src, scalar, len); } | |
| 852 | + unsafe { | |
| 853 | + sub_scalar_f64_impl(dest, src, scalar, len); | |
| 854 | + } | |
| 819 | 855 | } |
| 820 | 856 | |
| 821 | 857 | #[no_mangle] |
@@ -827,7 +863,9 @@ pub extern "C" fn afs_scalar_sub_array_i32(dest: *mut i32, scalar: i32, src: *co | ||
| 827 | 863 | if len == 0 { |
| 828 | 864 | return; |
| 829 | 865 | } |
| 830 | - unsafe { scalar_sub_i32_impl(dest, scalar, src, len); } | |
| 866 | + unsafe { | |
| 867 | + scalar_sub_i32_impl(dest, scalar, src, len); | |
| 868 | + } | |
| 831 | 869 | } |
| 832 | 870 | |
| 833 | 871 | #[no_mangle] |
@@ -839,7 +877,9 @@ pub extern "C" fn afs_scalar_sub_array_f32(dest: *mut f32, scalar: f32, src: *co | ||
| 839 | 877 | if len == 0 { |
| 840 | 878 | return; |
| 841 | 879 | } |
| 842 | - unsafe { scalar_sub_f32_impl(dest, scalar, src, len); } | |
| 880 | + unsafe { | |
| 881 | + scalar_sub_f32_impl(dest, scalar, src, len); | |
| 882 | + } | |
| 843 | 883 | } |
| 844 | 884 | |
| 845 | 885 | #[no_mangle] |
@@ -851,7 +891,9 @@ pub extern "C" fn afs_scalar_sub_array_f64(dest: *mut f64, scalar: f64, src: *co | ||
| 851 | 891 | if len == 0 { |
| 852 | 892 | return; |
| 853 | 893 | } |
| 854 | - unsafe { scalar_sub_f64_impl(dest, scalar, src, len); } | |
| 894 | + unsafe { | |
| 895 | + scalar_sub_f64_impl(dest, scalar, src, len); | |
| 896 | + } | |
| 855 | 897 | } |
| 856 | 898 | |
| 857 | 899 | #[no_mangle] |
@@ -863,7 +905,9 @@ pub extern "C" fn afs_array_mul_scalar_i32(dest: *mut i32, src: *const i32, scal | ||
| 863 | 905 | if len == 0 { |
| 864 | 906 | return; |
| 865 | 907 | } |
| 866 | - unsafe { mul_scalar_i32_impl(dest, src, scalar, len); } | |
| 908 | + unsafe { | |
| 909 | + mul_scalar_i32_impl(dest, src, scalar, len); | |
| 910 | + } | |
| 867 | 911 | } |
| 868 | 912 | |
| 869 | 913 | #[no_mangle] |
@@ -875,7 +919,9 @@ pub extern "C" fn afs_array_mul_scalar_f32(dest: *mut f32, src: *const f32, scal | ||
| 875 | 919 | if len == 0 { |
| 876 | 920 | return; |
| 877 | 921 | } |
| 878 | - unsafe { mul_scalar_f32_impl(dest, src, scalar, len); } | |
| 922 | + unsafe { | |
| 923 | + mul_scalar_f32_impl(dest, src, scalar, len); | |
| 924 | + } | |
| 879 | 925 | } |
| 880 | 926 | |
| 881 | 927 | #[no_mangle] |
@@ -887,7 +933,9 @@ pub extern "C" fn afs_array_mul_scalar_f64(dest: *mut f64, src: *const f64, scal | ||
| 887 | 933 | if len == 0 { |
| 888 | 934 | return; |
| 889 | 935 | } |
| 890 | - unsafe { mul_scalar_f64_impl(dest, src, scalar, len); } | |
| 936 | + unsafe { | |
| 937 | + mul_scalar_f64_impl(dest, src, scalar, len); | |
| 938 | + } | |
| 891 | 939 | } |
| 892 | 940 | |
| 893 | 941 | // ---- ALLOCATE ---- |
@@ -909,7 +957,11 @@ pub extern "C" fn afs_allocate_array( | ||
| 909 | 957 | stat: *mut i32, |
| 910 | 958 | ) { |
| 911 | 959 | if desc.is_null() { |
| 912 | - if !stat.is_null() { unsafe { *stat = 1; } } | |
| 960 | + if !stat.is_null() { | |
| 961 | + unsafe { | |
| 962 | + *stat = 1; | |
| 963 | + } | |
| 964 | + } | |
| 913 | 965 | return; |
| 914 | 966 | } |
| 915 | 967 | |
@@ -918,7 +970,9 @@ pub extern "C" fn afs_allocate_array( | ||
| 918 | 970 | // Check if already allocated. |
| 919 | 971 | if desc.is_allocated() { |
| 920 | 972 | if !stat.is_null() { |
| 921 | - unsafe { *stat = 2; } // already allocated | |
| 973 | + unsafe { | |
| 974 | + *stat = 2; | |
| 975 | + } // already allocated | |
| 922 | 976 | return; |
| 923 | 977 | } |
| 924 | 978 | eprintln!("ALLOCATE: array is already allocated"); |
@@ -943,7 +997,11 @@ pub extern "C" fn afs_allocate_array( | ||
| 943 | 997 | // Zero-size allocation: valid but produces a null/empty array. |
| 944 | 998 | desc.base_addr = ptr::null_mut(); |
| 945 | 999 | desc.flags = DESC_ALLOCATED | DESC_CONTIGUOUS; |
| 946 | - if !stat.is_null() { unsafe { *stat = 0; } } | |
| 1000 | + if !stat.is_null() { | |
| 1001 | + unsafe { | |
| 1002 | + *stat = 0; | |
| 1003 | + } | |
| 1004 | + } | |
| 947 | 1005 | return; |
| 948 | 1006 | } |
| 949 | 1007 | |
@@ -951,7 +1009,9 @@ pub extern "C" fn afs_allocate_array( | ||
| 951 | 1009 | let ptr = unsafe { libc_malloc(bytes as usize) }; |
| 952 | 1010 | if ptr.is_null() { |
| 953 | 1011 | if !stat.is_null() { |
| 954 | - unsafe { *stat = 3; } // allocation failed | |
| 1012 | + unsafe { | |
| 1013 | + *stat = 3; | |
| 1014 | + } // allocation failed | |
| 955 | 1015 | return; |
| 956 | 1016 | } |
| 957 | 1017 | eprintln!("ALLOCATE: out of memory ({} bytes)", bytes); |
@@ -959,24 +1019,36 @@ pub extern "C" fn afs_allocate_array( | ||
| 959 | 1019 | } |
| 960 | 1020 | |
| 961 | 1021 | // Zero-initialize (Fortran doesn't require this, but it's safer). |
| 962 | - unsafe { ptr::write_bytes(ptr, 0, bytes as usize); } | |
| 1022 | + unsafe { | |
| 1023 | + ptr::write_bytes(ptr, 0, bytes as usize); | |
| 1024 | + } | |
| 963 | 1025 | |
| 964 | 1026 | desc.base_addr = ptr; |
| 965 | 1027 | desc.flags = DESC_ALLOCATED | DESC_CONTIGUOUS; |
| 966 | 1028 | |
| 967 | - if !stat.is_null() { unsafe { *stat = 0; } } | |
| 1029 | + if !stat.is_null() { | |
| 1030 | + unsafe { | |
| 1031 | + *stat = 0; | |
| 1032 | + } | |
| 1033 | + } | |
| 968 | 1034 | } |
| 969 | 1035 | |
| 970 | 1036 | /// Simplified allocate for a 1D array with given element count. |
| 971 | 1037 | /// Used by generated code for simple `allocate(a(n))` patterns. |
| 972 | 1038 | #[no_mangle] |
| 973 | -pub extern "C" fn afs_allocate_1d( | |
| 974 | - desc: *mut ArrayDescriptor, | |
| 975 | - elem_size: i64, | |
| 976 | - n: i64, | |
| 977 | -) { | |
| 978 | - let dim = DimDescriptor { lower_bound: 1, upper_bound: n, stride: 1 }; | |
| 979 | - afs_allocate_array(desc, elem_size, 1, &dim as *const DimDescriptor, ptr::null_mut()); | |
| 1039 | +pub extern "C" fn afs_allocate_1d(desc: *mut ArrayDescriptor, elem_size: i64, n: i64) { | |
| 1040 | + let dim = DimDescriptor { | |
| 1041 | + lower_bound: 1, | |
| 1042 | + upper_bound: n, | |
| 1043 | + stride: 1, | |
| 1044 | + }; | |
| 1045 | + afs_allocate_array( | |
| 1046 | + desc, | |
| 1047 | + elem_size, | |
| 1048 | + 1, | |
| 1049 | + &dim as *const DimDescriptor, | |
| 1050 | + ptr::null_mut(), | |
| 1051 | + ); | |
| 980 | 1052 | } |
| 981 | 1053 | |
| 982 | 1054 | // ---- DEALLOCATE ---- |
@@ -985,12 +1057,13 @@ pub extern "C" fn afs_allocate_1d( | ||
| 985 | 1057 | /// |
| 986 | 1058 | /// Safe to call on an already-deallocated descriptor (no-op with stat=0). |
| 987 | 1059 | #[no_mangle] |
| 988 | -pub extern "C" fn afs_deallocate_array( | |
| 989 | - desc: *mut ArrayDescriptor, | |
| 990 | - stat: *mut i32, | |
| 991 | -) { | |
| 1060 | +pub extern "C" fn afs_deallocate_array(desc: *mut ArrayDescriptor, stat: *mut i32) { | |
| 992 | 1061 | if desc.is_null() { |
| 993 | - if !stat.is_null() { unsafe { *stat = 1; } } | |
| 1062 | + if !stat.is_null() { | |
| 1063 | + unsafe { | |
| 1064 | + *stat = 1; | |
| 1065 | + } | |
| 1066 | + } | |
| 994 | 1067 | return; |
| 995 | 1068 | } |
| 996 | 1069 | |
@@ -999,7 +1072,9 @@ pub extern "C" fn afs_deallocate_array( | ||
| 999 | 1072 | if !desc.is_allocated() { |
| 1000 | 1073 | // Not allocated — not an error with STAT, abort without STAT. |
| 1001 | 1074 | if !stat.is_null() { |
| 1002 | - unsafe { *stat = 0; } | |
| 1075 | + unsafe { | |
| 1076 | + *stat = 0; | |
| 1077 | + } | |
| 1003 | 1078 | return; |
| 1004 | 1079 | } |
| 1005 | 1080 | // Without STAT, deallocating an unallocated array is an error. |
@@ -1009,7 +1084,9 @@ pub extern "C" fn afs_deallocate_array( | ||
| 1009 | 1084 | |
| 1010 | 1085 | // Free the data. |
| 1011 | 1086 | if !desc.base_addr.is_null() { |
| 1012 | - unsafe { libc_free(desc.base_addr); } | |
| 1087 | + unsafe { | |
| 1088 | + libc_free(desc.base_addr); | |
| 1089 | + } | |
| 1013 | 1090 | } |
| 1014 | 1091 | |
| 1015 | 1092 | // Clear the descriptor. |
@@ -1017,7 +1094,11 @@ pub extern "C" fn afs_deallocate_array( | ||
| 1017 | 1094 | desc.flags &= !DESC_ALLOCATED; |
| 1018 | 1095 | // Leave rank, elem_size, dims intact (they describe the shape for future allocate). |
| 1019 | 1096 | |
| 1020 | - if !stat.is_null() { unsafe { *stat = 0; } } | |
| 1097 | + if !stat.is_null() { | |
| 1098 | + unsafe { | |
| 1099 | + *stat = 0; | |
| 1100 | + } | |
| 1101 | + } | |
| 1021 | 1102 | } |
| 1022 | 1103 | |
| 1023 | 1104 | // ---- ALLOCATABLE ASSIGNMENT ---- |
@@ -1031,22 +1112,24 @@ pub extern "C" fn afs_assign_allocatable( | ||
| 1031 | 1112 | dest: *mut ArrayDescriptor, |
| 1032 | 1113 | source: *const ArrayDescriptor, |
| 1033 | 1114 | ) { |
| 1034 | - if dest.is_null() || source.is_null() { return; } | |
| 1115 | + if dest.is_null() || source.is_null() { | |
| 1116 | + return; | |
| 1117 | + } | |
| 1035 | 1118 | |
| 1036 | 1119 | let dest = unsafe { &mut *dest }; |
| 1037 | 1120 | let source = unsafe { &*source }; |
| 1038 | 1121 | |
| 1039 | 1122 | // Check if shapes match. |
| 1040 | 1123 | let shapes_match = dest.rank == source.rank && { |
| 1041 | - (0..dest.rank as usize).all(|i| { | |
| 1042 | - dest.dims[i].extent() == source.dims[i].extent() | |
| 1043 | - }) | |
| 1124 | + (0..dest.rank as usize).all(|i| dest.dims[i].extent() == source.dims[i].extent()) | |
| 1044 | 1125 | }; |
| 1045 | 1126 | |
| 1046 | 1127 | if !shapes_match || !dest.is_allocated() { |
| 1047 | 1128 | // Deallocate dest if allocated. |
| 1048 | 1129 | if dest.is_allocated() && !dest.base_addr.is_null() { |
| 1049 | - unsafe { libc_free(dest.base_addr); } | |
| 1130 | + unsafe { | |
| 1131 | + libc_free(dest.base_addr); | |
| 1132 | + } | |
| 1050 | 1133 | dest.base_addr = ptr::null_mut(); |
| 1051 | 1134 | dest.flags &= !DESC_ALLOCATED; |
| 1052 | 1135 | } |
@@ -1087,18 +1170,19 @@ pub extern "C" fn afs_assign_allocatable( | ||
| 1087 | 1170 | /// `to` is deallocated if allocated, then receives `from`'s descriptor. |
| 1088 | 1171 | /// `from` is cleared (becomes unallocated). |
| 1089 | 1172 | #[no_mangle] |
| 1090 | -pub extern "C" fn afs_move_alloc( | |
| 1091 | - from: *mut ArrayDescriptor, | |
| 1092 | - to: *mut ArrayDescriptor, | |
| 1093 | -) { | |
| 1094 | - if from.is_null() || to.is_null() { return; } | |
| 1173 | +pub extern "C" fn afs_move_alloc(from: *mut ArrayDescriptor, to: *mut ArrayDescriptor) { | |
| 1174 | + if from.is_null() || to.is_null() { | |
| 1175 | + return; | |
| 1176 | + } | |
| 1095 | 1177 | |
| 1096 | 1178 | let from_desc = unsafe { &mut *from }; |
| 1097 | 1179 | let to_desc = unsafe { &mut *to }; |
| 1098 | 1180 | |
| 1099 | 1181 | // Deallocate `to` if allocated. |
| 1100 | 1182 | if to_desc.is_allocated() && !to_desc.base_addr.is_null() { |
| 1101 | - unsafe { libc_free(to_desc.base_addr); } | |
| 1183 | + unsafe { | |
| 1184 | + libc_free(to_desc.base_addr); | |
| 1185 | + } | |
| 1102 | 1186 | } |
| 1103 | 1187 | |
| 1104 | 1188 | // Copy descriptor from `from` to `to`. |
@@ -1114,7 +1198,9 @@ pub extern "C" fn afs_move_alloc( | ||
| 1114 | 1198 | /// Check if an array is allocated. Returns 1 (true) or 0 (false). |
| 1115 | 1199 | #[no_mangle] |
| 1116 | 1200 | pub extern "C" fn afs_allocated(desc: *const ArrayDescriptor) -> i32 { |
| 1117 | - if desc.is_null() { return 0; } | |
| 1201 | + if desc.is_null() { | |
| 1202 | + return 0; | |
| 1203 | + } | |
| 1118 | 1204 | unsafe { (*desc).is_allocated() as i32 } |
| 1119 | 1205 | } |
| 1120 | 1206 | |
@@ -1140,7 +1226,9 @@ pub extern "C" fn afs_create_section( | ||
| 1140 | 1226 | specs: *const SectionSpec, |
| 1141 | 1227 | n_dims: i32, |
| 1142 | 1228 | ) { |
| 1143 | - if source.is_null() || result.is_null() || specs.is_null() { return; } | |
| 1229 | + if source.is_null() || result.is_null() || specs.is_null() { | |
| 1230 | + return; | |
| 1231 | + } | |
| 1144 | 1232 | |
| 1145 | 1233 | let source = unsafe { &*source }; |
| 1146 | 1234 | let result = unsafe { &mut *result }; |
@@ -1149,7 +1237,7 @@ pub extern "C" fn afs_create_section( | ||
| 1149 | 1237 | result.elem_size = source.elem_size; |
| 1150 | 1238 | result.rank = n_dims; |
| 1151 | 1239 | result.flags = DESC_CONTIGUOUS; // sections may not be contiguous |
| 1152 | - // Don't set DESC_ALLOCATED — section doesn't own the data. | |
| 1240 | + // Don't set DESC_ALLOCATED — section doesn't own the data. | |
| 1153 | 1241 | |
| 1154 | 1242 | // Compute base address offset and new dims. |
| 1155 | 1243 | let mut byte_offset: i64 = 0; |
@@ -1168,7 +1256,8 @@ pub extern "C" fn afs_create_section( | ||
| 1168 | 1256 | let extent = if spec.stride == 0 { |
| 1169 | 1257 | 1 |
| 1170 | 1258 | } else if (spec.stride > 0 && spec.start > spec.end) |
| 1171 | - || (spec.stride < 0 && spec.start < spec.end) { | |
| 1259 | + || (spec.stride < 0 && spec.start < spec.end) | |
| 1260 | + { | |
| 1172 | 1261 | 0 // empty section |
| 1173 | 1262 | } else { |
| 1174 | 1263 | (spec.end - spec.start) / spec.stride + 1 |
@@ -1234,8 +1323,16 @@ mod tests { | ||
| 1234 | 1323 | fn allocate_2d() { |
| 1235 | 1324 | let mut desc = ArrayDescriptor::zeroed(); |
| 1236 | 1325 | let dims = [ |
| 1237 | - DimDescriptor { lower_bound: 1, upper_bound: 3, stride: 1 }, | |
| 1238 | - DimDescriptor { lower_bound: 1, upper_bound: 4, stride: 1 }, | |
| 1326 | + DimDescriptor { | |
| 1327 | + lower_bound: 1, | |
| 1328 | + upper_bound: 3, | |
| 1329 | + stride: 1, | |
| 1330 | + }, | |
| 1331 | + DimDescriptor { | |
| 1332 | + lower_bound: 1, | |
| 1333 | + upper_bound: 4, | |
| 1334 | + stride: 1, | |
| 1335 | + }, | |
| 1239 | 1336 | ]; |
| 1240 | 1337 | afs_allocate_array(&mut desc, 8, 2, dims.as_ptr(), ptr::null_mut()); |
| 1241 | 1338 | assert!(desc.is_allocated()); |
@@ -1250,7 +1347,11 @@ mod tests { | ||
| 1250 | 1347 | let mut stat: i32 = -1; |
| 1251 | 1348 | afs_allocate_1d(&mut desc, 4, 10); |
| 1252 | 1349 | // Allocating again should fail with stat. |
| 1253 | - let dim = DimDescriptor { lower_bound: 1, upper_bound: 10, stride: 1 }; | |
| 1350 | + let dim = DimDescriptor { | |
| 1351 | + lower_bound: 1, | |
| 1352 | + upper_bound: 10, | |
| 1353 | + stride: 1, | |
| 1354 | + }; | |
| 1254 | 1355 | afs_allocate_array(&mut desc, 4, 1, &dim, &mut stat); |
| 1255 | 1356 | assert_eq!(stat, 2); // already allocated |
| 1256 | 1357 | afs_deallocate_array(&mut desc, ptr::null_mut()); |
@@ -1333,7 +1434,12 @@ mod tests { | ||
| 1333 | 1434 | let lhs = [1_i32, 2, 3, 4, 5, 6, 7, 8]; |
| 1334 | 1435 | let rhs = [10_i32, 20, 30, 40, 50, 60, 70, 80]; |
| 1335 | 1436 | let mut out = [0_i32; 8]; |
| 1336 | - afs_array_add_i32(out.as_mut_ptr(), lhs.as_ptr(), rhs.as_ptr(), out.len() as i64); | |
| 1437 | + afs_array_add_i32( | |
| 1438 | + out.as_mut_ptr(), | |
| 1439 | + lhs.as_ptr(), | |
| 1440 | + rhs.as_ptr(), | |
| 1441 | + out.len() as i64, | |
| 1442 | + ); | |
| 1337 | 1443 | assert_eq!(out, [11, 22, 33, 44, 55, 66, 77, 88]); |
| 1338 | 1444 | } |
| 1339 | 1445 | |
@@ -1342,7 +1448,12 @@ mod tests { | ||
| 1342 | 1448 | let lhs = [1.5_f64, 2.5, 3.5, 4.5]; |
| 1343 | 1449 | let rhs = [10.0_f64, 20.0, 30.0, 40.0]; |
| 1344 | 1450 | let mut out = [0.0_f64; 4]; |
| 1345 | - afs_array_add_f64(out.as_mut_ptr(), lhs.as_ptr(), rhs.as_ptr(), out.len() as i64); | |
| 1451 | + afs_array_add_f64( | |
| 1452 | + out.as_mut_ptr(), | |
| 1453 | + lhs.as_ptr(), | |
| 1454 | + rhs.as_ptr(), | |
| 1455 | + out.len() as i64, | |
| 1456 | + ); | |
| 1346 | 1457 | assert_eq!(out, [11.5, 22.5, 33.5, 44.5]); |
| 1347 | 1458 | } |
| 1348 | 1459 | |
@@ -1351,7 +1462,12 @@ mod tests { | ||
| 1351 | 1462 | let lhs = [11_i32, 22, 33, 44, 55, 66, 77, 88]; |
| 1352 | 1463 | let rhs = [1_i32, 2, 3, 4, 5, 6, 7, 8]; |
| 1353 | 1464 | let mut out = [0_i32; 8]; |
| 1354 | - afs_array_sub_i32(out.as_mut_ptr(), lhs.as_ptr(), rhs.as_ptr(), out.len() as i64); | |
| 1465 | + afs_array_sub_i32( | |
| 1466 | + out.as_mut_ptr(), | |
| 1467 | + lhs.as_ptr(), | |
| 1468 | + rhs.as_ptr(), | |
| 1469 | + out.len() as i64, | |
| 1470 | + ); | |
| 1355 | 1471 | assert_eq!(out, [10, 20, 30, 40, 50, 60, 70, 80]); |
| 1356 | 1472 | } |
| 1357 | 1473 | |
@@ -1360,7 +1476,12 @@ mod tests { | ||
| 1360 | 1476 | let lhs = [1.0_f32, 2.0, 3.0, 4.0]; |
| 1361 | 1477 | let rhs = [2.0_f32, 3.0, 4.0, 5.0]; |
| 1362 | 1478 | let mut out = [0.0_f32; 4]; |
| 1363 | - afs_array_mul_f32(out.as_mut_ptr(), lhs.as_ptr(), rhs.as_ptr(), out.len() as i64); | |
| 1479 | + afs_array_mul_f32( | |
| 1480 | + out.as_mut_ptr(), | |
| 1481 | + lhs.as_ptr(), | |
| 1482 | + rhs.as_ptr(), | |
| 1483 | + out.len() as i64, | |
| 1484 | + ); | |
| 1364 | 1485 | assert_eq!(out, [2.0, 6.0, 12.0, 20.0]); |
| 1365 | 1486 | } |
| 1366 | 1487 | |
@@ -1394,43 +1515,63 @@ mod tests { | ||
| 1394 | 1515 | /// SIZE(array) — total number of elements. |
| 1395 | 1516 | #[no_mangle] |
| 1396 | 1517 | pub extern "C" fn afs_array_size(desc: *const ArrayDescriptor) -> i64 { |
| 1397 | - if desc.is_null() { return 0; } | |
| 1518 | + if desc.is_null() { | |
| 1519 | + return 0; | |
| 1520 | + } | |
| 1398 | 1521 | unsafe { (*desc).total_elements() } |
| 1399 | 1522 | } |
| 1400 | 1523 | |
| 1401 | 1524 | /// SIZE(array, dim) — number of elements along dimension `dim` (1-based). |
| 1402 | 1525 | #[no_mangle] |
| 1403 | 1526 | pub extern "C" fn afs_array_size_dim(desc: *const ArrayDescriptor, dim: i32) -> i64 { |
| 1404 | - if desc.is_null() || dim < 1 { return 0; } | |
| 1527 | + if desc.is_null() || dim < 1 { | |
| 1528 | + return 0; | |
| 1529 | + } | |
| 1405 | 1530 | let d = unsafe { &*desc }; |
| 1406 | 1531 | let idx = (dim - 1) as usize; |
| 1407 | 1532 | if idx < d.rank as usize { |
| 1408 | 1533 | d.dims[idx].extent() |
| 1409 | - } else { 0 } | |
| 1534 | + } else { | |
| 1535 | + 0 | |
| 1536 | + } | |
| 1410 | 1537 | } |
| 1411 | 1538 | |
| 1412 | 1539 | /// LBOUND(array, dim) — lower bound along dimension `dim` (1-based). |
| 1413 | 1540 | #[no_mangle] |
| 1414 | 1541 | pub extern "C" fn afs_array_lbound(desc: *const ArrayDescriptor, dim: i32) -> i64 { |
| 1415 | - if desc.is_null() || dim < 1 { return 1; } | |
| 1542 | + if desc.is_null() || dim < 1 { | |
| 1543 | + return 1; | |
| 1544 | + } | |
| 1416 | 1545 | let d = unsafe { &*desc }; |
| 1417 | 1546 | let idx = (dim - 1) as usize; |
| 1418 | - if idx < d.rank as usize { d.dims[idx].lower_bound } else { 1 } | |
| 1547 | + if idx < d.rank as usize { | |
| 1548 | + d.dims[idx].lower_bound | |
| 1549 | + } else { | |
| 1550 | + 1 | |
| 1551 | + } | |
| 1419 | 1552 | } |
| 1420 | 1553 | |
| 1421 | 1554 | /// UBOUND(array, dim) — upper bound along dimension `dim` (1-based). |
| 1422 | 1555 | #[no_mangle] |
| 1423 | 1556 | pub extern "C" fn afs_array_ubound(desc: *const ArrayDescriptor, dim: i32) -> i64 { |
| 1424 | - if desc.is_null() || dim < 1 { return 0; } | |
| 1557 | + if desc.is_null() || dim < 1 { | |
| 1558 | + return 0; | |
| 1559 | + } | |
| 1425 | 1560 | let d = unsafe { &*desc }; |
| 1426 | 1561 | let idx = (dim - 1) as usize; |
| 1427 | - if idx < d.rank as usize { d.dims[idx].upper_bound } else { 0 } | |
| 1562 | + if idx < d.rank as usize { | |
| 1563 | + d.dims[idx].upper_bound | |
| 1564 | + } else { | |
| 1565 | + 0 | |
| 1566 | + } | |
| 1428 | 1567 | } |
| 1429 | 1568 | |
| 1430 | 1569 | /// ALLOCATED(array) — check if array is allocated (returns 1 or 0). |
| 1431 | 1570 | #[no_mangle] |
| 1432 | 1571 | pub extern "C" fn afs_array_allocated(desc: *const ArrayDescriptor) -> i32 { |
| 1433 | - if desc.is_null() { return 0; } | |
| 1572 | + if desc.is_null() { | |
| 1573 | + return 0; | |
| 1574 | + } | |
| 1434 | 1575 | unsafe { (*desc).is_allocated() as i32 } |
| 1435 | 1576 | } |
| 1436 | 1577 | |
@@ -1438,9 +1579,13 @@ pub extern "C" fn afs_array_allocated(desc: *const ArrayDescriptor) -> i32 { | ||
| 1438 | 1579 | /// Respects strides for non-contiguous sections. |
| 1439 | 1580 | #[no_mangle] |
| 1440 | 1581 | pub extern "C" fn afs_array_sum_real8(desc: *const ArrayDescriptor) -> f64 { |
| 1441 | - if desc.is_null() { return 0.0; } | |
| 1582 | + if desc.is_null() { | |
| 1583 | + return 0.0; | |
| 1584 | + } | |
| 1442 | 1585 | let d = unsafe { &*desc }; |
| 1443 | - if d.base_addr.is_null() { return 0.0; } | |
| 1586 | + if d.base_addr.is_null() { | |
| 1587 | + return 0.0; | |
| 1588 | + } | |
| 1444 | 1589 | let n = d.total_elements() as usize; |
| 1445 | 1590 | let stride = d.dims[0].stride.max(1) as usize; |
| 1446 | 1591 | let ptr = d.base_addr as *const f64; |
@@ -1455,9 +1600,13 @@ pub extern "C" fn afs_array_sum_real8(desc: *const ArrayDescriptor) -> f64 { | ||
| 1455 | 1600 | /// Respects strides for non-contiguous sections. |
| 1456 | 1601 | #[no_mangle] |
| 1457 | 1602 | pub extern "C" fn afs_array_sum_int(desc: *const ArrayDescriptor) -> i64 { |
| 1458 | - if desc.is_null() { return 0; } | |
| 1603 | + if desc.is_null() { | |
| 1604 | + return 0; | |
| 1605 | + } | |
| 1459 | 1606 | let d = unsafe { &*desc }; |
| 1460 | - if d.base_addr.is_null() { return 0; } | |
| 1607 | + if d.base_addr.is_null() { | |
| 1608 | + return 0; | |
| 1609 | + } | |
| 1461 | 1610 | let n = d.total_elements() as usize; |
| 1462 | 1611 | let stride = d.dims[0].stride.max(1) as usize; |
| 1463 | 1612 | let ptr = d.base_addr as *const i32; |
@@ -1471,9 +1620,13 @@ pub extern "C" fn afs_array_sum_int(desc: *const ArrayDescriptor) -> i64 { | ||
| 1471 | 1620 | /// PRODUCT(array) — product of all elements (real(8) version). |
| 1472 | 1621 | #[no_mangle] |
| 1473 | 1622 | pub extern "C" fn afs_array_product_real8(desc: *const ArrayDescriptor) -> f64 { |
| 1474 | - if desc.is_null() { return 1.0; } | |
| 1623 | + if desc.is_null() { | |
| 1624 | + return 1.0; | |
| 1625 | + } | |
| 1475 | 1626 | let d = unsafe { &*desc }; |
| 1476 | - if d.base_addr.is_null() { return 1.0; } | |
| 1627 | + if d.base_addr.is_null() { | |
| 1628 | + return 1.0; | |
| 1629 | + } | |
| 1477 | 1630 | let n = d.total_elements() as usize; |
| 1478 | 1631 | let stride = d.dims[0].stride.max(1) as usize; |
| 1479 | 1632 | let ptr = d.base_addr as *const f64; |
@@ -1487,11 +1640,17 @@ pub extern "C" fn afs_array_product_real8(desc: *const ArrayDescriptor) -> f64 { | ||
| 1487 | 1640 | /// PRODUCT(array) — product of all elements (integer(4) version). |
| 1488 | 1641 | #[no_mangle] |
| 1489 | 1642 | pub extern "C" fn afs_array_product_int(desc: *const ArrayDescriptor) -> i64 { |
| 1490 | - if desc.is_null() { return 1; } | |
| 1643 | + if desc.is_null() { | |
| 1644 | + return 1; | |
| 1645 | + } | |
| 1491 | 1646 | let d = unsafe { &*desc }; |
| 1492 | - if d.base_addr.is_null() { return 1; } | |
| 1647 | + if d.base_addr.is_null() { | |
| 1648 | + return 1; | |
| 1649 | + } | |
| 1493 | 1650 | let n = d.total_elements() as usize; |
| 1494 | - if n == 0 { return 1; } | |
| 1651 | + if n == 0 { | |
| 1652 | + return 1; | |
| 1653 | + } | |
| 1495 | 1654 | let stride = d.dims[0].stride.max(1) as usize; |
| 1496 | 1655 | let ptr = d.base_addr as *const i32; |
| 1497 | 1656 | let mut prod: i64 = 1; |
@@ -1504,17 +1663,25 @@ pub extern "C" fn afs_array_product_int(desc: *const ArrayDescriptor) -> i64 { | ||
| 1504 | 1663 | /// MAXVAL(array) — maximum element (real(8) version). Respects strides. |
| 1505 | 1664 | #[no_mangle] |
| 1506 | 1665 | pub extern "C" fn afs_array_maxval_real8(desc: *const ArrayDescriptor) -> f64 { |
| 1507 | - if desc.is_null() { return f64::NEG_INFINITY; } | |
| 1666 | + if desc.is_null() { | |
| 1667 | + return f64::NEG_INFINITY; | |
| 1668 | + } | |
| 1508 | 1669 | let d = unsafe { &*desc }; |
| 1509 | - if d.base_addr.is_null() { return f64::NEG_INFINITY; } | |
| 1670 | + if d.base_addr.is_null() { | |
| 1671 | + return f64::NEG_INFINITY; | |
| 1672 | + } | |
| 1510 | 1673 | let n = d.total_elements() as usize; |
| 1511 | - if n == 0 { return f64::NEG_INFINITY; } | |
| 1674 | + if n == 0 { | |
| 1675 | + return f64::NEG_INFINITY; | |
| 1676 | + } | |
| 1512 | 1677 | let stride = d.dims[0].stride.max(1) as usize; |
| 1513 | 1678 | let ptr = d.base_addr as *const f64; |
| 1514 | 1679 | let mut max = unsafe { *ptr }; |
| 1515 | 1680 | for i in 1..n { |
| 1516 | 1681 | let v = unsafe { *ptr.add(i * stride) }; |
| 1517 | - if v > max { max = v; } | |
| 1682 | + if v > max { | |
| 1683 | + max = v; | |
| 1684 | + } | |
| 1518 | 1685 | } |
| 1519 | 1686 | max |
| 1520 | 1687 | } |
@@ -1522,17 +1689,25 @@ pub extern "C" fn afs_array_maxval_real8(desc: *const ArrayDescriptor) -> f64 { | ||
| 1522 | 1689 | /// MINVAL(array) — minimum element (real(8) version). Respects strides. |
| 1523 | 1690 | #[no_mangle] |
| 1524 | 1691 | pub extern "C" fn afs_array_minval_real8(desc: *const ArrayDescriptor) -> f64 { |
| 1525 | - if desc.is_null() { return f64::INFINITY; } | |
| 1692 | + if desc.is_null() { | |
| 1693 | + return f64::INFINITY; | |
| 1694 | + } | |
| 1526 | 1695 | let d = unsafe { &*desc }; |
| 1527 | - if d.base_addr.is_null() { return f64::INFINITY; } | |
| 1696 | + if d.base_addr.is_null() { | |
| 1697 | + return f64::INFINITY; | |
| 1698 | + } | |
| 1528 | 1699 | let n = d.total_elements() as usize; |
| 1529 | - if n == 0 { return f64::INFINITY; } | |
| 1700 | + if n == 0 { | |
| 1701 | + return f64::INFINITY; | |
| 1702 | + } | |
| 1530 | 1703 | let stride = d.dims[0].stride.max(1) as usize; |
| 1531 | 1704 | let ptr = d.base_addr as *const f64; |
| 1532 | 1705 | let mut min = unsafe { *ptr }; |
| 1533 | 1706 | for i in 1..n { |
| 1534 | 1707 | let v = unsafe { *ptr.add(i * stride) }; |
| 1535 | - if v < min { min = v; } | |
| 1708 | + if v < min { | |
| 1709 | + min = v; | |
| 1710 | + } | |
| 1536 | 1711 | } |
| 1537 | 1712 | min |
| 1538 | 1713 | } |
@@ -1540,17 +1715,25 @@ pub extern "C" fn afs_array_minval_real8(desc: *const ArrayDescriptor) -> f64 { | ||
| 1540 | 1715 | /// MAXVAL(array) — maximum element (integer(4) version). Respects strides. |
| 1541 | 1716 | #[no_mangle] |
| 1542 | 1717 | pub extern "C" fn afs_array_maxval_int(desc: *const ArrayDescriptor) -> i32 { |
| 1543 | - if desc.is_null() { return i32::MIN; } | |
| 1718 | + if desc.is_null() { | |
| 1719 | + return i32::MIN; | |
| 1720 | + } | |
| 1544 | 1721 | let d = unsafe { &*desc }; |
| 1545 | - if d.base_addr.is_null() { return i32::MIN; } | |
| 1722 | + if d.base_addr.is_null() { | |
| 1723 | + return i32::MIN; | |
| 1724 | + } | |
| 1546 | 1725 | let n = d.total_elements() as usize; |
| 1547 | - if n == 0 { return i32::MIN; } | |
| 1726 | + if n == 0 { | |
| 1727 | + return i32::MIN; | |
| 1728 | + } | |
| 1548 | 1729 | let stride = d.dims[0].stride.max(1) as usize; |
| 1549 | 1730 | let ptr = d.base_addr as *const i32; |
| 1550 | 1731 | let mut max = unsafe { *ptr }; |
| 1551 | 1732 | for i in 1..n { |
| 1552 | 1733 | let v = unsafe { *ptr.add(i * stride) }; |
| 1553 | - if v > max { max = v; } | |
| 1734 | + if v > max { | |
| 1735 | + max = v; | |
| 1736 | + } | |
| 1554 | 1737 | } |
| 1555 | 1738 | max |
| 1556 | 1739 | } |
@@ -1558,17 +1741,25 @@ pub extern "C" fn afs_array_maxval_int(desc: *const ArrayDescriptor) -> i32 { | ||
| 1558 | 1741 | /// MINVAL(array) — minimum element (integer(4) version). Respects strides. |
| 1559 | 1742 | #[no_mangle] |
| 1560 | 1743 | pub extern "C" fn afs_array_minval_int(desc: *const ArrayDescriptor) -> i32 { |
| 1561 | - if desc.is_null() { return i32::MAX; } | |
| 1744 | + if desc.is_null() { | |
| 1745 | + return i32::MAX; | |
| 1746 | + } | |
| 1562 | 1747 | let d = unsafe { &*desc }; |
| 1563 | - if d.base_addr.is_null() { return i32::MAX; } | |
| 1748 | + if d.base_addr.is_null() { | |
| 1749 | + return i32::MAX; | |
| 1750 | + } | |
| 1564 | 1751 | let n = d.total_elements() as usize; |
| 1565 | - if n == 0 { return i32::MAX; } | |
| 1752 | + if n == 0 { | |
| 1753 | + return i32::MAX; | |
| 1754 | + } | |
| 1566 | 1755 | let stride = d.dims[0].stride.max(1) as usize; |
| 1567 | 1756 | let ptr = d.base_addr as *const i32; |
| 1568 | 1757 | let mut min = unsafe { *ptr }; |
| 1569 | 1758 | for i in 1..n { |
| 1570 | 1759 | let v = unsafe { *ptr.add(i * stride) }; |
| 1571 | - if v < min { min = v; } | |
| 1760 | + if v < min { | |
| 1761 | + min = v; | |
| 1762 | + } | |
| 1572 | 1763 | } |
| 1573 | 1764 | min |
| 1574 | 1765 | } |
@@ -1580,9 +1771,13 @@ pub extern "C" fn afs_transpose_real8( | ||
| 1580 | 1771 | source: *const ArrayDescriptor, |
| 1581 | 1772 | result: *mut ArrayDescriptor, |
| 1582 | 1773 | ) { |
| 1583 | - if source.is_null() || result.is_null() { return; } | |
| 1774 | + if source.is_null() || result.is_null() { | |
| 1775 | + return; | |
| 1776 | + } | |
| 1584 | 1777 | let src = unsafe { &*source }; |
| 1585 | - if src.rank < 2 || src.base_addr.is_null() { return; } | |
| 1778 | + if src.rank < 2 || src.base_addr.is_null() { | |
| 1779 | + return; | |
| 1780 | + } | |
| 1586 | 1781 | |
| 1587 | 1782 | let m = src.dims[0].extent() as usize; |
| 1588 | 1783 | let n = src.dims[1].extent() as usize; |
@@ -1592,13 +1787,23 @@ pub extern "C" fn afs_transpose_real8( | ||
| 1592 | 1787 | afs_allocate_1d(result, 8, (n * m) as i64); |
| 1593 | 1788 | let res = unsafe { &mut *result }; |
| 1594 | 1789 | res.rank = 2; |
| 1595 | - res.dims[0] = DimDescriptor { lower_bound: 1, upper_bound: n as i64, stride: 1 }; | |
| 1596 | - res.dims[1] = DimDescriptor { lower_bound: 1, upper_bound: m as i64, stride: 1 }; | |
| 1790 | + res.dims[0] = DimDescriptor { | |
| 1791 | + lower_bound: 1, | |
| 1792 | + upper_bound: n as i64, | |
| 1793 | + stride: 1, | |
| 1794 | + }; | |
| 1795 | + res.dims[1] = DimDescriptor { | |
| 1796 | + lower_bound: 1, | |
| 1797 | + upper_bound: m as i64, | |
| 1798 | + stride: 1, | |
| 1799 | + }; | |
| 1597 | 1800 | let rp = res.base_addr as *mut f64; |
| 1598 | 1801 | |
| 1599 | 1802 | for i in 0..m { |
| 1600 | 1803 | for j in 0..n { |
| 1601 | - unsafe { *rp.add(j * m + i) = *sp.add(i * n + j); } | |
| 1804 | + unsafe { | |
| 1805 | + *rp.add(j * m + i) = *sp.add(i * n + j); | |
| 1806 | + } | |
| 1602 | 1807 | } |
| 1603 | 1808 | } |
| 1604 | 1809 | } |
@@ -1611,14 +1816,26 @@ pub extern "C" fn afs_matmul_real8( | ||
| 1611 | 1816 | b: *const ArrayDescriptor, |
| 1612 | 1817 | result: *mut ArrayDescriptor, |
| 1613 | 1818 | ) { |
| 1614 | - if a.is_null() || b.is_null() || result.is_null() { return; } | |
| 1819 | + if a.is_null() || b.is_null() || result.is_null() { | |
| 1820 | + return; | |
| 1821 | + } | |
| 1615 | 1822 | let da = unsafe { &*a }; |
| 1616 | 1823 | let db = unsafe { &*b }; |
| 1617 | - if da.base_addr.is_null() || db.base_addr.is_null() { return; } | |
| 1824 | + if da.base_addr.is_null() || db.base_addr.is_null() { | |
| 1825 | + return; | |
| 1826 | + } | |
| 1618 | 1827 | |
| 1619 | 1828 | let m = da.dims[0].extent() as usize; |
| 1620 | - let k = if da.rank >= 2 { da.dims[1].extent() as usize } else { 1 }; | |
| 1621 | - let n = if db.rank >= 2 { db.dims[1].extent() as usize } else { db.dims[0].extent() as usize }; | |
| 1829 | + let k = if da.rank >= 2 { | |
| 1830 | + da.dims[1].extent() as usize | |
| 1831 | + } else { | |
| 1832 | + 1 | |
| 1833 | + }; | |
| 1834 | + let n = if db.rank >= 2 { | |
| 1835 | + db.dims[1].extent() as usize | |
| 1836 | + } else { | |
| 1837 | + db.dims[0].extent() as usize | |
| 1838 | + }; | |
| 1622 | 1839 | |
| 1623 | 1840 | // For vector * matrix or matrix * vector, adjust dimensions. |
| 1624 | 1841 | let ap = da.base_addr as *const f64; |
@@ -1628,8 +1845,16 @@ pub extern "C" fn afs_matmul_real8( | ||
| 1628 | 1845 | afs_allocate_1d(result, 8, (m * n) as i64); |
| 1629 | 1846 | let res = unsafe { &mut *result }; |
| 1630 | 1847 | res.rank = 2; |
| 1631 | - res.dims[0] = DimDescriptor { lower_bound: 1, upper_bound: m as i64, stride: 1 }; | |
| 1632 | - res.dims[1] = DimDescriptor { lower_bound: 1, upper_bound: n as i64, stride: 1 }; | |
| 1848 | + res.dims[0] = DimDescriptor { | |
| 1849 | + lower_bound: 1, | |
| 1850 | + upper_bound: m as i64, | |
| 1851 | + stride: 1, | |
| 1852 | + }; | |
| 1853 | + res.dims[1] = DimDescriptor { | |
| 1854 | + lower_bound: 1, | |
| 1855 | + upper_bound: n as i64, | |
| 1856 | + stride: 1, | |
| 1857 | + }; | |
| 1633 | 1858 | let rp = res.base_addr as *mut f64; |
| 1634 | 1859 | |
| 1635 | 1860 | // Triple loop: C(i,j) = sum_l A(i,l) * B(l,j) |
@@ -1641,7 +1866,9 @@ pub extern "C" fn afs_matmul_real8( | ||
| 1641 | 1866 | let b_val = unsafe { *bp.add(l * n + j) }; |
| 1642 | 1867 | sum += a_val * b_val; |
| 1643 | 1868 | } |
| 1644 | - unsafe { *rp.add(i * n + j) = sum; } | |
| 1869 | + unsafe { | |
| 1870 | + *rp.add(i * n + j) = sum; | |
| 1871 | + } | |
| 1645 | 1872 | } |
| 1646 | 1873 | } |
| 1647 | 1874 | } |
@@ -1653,14 +1880,26 @@ pub extern "C" fn afs_matmul_int( | ||
| 1653 | 1880 | b: *const ArrayDescriptor, |
| 1654 | 1881 | result: *mut ArrayDescriptor, |
| 1655 | 1882 | ) { |
| 1656 | - if a.is_null() || b.is_null() || result.is_null() { return; } | |
| 1883 | + if a.is_null() || b.is_null() || result.is_null() { | |
| 1884 | + return; | |
| 1885 | + } | |
| 1657 | 1886 | let da = unsafe { &*a }; |
| 1658 | 1887 | let db = unsafe { &*b }; |
| 1659 | - if da.base_addr.is_null() || db.base_addr.is_null() { return; } | |
| 1888 | + if da.base_addr.is_null() || db.base_addr.is_null() { | |
| 1889 | + return; | |
| 1890 | + } | |
| 1660 | 1891 | |
| 1661 | 1892 | let m = da.dims[0].extent() as usize; |
| 1662 | - let k = if da.rank >= 2 { da.dims[1].extent() as usize } else { 1 }; | |
| 1663 | - let n = if db.rank >= 2 { db.dims[1].extent() as usize } else { db.dims[0].extent() as usize }; | |
| 1893 | + let k = if da.rank >= 2 { | |
| 1894 | + da.dims[1].extent() as usize | |
| 1895 | + } else { | |
| 1896 | + 1 | |
| 1897 | + }; | |
| 1898 | + let n = if db.rank >= 2 { | |
| 1899 | + db.dims[1].extent() as usize | |
| 1900 | + } else { | |
| 1901 | + db.dims[0].extent() as usize | |
| 1902 | + }; | |
| 1664 | 1903 | |
| 1665 | 1904 | let ap = da.base_addr as *const i32; |
| 1666 | 1905 | let bp = db.base_addr as *const i32; |
@@ -1668,8 +1907,16 @@ pub extern "C" fn afs_matmul_int( | ||
| 1668 | 1907 | afs_allocate_1d(result, 4, (m * n) as i64); |
| 1669 | 1908 | let res = unsafe { &mut *result }; |
| 1670 | 1909 | res.rank = 2; |
| 1671 | - res.dims[0] = DimDescriptor { lower_bound: 1, upper_bound: m as i64, stride: 1 }; | |
| 1672 | - res.dims[1] = DimDescriptor { lower_bound: 1, upper_bound: n as i64, stride: 1 }; | |
| 1910 | + res.dims[0] = DimDescriptor { | |
| 1911 | + lower_bound: 1, | |
| 1912 | + upper_bound: m as i64, | |
| 1913 | + stride: 1, | |
| 1914 | + }; | |
| 1915 | + res.dims[1] = DimDescriptor { | |
| 1916 | + lower_bound: 1, | |
| 1917 | + upper_bound: n as i64, | |
| 1918 | + stride: 1, | |
| 1919 | + }; | |
| 1673 | 1920 | let rp = res.base_addr as *mut i32; |
| 1674 | 1921 | |
| 1675 | 1922 | for i in 0..m { |
@@ -1680,20 +1927,23 @@ pub extern "C" fn afs_matmul_int( | ||
| 1680 | 1927 | let b_val = unsafe { *bp.add(l * n + j) as i64 }; |
| 1681 | 1928 | sum += a_val * b_val; |
| 1682 | 1929 | } |
| 1683 | - unsafe { *rp.add(i * n + j) = sum as i32; } | |
| 1930 | + unsafe { | |
| 1931 | + *rp.add(i * n + j) = sum as i32; | |
| 1932 | + } | |
| 1684 | 1933 | } |
| 1685 | 1934 | } |
| 1686 | 1935 | } |
| 1687 | 1936 | |
| 1688 | 1937 | /// TRANSPOSE(source, result) — matrix transpose (integer(4) version). |
| 1689 | 1938 | #[no_mangle] |
| 1690 | -pub extern "C" fn afs_transpose_int( | |
| 1691 | - source: *const ArrayDescriptor, | |
| 1692 | - result: *mut ArrayDescriptor, | |
| 1693 | -) { | |
| 1694 | - if source.is_null() || result.is_null() { return; } | |
| 1939 | +pub extern "C" fn afs_transpose_int(source: *const ArrayDescriptor, result: *mut ArrayDescriptor) { | |
| 1940 | + if source.is_null() || result.is_null() { | |
| 1941 | + return; | |
| 1942 | + } | |
| 1695 | 1943 | let src = unsafe { &*source }; |
| 1696 | - if src.rank < 2 || src.base_addr.is_null() { return; } | |
| 1944 | + if src.rank < 2 || src.base_addr.is_null() { | |
| 1945 | + return; | |
| 1946 | + } | |
| 1697 | 1947 | |
| 1698 | 1948 | let m = src.dims[0].extent() as usize; |
| 1699 | 1949 | let n = src.dims[1].extent() as usize; |
@@ -1702,13 +1952,23 @@ pub extern "C" fn afs_transpose_int( | ||
| 1702 | 1952 | afs_allocate_1d(result, 4, (n * m) as i64); |
| 1703 | 1953 | let res = unsafe { &mut *result }; |
| 1704 | 1954 | res.rank = 2; |
| 1705 | - res.dims[0] = DimDescriptor { lower_bound: 1, upper_bound: n as i64, stride: 1 }; | |
| 1706 | - res.dims[1] = DimDescriptor { lower_bound: 1, upper_bound: m as i64, stride: 1 }; | |
| 1955 | + res.dims[0] = DimDescriptor { | |
| 1956 | + lower_bound: 1, | |
| 1957 | + upper_bound: n as i64, | |
| 1958 | + stride: 1, | |
| 1959 | + }; | |
| 1960 | + res.dims[1] = DimDescriptor { | |
| 1961 | + lower_bound: 1, | |
| 1962 | + upper_bound: m as i64, | |
| 1963 | + stride: 1, | |
| 1964 | + }; | |
| 1707 | 1965 | let rp = res.base_addr as *mut i32; |
| 1708 | 1966 | |
| 1709 | 1967 | for i in 0..m { |
| 1710 | 1968 | for j in 0..n { |
| 1711 | - unsafe { *rp.add(j * m + i) = *sp.add(i * n + j); } | |
| 1969 | + unsafe { | |
| 1970 | + *rp.add(j * m + i) = *sp.add(i * n + j); | |
| 1971 | + } | |
| 1712 | 1972 | } |
| 1713 | 1973 | } |
| 1714 | 1974 | } |
@@ -1720,10 +1980,14 @@ pub extern "C" fn afs_dot_product_real8( | ||
| 1720 | 1980 | a: *const ArrayDescriptor, |
| 1721 | 1981 | b: *const ArrayDescriptor, |
| 1722 | 1982 | ) -> f64 { |
| 1723 | - if a.is_null() || b.is_null() { return 0.0; } | |
| 1983 | + if a.is_null() || b.is_null() { | |
| 1984 | + return 0.0; | |
| 1985 | + } | |
| 1724 | 1986 | let da = unsafe { &*a }; |
| 1725 | 1987 | let db = unsafe { &*b }; |
| 1726 | - if da.base_addr.is_null() || db.base_addr.is_null() { return 0.0; } | |
| 1988 | + if da.base_addr.is_null() || db.base_addr.is_null() { | |
| 1989 | + return 0.0; | |
| 1990 | + } | |
| 1727 | 1991 | let n = da.dims[0].extent().min(db.dims[0].extent()) as usize; |
| 1728 | 1992 | let stride_a = da.dims[0].stride.max(1) as usize; |
| 1729 | 1993 | let stride_b = db.dims[0].stride.max(1) as usize; |
@@ -1742,10 +2006,14 @@ pub extern "C" fn afs_dot_product_real4( | ||
| 1742 | 2006 | a: *const ArrayDescriptor, |
| 1743 | 2007 | b: *const ArrayDescriptor, |
| 1744 | 2008 | ) -> f32 { |
| 1745 | - if a.is_null() || b.is_null() { return 0.0; } | |
| 2009 | + if a.is_null() || b.is_null() { | |
| 2010 | + return 0.0; | |
| 2011 | + } | |
| 1746 | 2012 | let da = unsafe { &*a }; |
| 1747 | 2013 | let db = unsafe { &*b }; |
| 1748 | - if da.base_addr.is_null() || db.base_addr.is_null() { return 0.0; } | |
| 2014 | + if da.base_addr.is_null() || db.base_addr.is_null() { | |
| 2015 | + return 0.0; | |
| 2016 | + } | |
| 1749 | 2017 | let n = da.dims[0].extent().min(db.dims[0].extent()) as usize; |
| 1750 | 2018 | let stride_a = da.dims[0].stride.max(1) as usize; |
| 1751 | 2019 | let stride_b = db.dims[0].stride.max(1) as usize; |
@@ -1760,14 +2028,15 @@ pub extern "C" fn afs_dot_product_real4( | ||
| 1760 | 2028 | |
| 1761 | 2029 | /// DOT_PRODUCT(a, b) — vector dot product (integer(4) version). |
| 1762 | 2030 | #[no_mangle] |
| 1763 | -pub extern "C" fn afs_dot_product_int( | |
| 1764 | - a: *const ArrayDescriptor, | |
| 1765 | - b: *const ArrayDescriptor, | |
| 1766 | -) -> i64 { | |
| 1767 | - if a.is_null() || b.is_null() { return 0; } | |
| 2031 | +pub extern "C" fn afs_dot_product_int(a: *const ArrayDescriptor, b: *const ArrayDescriptor) -> i64 { | |
| 2032 | + if a.is_null() || b.is_null() { | |
| 2033 | + return 0; | |
| 2034 | + } | |
| 1768 | 2035 | let da = unsafe { &*a }; |
| 1769 | 2036 | let db = unsafe { &*b }; |
| 1770 | - if da.base_addr.is_null() || db.base_addr.is_null() { return 0; } | |
| 2037 | + if da.base_addr.is_null() || db.base_addr.is_null() { | |
| 2038 | + return 0; | |
| 2039 | + } | |
| 1771 | 2040 | let n = da.dims[0].extent().min(db.dims[0].extent()) as usize; |
| 1772 | 2041 | let stride_a = da.dims[0].stride.max(1) as usize; |
| 1773 | 2042 | let stride_b = db.dims[0].stride.max(1) as usize; |
runtime/src/descriptor.rsmodified@@ -221,13 +221,25 @@ mod tests { | ||
| 221 | 221 | |
| 222 | 222 | #[test] |
| 223 | 223 | fn dim_extent() { |
| 224 | - let dim = DimDescriptor { lower_bound: 1, upper_bound: 10, stride: 1 }; | |
| 224 | + let dim = DimDescriptor { | |
| 225 | + lower_bound: 1, | |
| 226 | + upper_bound: 10, | |
| 227 | + stride: 1, | |
| 228 | + }; | |
| 225 | 229 | assert_eq!(dim.extent(), 10); |
| 226 | 230 | |
| 227 | - let dim2 = DimDescriptor { lower_bound: 1, upper_bound: 10, stride: 2 }; | |
| 231 | + let dim2 = DimDescriptor { | |
| 232 | + lower_bound: 1, | |
| 233 | + upper_bound: 10, | |
| 234 | + stride: 2, | |
| 235 | + }; | |
| 228 | 236 | assert_eq!(dim2.extent(), 5); |
| 229 | 237 | |
| 230 | - let dim3 = DimDescriptor { lower_bound: 5, upper_bound: 3, stride: 1 }; | |
| 238 | + let dim3 = DimDescriptor { | |
| 239 | + lower_bound: 5, | |
| 240 | + upper_bound: 3, | |
| 241 | + stride: 1, | |
| 242 | + }; | |
| 231 | 243 | assert_eq!(dim3.extent(), 0); // empty |
| 232 | 244 | } |
| 233 | 245 | |
@@ -256,7 +268,7 @@ mod tests { | ||
| 256 | 268 | let mut d = ArrayDescriptor::zeroed(); |
| 257 | 269 | d.elem_size = 8; // f64 |
| 258 | 270 | d.set_bounds(&[(1, 3), (1, 4)]); // 3x4 matrix |
| 259 | - // Column-major: a(1,1)=0, a(2,1)=8, a(3,1)=16, a(1,2)=24 | |
| 271 | + // Column-major: a(1,1)=0, a(2,1)=8, a(3,1)=16, a(1,2)=24 | |
| 260 | 272 | assert_eq!(d.element_offset(&[1, 1]), 0); |
| 261 | 273 | assert_eq!(d.element_offset(&[2, 1]), 8); |
| 262 | 274 | assert_eq!(d.element_offset(&[3, 1]), 16); |
runtime/src/format.rsmodified@@ -5,33 +5,64 @@ | ||
| 5 | 5 | //! Fortran standard set including repeat counts, group repeat, |
| 6 | 6 | //! unlimited repeat, scale factors, and all data/control descriptors. |
| 7 | 7 | |
| 8 | - | |
| 9 | 8 | /// A parsed format descriptor. |
| 10 | 9 | #[derive(Debug, Clone)] |
| 11 | 10 | pub enum FormatDesc { |
| 12 | 11 | // ---- Data edit descriptors ---- |
| 13 | 12 | /// I: integer. Iw or Iw.m (w=width, m=minimum digits). |
| 14 | - IntegerI { width: usize, min_digits: Option<usize> }, | |
| 13 | + IntegerI { | |
| 14 | + width: usize, | |
| 15 | + min_digits: Option<usize>, | |
| 16 | + }, | |
| 15 | 17 | /// B: binary integer. Bw or Bw.m. |
| 16 | - IntegerB { width: usize, min_digits: Option<usize> }, | |
| 18 | + IntegerB { | |
| 19 | + width: usize, | |
| 20 | + min_digits: Option<usize>, | |
| 21 | + }, | |
| 17 | 22 | /// O: octal integer. Ow or Ow.m. |
| 18 | - IntegerO { width: usize, min_digits: Option<usize> }, | |
| 23 | + IntegerO { | |
| 24 | + width: usize, | |
| 25 | + min_digits: Option<usize>, | |
| 26 | + }, | |
| 19 | 27 | /// Z: hexadecimal integer. Zw or Zw.m. |
| 20 | - IntegerZ { width: usize, min_digits: Option<usize> }, | |
| 28 | + IntegerZ { | |
| 29 | + width: usize, | |
| 30 | + min_digits: Option<usize>, | |
| 31 | + }, | |
| 21 | 32 | /// F: fixed-point real. Fw.d. |
| 22 | 33 | RealF { width: usize, decimals: usize }, |
| 23 | 34 | /// E: exponential real. Ew.d or Ew.dEe. |
| 24 | - RealE { width: usize, decimals: usize, exp_width: Option<usize> }, | |
| 35 | + RealE { | |
| 36 | + width: usize, | |
| 37 | + decimals: usize, | |
| 38 | + exp_width: Option<usize>, | |
| 39 | + }, | |
| 25 | 40 | /// EN: engineering notation. ENw.d or ENw.dEe. |
| 26 | - RealEN { width: usize, decimals: usize, exp_width: Option<usize> }, | |
| 41 | + RealEN { | |
| 42 | + width: usize, | |
| 43 | + decimals: usize, | |
| 44 | + exp_width: Option<usize>, | |
| 45 | + }, | |
| 27 | 46 | /// ES: scientific notation (1.0-9.999 mantissa). ESw.d or ESw.dEe. |
| 28 | - RealES { width: usize, decimals: usize, exp_width: Option<usize> }, | |
| 47 | + RealES { | |
| 48 | + width: usize, | |
| 49 | + decimals: usize, | |
| 50 | + exp_width: Option<usize>, | |
| 51 | + }, | |
| 29 | 52 | /// EX: hexadecimal-significand real. EXw.d or EXw.dEe. (F2018) |
| 30 | - RealEX { width: usize, decimals: usize, exp_width: Option<usize> }, | |
| 53 | + RealEX { | |
| 54 | + width: usize, | |
| 55 | + decimals: usize, | |
| 56 | + exp_width: Option<usize>, | |
| 57 | + }, | |
| 31 | 58 | /// D: double-precision exponential. Dw.d (same as Ew.d with D exponent letter). |
| 32 | 59 | RealD { width: usize, decimals: usize }, |
| 33 | 60 | /// G: generalized real. Gw.d or Gw.dEe. Chooses F or E format automatically. |
| 34 | - RealG { width: usize, decimals: usize, exp_width: Option<usize> }, | |
| 61 | + RealG { | |
| 62 | + width: usize, | |
| 63 | + decimals: usize, | |
| 64 | + exp_width: Option<usize>, | |
| 65 | + }, | |
| 35 | 66 | /// L: logical. Lw. |
| 36 | 67 | Logical { width: usize }, |
| 37 | 68 | /// A: character. A or Aw. |
@@ -69,7 +100,10 @@ pub enum FormatDesc { | ||
| 69 | 100 | |
| 70 | 101 | // ---- Grouping ---- |
| 71 | 102 | /// Repeated group: n(...). |
| 72 | - Group { repeat: usize, descriptors: Vec<FormatDesc> }, | |
| 103 | + Group { | |
| 104 | + repeat: usize, | |
| 105 | + descriptors: Vec<FormatDesc>, | |
| 106 | + }, | |
| 73 | 107 | /// Unlimited repeat: *(...). |
| 74 | 108 | UnlimitedRepeat { descriptors: Vec<FormatDesc> }, |
| 75 | 109 | } |
@@ -94,18 +128,18 @@ pub enum BlankInterpretation { | ||
| 94 | 128 | |
| 95 | 129 | #[derive(Debug, Clone, Copy)] |
| 96 | 130 | pub enum RoundMode { |
| 97 | - Up, // RU | |
| 98 | - Down, // RD | |
| 99 | - Zero, // RZ | |
| 100 | - Nearest, // RN | |
| 101 | - Compatible, // RC | |
| 131 | + Up, // RU | |
| 132 | + Down, // RD | |
| 133 | + Zero, // RZ | |
| 134 | + Nearest, // RN | |
| 135 | + Compatible, // RC | |
| 102 | 136 | ProcessorDefined, // RP |
| 103 | 137 | } |
| 104 | 138 | |
| 105 | 139 | #[derive(Debug, Clone, Copy)] |
| 106 | 140 | pub enum DecimalSep { |
| 107 | - Comma, // DC | |
| 108 | - Point, // DP | |
| 141 | + Comma, // DC | |
| 142 | + Point, // DP | |
| 109 | 143 | } |
| 110 | 144 | |
| 111 | 145 | /// Parse a Fortran format string (the part inside parentheses) into descriptors. |
@@ -113,7 +147,7 @@ pub fn parse_format(fmt: &str) -> Vec<FormatDesc> { | ||
| 113 | 147 | let trimmed = fmt.trim(); |
| 114 | 148 | // Strip outer parens if present. |
| 115 | 149 | let inner = if trimmed.starts_with('(') && trimmed.ends_with(')') { |
| 116 | - &trimmed[1..trimmed.len()-1] | |
| 150 | + &trimmed[1..trimmed.len() - 1] | |
| 117 | 151 | } else { |
| 118 | 152 | trimmed |
| 119 | 153 | }; |
@@ -126,7 +160,9 @@ fn parse_format_list(input: &str) -> Vec<FormatDesc> { | ||
| 126 | 160 | |
| 127 | 161 | while chars.peek().is_some() { |
| 128 | 162 | skip_spaces(&mut chars); |
| 129 | - if chars.peek().is_none() { break; } | |
| 163 | + if chars.peek().is_none() { | |
| 164 | + break; | |
| 165 | + } | |
| 130 | 166 | |
| 131 | 167 | // Check for comma separator. |
| 132 | 168 | if chars.peek() == Some(&',') { |
@@ -146,7 +182,9 @@ fn parse_format_list(input: &str) -> Vec<FormatDesc> { | ||
| 146 | 182 | let repeat = parse_number(&mut chars); |
| 147 | 183 | |
| 148 | 184 | skip_spaces(&mut chars); |
| 149 | - if chars.peek().is_none() { break; } | |
| 185 | + if chars.peek().is_none() { | |
| 186 | + break; | |
| 187 | + } | |
| 150 | 188 | |
| 151 | 189 | let c = chars.peek().copied().unwrap_or(' '); |
| 152 | 190 | |
@@ -161,7 +199,10 @@ fn parse_format_list(input: &str) -> Vec<FormatDesc> { | ||
| 161 | 199 | // *(...) unlimited repeat — not representable with 0. |
| 162 | 200 | result.push(FormatDesc::UnlimitedRepeat { descriptors }); |
| 163 | 201 | } else { |
| 164 | - result.push(FormatDesc::Group { repeat: n, descriptors }); | |
| 202 | + result.push(FormatDesc::Group { | |
| 203 | + repeat: n, | |
| 204 | + descriptors, | |
| 205 | + }); | |
| 165 | 206 | } |
| 166 | 207 | } |
| 167 | 208 | |
@@ -203,9 +244,14 @@ fn parse_format_list(input: &str) -> Vec<FormatDesc> { | ||
| 203 | 244 | let desc = parse_edit_descriptor(&mut chars, repeat, negative); |
| 204 | 245 | if let Some(d) = desc { |
| 205 | 246 | if let Some(n) = repeat { |
| 206 | - if n > 1 && !matches!(d, FormatDesc::Skip { .. } | FormatDesc::ScaleFactor(_)) { | |
| 247 | + if n > 1 | |
| 248 | + && !matches!(d, FormatDesc::Skip { .. } | FormatDesc::ScaleFactor(_)) | |
| 249 | + { | |
| 207 | 250 | // Repeat count on a data descriptor: wrap in a group. |
| 208 | - result.push(FormatDesc::Group { repeat: n, descriptors: vec![d] }); | |
| 251 | + result.push(FormatDesc::Group { | |
| 252 | + repeat: n, | |
| 253 | + descriptors: vec![d], | |
| 254 | + }); | |
| 209 | 255 | } else { |
| 210 | 256 | result.push(d); |
| 211 | 257 | } |
@@ -230,47 +276,122 @@ fn parse_edit_descriptor( | ||
| 230 | 276 | match letter { |
| 231 | 277 | 'I' => { |
| 232 | 278 | let w = parse_number(chars).unwrap_or(0); |
| 233 | - let m = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars) } else { None }; | |
| 234 | - Some(FormatDesc::IntegerI { width: w, min_digits: m }) | |
| 279 | + let m = if chars.peek() == Some(&'.') { | |
| 280 | + chars.next(); | |
| 281 | + parse_number(chars) | |
| 282 | + } else { | |
| 283 | + None | |
| 284 | + }; | |
| 285 | + Some(FormatDesc::IntegerI { | |
| 286 | + width: w, | |
| 287 | + min_digits: m, | |
| 288 | + }) | |
| 235 | 289 | } |
| 236 | - 'B' if chars.peek().map(|c| c.is_ascii_digit() || *c == '\'').unwrap_or(false) => { | |
| 290 | + 'B' if chars | |
| 291 | + .peek() | |
| 292 | + .map(|c| c.is_ascii_digit() || *c == '\'') | |
| 293 | + .unwrap_or(false) => | |
| 294 | + { | |
| 237 | 295 | // B followed by digit → binary integer format. |
| 238 | 296 | // B followed by quote → BOZ literal (not handled here). |
| 239 | 297 | let w = parse_number(chars).unwrap_or(0); |
| 240 | - let m = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars) } else { None }; | |
| 241 | - Some(FormatDesc::IntegerB { width: w, min_digits: m }) | |
| 298 | + let m = if chars.peek() == Some(&'.') { | |
| 299 | + chars.next(); | |
| 300 | + parse_number(chars) | |
| 301 | + } else { | |
| 302 | + None | |
| 303 | + }; | |
| 304 | + Some(FormatDesc::IntegerB { | |
| 305 | + width: w, | |
| 306 | + min_digits: m, | |
| 307 | + }) | |
| 242 | 308 | } |
| 243 | 309 | 'O' => { |
| 244 | 310 | let w = parse_number(chars).unwrap_or(0); |
| 245 | - let m = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars) } else { None }; | |
| 246 | - Some(FormatDesc::IntegerO { width: w, min_digits: m }) | |
| 311 | + let m = if chars.peek() == Some(&'.') { | |
| 312 | + chars.next(); | |
| 313 | + parse_number(chars) | |
| 314 | + } else { | |
| 315 | + None | |
| 316 | + }; | |
| 317 | + Some(FormatDesc::IntegerO { | |
| 318 | + width: w, | |
| 319 | + min_digits: m, | |
| 320 | + }) | |
| 247 | 321 | } |
| 248 | 322 | 'Z' => { |
| 249 | 323 | let w = parse_number(chars).unwrap_or(0); |
| 250 | - let m = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars) } else { None }; | |
| 251 | - Some(FormatDesc::IntegerZ { width: w, min_digits: m }) | |
| 324 | + let m = if chars.peek() == Some(&'.') { | |
| 325 | + chars.next(); | |
| 326 | + parse_number(chars) | |
| 327 | + } else { | |
| 328 | + None | |
| 329 | + }; | |
| 330 | + Some(FormatDesc::IntegerZ { | |
| 331 | + width: w, | |
| 332 | + min_digits: m, | |
| 333 | + }) | |
| 252 | 334 | } |
| 253 | 335 | 'F' => { |
| 254 | 336 | let w = parse_number(chars).unwrap_or(0); |
| 255 | - let d = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars).unwrap_or(0) } else { 0 }; | |
| 256 | - Some(FormatDesc::RealF { width: w, decimals: d }) | |
| 337 | + let d = if chars.peek() == Some(&'.') { | |
| 338 | + chars.next(); | |
| 339 | + parse_number(chars).unwrap_or(0) | |
| 340 | + } else { | |
| 341 | + 0 | |
| 342 | + }; | |
| 343 | + Some(FormatDesc::RealF { | |
| 344 | + width: w, | |
| 345 | + decimals: d, | |
| 346 | + }) | |
| 257 | 347 | } |
| 258 | 348 | 'E' => { |
| 259 | 349 | // Check for EN, ES, EX. |
| 260 | 350 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 261 | 351 | match next { |
| 262 | - 'N' => { chars.next(); parse_real_desc(chars, |w, d, e| FormatDesc::RealEN { width: w, decimals: d, exp_width: e }) } | |
| 263 | - 'S' => { chars.next(); parse_real_desc(chars, |w, d, e| FormatDesc::RealES { width: w, decimals: d, exp_width: e }) } | |
| 264 | - 'X' => { chars.next(); parse_real_desc(chars, |w, d, e| FormatDesc::RealEX { width: w, decimals: d, exp_width: e }) } | |
| 265 | - _ => parse_real_desc(chars, |w, d, e| FormatDesc::RealE { width: w, decimals: d, exp_width: e }), | |
| 352 | + 'N' => { | |
| 353 | + chars.next(); | |
| 354 | + parse_real_desc(chars, |w, d, e| FormatDesc::RealEN { | |
| 355 | + width: w, | |
| 356 | + decimals: d, | |
| 357 | + exp_width: e, | |
| 358 | + }) | |
| 359 | + } | |
| 360 | + 'S' => { | |
| 361 | + chars.next(); | |
| 362 | + parse_real_desc(chars, |w, d, e| FormatDesc::RealES { | |
| 363 | + width: w, | |
| 364 | + decimals: d, | |
| 365 | + exp_width: e, | |
| 366 | + }) | |
| 367 | + } | |
| 368 | + 'X' => { | |
| 369 | + chars.next(); | |
| 370 | + parse_real_desc(chars, |w, d, e| FormatDesc::RealEX { | |
| 371 | + width: w, | |
| 372 | + decimals: d, | |
| 373 | + exp_width: e, | |
| 374 | + }) | |
| 375 | + } | |
| 376 | + _ => parse_real_desc(chars, |w, d, e| FormatDesc::RealE { | |
| 377 | + width: w, | |
| 378 | + decimals: d, | |
| 379 | + exp_width: e, | |
| 380 | + }), | |
| 266 | 381 | } |
| 267 | 382 | } |
| 268 | 383 | 'D' => { |
| 269 | 384 | // DC/DP (decimal mode) vs DT (derived type) vs Dw.d (real format). |
| 270 | 385 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 271 | 386 | match next { |
| 272 | - 'C' => { chars.next(); Some(FormatDesc::DecimalMode(DecimalSep::Comma)) } | |
| 273 | - 'P' => { chars.next(); Some(FormatDesc::DecimalMode(DecimalSep::Point)) } | |
| 387 | + 'C' => { | |
| 388 | + chars.next(); | |
| 389 | + Some(FormatDesc::DecimalMode(DecimalSep::Comma)) | |
| 390 | + } | |
| 391 | + 'P' => { | |
| 392 | + chars.next(); | |
| 393 | + Some(FormatDesc::DecimalMode(DecimalSep::Point)) | |
| 394 | + } | |
| 274 | 395 | 'T' => { |
| 275 | 396 | chars.next(); |
| 276 | 397 | // DT optionally followed by 'typename'. |
@@ -284,14 +405,24 @@ fn parse_edit_descriptor( | ||
| 284 | 405 | } |
| 285 | 406 | _ => { |
| 286 | 407 | let w = parse_number(chars).unwrap_or(0); |
| 287 | - let d = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars).unwrap_or(0) } else { 0 }; | |
| 288 | - Some(FormatDesc::RealD { width: w, decimals: d }) | |
| 408 | + let d = if chars.peek() == Some(&'.') { | |
| 409 | + chars.next(); | |
| 410 | + parse_number(chars).unwrap_or(0) | |
| 411 | + } else { | |
| 412 | + 0 | |
| 413 | + }; | |
| 414 | + Some(FormatDesc::RealD { | |
| 415 | + width: w, | |
| 416 | + decimals: d, | |
| 417 | + }) | |
| 289 | 418 | } |
| 290 | 419 | } |
| 291 | 420 | } |
| 292 | - 'G' => { | |
| 293 | - parse_real_desc(chars, |w, d, e| FormatDesc::RealG { width: w, decimals: d, exp_width: e }) | |
| 294 | - } | |
| 421 | + 'G' => parse_real_desc(chars, |w, d, e| FormatDesc::RealG { | |
| 422 | + width: w, | |
| 423 | + decimals: d, | |
| 424 | + exp_width: e, | |
| 425 | + }), | |
| 295 | 426 | 'L' => { |
| 296 | 427 | let w = parse_number(chars).unwrap_or(1); |
| 297 | 428 | Some(FormatDesc::Logical { width: w }) |
@@ -300,22 +431,39 @@ fn parse_edit_descriptor( | ||
| 300 | 431 | let w = parse_number(chars); |
| 301 | 432 | Some(FormatDesc::Character { width: w }) |
| 302 | 433 | } |
| 303 | - 'X' => { | |
| 304 | - Some(FormatDesc::Skip { count: repeat.unwrap_or(1) }) | |
| 305 | - } | |
| 434 | + 'X' => Some(FormatDesc::Skip { | |
| 435 | + count: repeat.unwrap_or(1), | |
| 436 | + }), | |
| 306 | 437 | 'T' => { |
| 307 | 438 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 308 | 439 | match next { |
| 309 | - 'L' => { chars.next(); let n = parse_number(chars).unwrap_or(1); Some(FormatDesc::TabLeft { count: n }) } | |
| 310 | - 'R' => { chars.next(); let n = parse_number(chars).unwrap_or(1); Some(FormatDesc::TabRight { count: n }) } | |
| 311 | - _ => { let n = parse_number(chars).unwrap_or(1); Some(FormatDesc::TabTo { position: n }) } | |
| 440 | + 'L' => { | |
| 441 | + chars.next(); | |
| 442 | + let n = parse_number(chars).unwrap_or(1); | |
| 443 | + Some(FormatDesc::TabLeft { count: n }) | |
| 444 | + } | |
| 445 | + 'R' => { | |
| 446 | + chars.next(); | |
| 447 | + let n = parse_number(chars).unwrap_or(1); | |
| 448 | + Some(FormatDesc::TabRight { count: n }) | |
| 449 | + } | |
| 450 | + _ => { | |
| 451 | + let n = parse_number(chars).unwrap_or(1); | |
| 452 | + Some(FormatDesc::TabTo { position: n }) | |
| 453 | + } | |
| 312 | 454 | } |
| 313 | 455 | } |
| 314 | 456 | 'S' => { |
| 315 | 457 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 316 | 458 | match next { |
| 317 | - 'P' => { chars.next(); Some(FormatDesc::Sign(SignMode::Plus)) } | |
| 318 | - 'S' => { chars.next(); Some(FormatDesc::Sign(SignMode::Suppress)) } | |
| 459 | + 'P' => { | |
| 460 | + chars.next(); | |
| 461 | + Some(FormatDesc::Sign(SignMode::Plus)) | |
| 462 | + } | |
| 463 | + 'S' => { | |
| 464 | + chars.next(); | |
| 465 | + Some(FormatDesc::Sign(SignMode::Suppress)) | |
| 466 | + } | |
| 319 | 467 | _ => Some(FormatDesc::Sign(SignMode::Default)), |
| 320 | 468 | } |
| 321 | 469 | } |
@@ -328,12 +476,30 @@ fn parse_edit_descriptor( | ||
| 328 | 476 | // Rounding modes: RU, RD, RZ, RN, RC, RP. |
| 329 | 477 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 330 | 478 | let mode = match next { |
| 331 | - 'U' => { chars.next(); Some(RoundMode::Up) } | |
| 332 | - 'D' => { chars.next(); Some(RoundMode::Down) } | |
| 333 | - 'Z' => { chars.next(); Some(RoundMode::Zero) } | |
| 334 | - 'N' => { chars.next(); Some(RoundMode::Nearest) } | |
| 335 | - 'C' => { chars.next(); Some(RoundMode::Compatible) } | |
| 336 | - 'P' => { chars.next(); Some(RoundMode::ProcessorDefined) } | |
| 479 | + 'U' => { | |
| 480 | + chars.next(); | |
| 481 | + Some(RoundMode::Up) | |
| 482 | + } | |
| 483 | + 'D' => { | |
| 484 | + chars.next(); | |
| 485 | + Some(RoundMode::Down) | |
| 486 | + } | |
| 487 | + 'Z' => { | |
| 488 | + chars.next(); | |
| 489 | + Some(RoundMode::Zero) | |
| 490 | + } | |
| 491 | + 'N' => { | |
| 492 | + chars.next(); | |
| 493 | + Some(RoundMode::Nearest) | |
| 494 | + } | |
| 495 | + 'C' => { | |
| 496 | + chars.next(); | |
| 497 | + Some(RoundMode::Compatible) | |
| 498 | + } | |
| 499 | + 'P' => { | |
| 500 | + chars.next(); | |
| 501 | + Some(RoundMode::ProcessorDefined) | |
| 502 | + } | |
| 337 | 503 | _ => None, |
| 338 | 504 | }; |
| 339 | 505 | mode.map(FormatDesc::RoundingMode) |
@@ -342,8 +508,14 @@ fn parse_edit_descriptor( | ||
| 342 | 508 | // BN or BZ. |
| 343 | 509 | let next = chars.peek().copied().unwrap_or(' ').to_ascii_uppercase(); |
| 344 | 510 | match next { |
| 345 | - 'N' => { chars.next(); Some(FormatDesc::BlankMode(BlankInterpretation::Null)) } | |
| 346 | - 'Z' => { chars.next(); Some(FormatDesc::BlankMode(BlankInterpretation::Zero)) } | |
| 511 | + 'N' => { | |
| 512 | + chars.next(); | |
| 513 | + Some(FormatDesc::BlankMode(BlankInterpretation::Null)) | |
| 514 | + } | |
| 515 | + 'Z' => { | |
| 516 | + chars.next(); | |
| 517 | + Some(FormatDesc::BlankMode(BlankInterpretation::Zero)) | |
| 518 | + } | |
| 347 | 519 | _ => None, |
| 348 | 520 | } |
| 349 | 521 | } |
@@ -356,11 +528,22 @@ fn parse_real_desc( | ||
| 356 | 528 | constructor: impl Fn(usize, usize, Option<usize>) -> FormatDesc, |
| 357 | 529 | ) -> Option<FormatDesc> { |
| 358 | 530 | let w = parse_number(chars).unwrap_or(0); |
| 359 | - let d = if chars.peek() == Some(&'.') { chars.next(); parse_number(chars).unwrap_or(0) } else { 0 }; | |
| 360 | - let e = if chars.peek().map(|c| c.eq_ignore_ascii_case(&'E')).unwrap_or(false) { | |
| 531 | + let d = if chars.peek() == Some(&'.') { | |
| 532 | + chars.next(); | |
| 533 | + parse_number(chars).unwrap_or(0) | |
| 534 | + } else { | |
| 535 | + 0 | |
| 536 | + }; | |
| 537 | + let e = if chars | |
| 538 | + .peek() | |
| 539 | + .map(|c| c.eq_ignore_ascii_case(&'E')) | |
| 540 | + .unwrap_or(false) | |
| 541 | + { | |
| 361 | 542 | chars.next(); |
| 362 | 543 | parse_number(chars) |
| 363 | - } else { None }; | |
| 544 | + } else { | |
| 545 | + None | |
| 546 | + }; | |
| 364 | 547 | Some(constructor(w, d, e)) |
| 365 | 548 | } |
| 366 | 549 | |
@@ -413,22 +596,38 @@ impl FormatEngine { | ||
| 413 | 596 | match desc { |
| 414 | 597 | // ---- Control descriptors ---- |
| 415 | 598 | FormatDesc::Skip { count } => { |
| 416 | - for _ in 0..*count { output.push(' '); } | |
| 599 | + for _ in 0..*count { | |
| 600 | + output.push(' '); | |
| 601 | + } | |
| 602 | + } | |
| 603 | + FormatDesc::Newline => { | |
| 604 | + output.push('\n'); | |
| 417 | 605 | } |
| 418 | - FormatDesc::Newline => { output.push('\n'); } | |
| 419 | 606 | FormatDesc::Colon => { |
| 420 | - if *val_idx >= values.len() { return; } | |
| 607 | + if *val_idx >= values.len() { | |
| 608 | + return; | |
| 609 | + } | |
| 610 | + } | |
| 611 | + FormatDesc::Sign(mode) => { | |
| 612 | + self.sign_mode = *mode; | |
| 613 | + } | |
| 614 | + FormatDesc::ScaleFactor(k) => { | |
| 615 | + self.scale_factor = *k; | |
| 421 | 616 | } |
| 422 | - FormatDesc::Sign(mode) => { self.sign_mode = *mode; } | |
| 423 | - FormatDesc::ScaleFactor(k) => { self.scale_factor = *k; } | |
| 424 | 617 | FormatDesc::BlankMode(_) => {} // input only |
| 425 | - FormatDesc::RoundingMode(mode) => { self.round_mode = *mode; } | |
| 426 | - FormatDesc::DecimalMode(sep) => { self.decimal_sep = *sep; } | |
| 618 | + FormatDesc::RoundingMode(mode) => { | |
| 619 | + self.round_mode = *mode; | |
| 620 | + } | |
| 621 | + FormatDesc::DecimalMode(sep) => { | |
| 622 | + self.decimal_sep = *sep; | |
| 623 | + } | |
| 427 | 624 | FormatDesc::DerivedType { .. } => {} // requires user-defined I/O — no-op for now |
| 428 | 625 | FormatDesc::TabTo { position } => { |
| 429 | 626 | let cur_col = output.lines().last().map(|l| l.len()).unwrap_or(0); |
| 430 | 627 | if *position > cur_col + 1 { |
| 431 | - for _ in 0..(*position - cur_col - 1) { output.push(' '); } | |
| 628 | + for _ in 0..(*position - cur_col - 1) { | |
| 629 | + output.push(' '); | |
| 630 | + } | |
| 432 | 631 | } |
| 433 | 632 | } |
| 434 | 633 | FormatDesc::TabLeft { count } => { |
@@ -437,12 +636,19 @@ impl FormatEngine { | ||
| 437 | 636 | output.truncate(new_len); |
| 438 | 637 | } |
| 439 | 638 | FormatDesc::TabRight { count } => { |
| 440 | - for _ in 0..*count { output.push(' '); } | |
| 639 | + for _ in 0..*count { | |
| 640 | + output.push(' '); | |
| 641 | + } | |
| 642 | + } | |
| 643 | + FormatDesc::LiteralString(s) => { | |
| 644 | + output.push_str(s); | |
| 441 | 645 | } |
| 442 | - FormatDesc::LiteralString(s) => { output.push_str(s); } | |
| 443 | 646 | |
| 444 | 647 | // ---- Group repeat ---- |
| 445 | - FormatDesc::Group { repeat, descriptors } => { | |
| 648 | + FormatDesc::Group { | |
| 649 | + repeat, | |
| 650 | + descriptors, | |
| 651 | + } => { | |
| 446 | 652 | for _ in 0..*repeat { |
| 447 | 653 | self.apply_descriptors(descriptors, values, val_idx, output); |
| 448 | 654 | } |
@@ -455,7 +661,9 @@ impl FormatEngine { | ||
| 455 | 661 | |
| 456 | 662 | // ---- Data descriptors ---- |
| 457 | 663 | _ => { |
| 458 | - if *val_idx >= values.len() { return; } | |
| 664 | + if *val_idx >= values.len() { | |
| 665 | + return; | |
| 666 | + } | |
| 459 | 667 | let val = &values[*val_idx]; |
| 460 | 668 | *val_idx += 1; |
| 461 | 669 | let formatted = self.format_value(desc, val); |
@@ -472,7 +680,11 @@ impl FormatEngine { | ||
| 472 | 680 | let s = if let Some(m) = min_digits { |
| 473 | 681 | let abs_s = format!("{}", v.unsigned_abs()); |
| 474 | 682 | let padded = format!("{:0>width$}", abs_s, width = *m); |
| 475 | - if *v < 0 { format!("-{}", padded) } else { self.apply_sign(&padded, *v >= 0) } | |
| 683 | + if *v < 0 { | |
| 684 | + format!("-{}", padded) | |
| 685 | + } else { | |
| 686 | + self.apply_sign(&padded, *v >= 0) | |
| 687 | + } | |
| 476 | 688 | } else { |
| 477 | 689 | self.apply_sign(&format!("{}", v.unsigned_abs()), *v >= 0) |
| 478 | 690 | }; |
@@ -496,16 +708,35 @@ impl FormatEngine { | ||
| 496 | 708 | let s = self.format_fixed(rounded, *decimals); |
| 497 | 709 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
| 498 | 710 | } |
| 499 | - (FormatDesc::RealE { width, decimals, exp_width }, IoValue::Real(v)) => { | |
| 711 | + ( | |
| 712 | + FormatDesc::RealE { | |
| 713 | + width, | |
| 714 | + decimals, | |
| 715 | + exp_width, | |
| 716 | + }, | |
| 717 | + IoValue::Real(v), | |
| 718 | + ) => { | |
| 500 | 719 | let s = self.format_e_style(*v, *decimals, *exp_width, 'E'); |
| 501 | 720 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
| 502 | 721 | } |
| 503 | - (FormatDesc::RealES { width, decimals, exp_width }, IoValue::Real(v)) => { | |
| 722 | + ( | |
| 723 | + FormatDesc::RealES { | |
| 724 | + width, | |
| 725 | + decimals, | |
| 726 | + exp_width, | |
| 727 | + }, | |
| 728 | + IoValue::Real(v), | |
| 729 | + ) => { | |
| 504 | 730 | // Scientific: mantissa in [1.0, 10.0). Equivalent to 1P,E. |
| 505 | 731 | let s = self.format_es_style(*v, *decimals, *exp_width); |
| 506 | 732 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
| 507 | 733 | } |
| 508 | - (FormatDesc::RealEN { width, decimals, .. }, IoValue::Real(v)) => { | |
| 734 | + ( | |
| 735 | + FormatDesc::RealEN { | |
| 736 | + width, decimals, .. | |
| 737 | + }, | |
| 738 | + IoValue::Real(v), | |
| 739 | + ) => { | |
| 509 | 740 | // Engineering: exponent is multiple of 3. |
| 510 | 741 | let (mantissa, exp) = to_engineering(*v); |
| 511 | 742 | let rounded = self.apply_rounding(mantissa, *decimals); |
@@ -516,7 +747,14 @@ impl FormatEngine { | ||
| 516 | 747 | let s = self.format_e_style(*v, *decimals, None, 'D'); |
| 517 | 748 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
| 518 | 749 | } |
| 519 | - (FormatDesc::RealG { width, decimals, exp_width }, IoValue::Real(v)) => { | |
| 750 | + ( | |
| 751 | + FormatDesc::RealG { | |
| 752 | + width, | |
| 753 | + decimals, | |
| 754 | + exp_width, | |
| 755 | + }, | |
| 756 | + IoValue::Real(v), | |
| 757 | + ) => { | |
| 520 | 758 | // G format: use F if magnitude fits, else E. |
| 521 | 759 | let abs_v = v.abs(); |
| 522 | 760 | if abs_v == 0.0 || (abs_v >= 0.1 && abs_v < 10f64.powi(*decimals as i32)) { |
@@ -528,7 +766,12 @@ impl FormatEngine { | ||
| 528 | 766 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
| 529 | 767 | } |
| 530 | 768 | } |
| 531 | - (FormatDesc::RealEX { width, decimals, .. }, IoValue::Real(v)) => { | |
| 769 | + ( | |
| 770 | + FormatDesc::RealEX { | |
| 771 | + width, decimals, .. | |
| 772 | + }, | |
| 773 | + IoValue::Real(v), | |
| 774 | + ) => { | |
| 532 | 775 | // Hex-significand: use %a-like format. Rust doesn't have this natively. |
| 533 | 776 | let s = format!("{:.*E}", *decimals, v); // fallback to E format |
| 534 | 777 | self.apply_decimal_sep(&format!("{:>width$}", s, width = *width)) |
@@ -616,10 +859,23 @@ impl FormatEngine { | ||
| 616 | 859 | /// Fortran kP with E format: the mantissa is multiplied by 10^k, |
| 617 | 860 | /// and the exponent is decreased by k. With 0P (default), the mantissa |
| 618 | 861 | /// is in [0.1, 1.0) — Fortran's convention, not C's. |
| 619 | - fn format_e_style(&self, v: f64, decimals: usize, exp_width: Option<usize>, exp_char: char) -> String { | |
| 862 | + fn format_e_style( | |
| 863 | + &self, | |
| 864 | + v: f64, | |
| 865 | + decimals: usize, | |
| 866 | + exp_width: Option<usize>, | |
| 867 | + exp_char: char, | |
| 868 | + ) -> String { | |
| 620 | 869 | if v == 0.0 { |
| 621 | 870 | let ew = exp_width.unwrap_or(2); |
| 622 | - return format!("0.{:0>d$}{}{:+0ew$}", "", exp_char, 0, d = decimals, ew = ew + 1); | |
| 871 | + return format!( | |
| 872 | + "0.{:0>d$}{}{:+0ew$}", | |
| 873 | + "", | |
| 874 | + exp_char, | |
| 875 | + 0, | |
| 876 | + d = decimals, | |
| 877 | + ew = ew + 1 | |
| 878 | + ); | |
| 623 | 879 | } |
| 624 | 880 | |
| 625 | 881 | let abs_v = v.abs(); |
@@ -630,8 +886,22 @@ impl FormatEngine { | ||
| 630 | 886 | let rounded = self.apply_rounding(mantissa, decimals); |
| 631 | 887 | |
| 632 | 888 | let ew = exp_width.unwrap_or(2); |
| 633 | - let sign = if v < 0.0 { "-" } else if matches!(self.sign_mode, SignMode::Plus) { "+" } else { "" }; | |
| 634 | - format!("{}{:.*}{}{:+0ew$}", sign, decimals, rounded, exp_char, fort_exp, ew = ew + 1) | |
| 889 | + let sign = if v < 0.0 { | |
| 890 | + "-" | |
| 891 | + } else if matches!(self.sign_mode, SignMode::Plus) { | |
| 892 | + "+" | |
| 893 | + } else { | |
| 894 | + "" | |
| 895 | + }; | |
| 896 | + format!( | |
| 897 | + "{}{:.*}{}{:+0ew$}", | |
| 898 | + sign, | |
| 899 | + decimals, | |
| 900 | + rounded, | |
| 901 | + exp_char, | |
| 902 | + fort_exp, | |
| 903 | + ew = ew + 1 | |
| 904 | + ) | |
| 635 | 905 | } |
| 636 | 906 | |
| 637 | 907 | /// Format in ES style (scientific): mantissa in [1.0, 10.0). |
@@ -647,8 +917,21 @@ impl FormatEngine { | ||
| 647 | 917 | let rounded = self.apply_rounding(mantissa, decimals); |
| 648 | 918 | |
| 649 | 919 | let ew = exp_width.unwrap_or(2); |
| 650 | - let sign = if v < 0.0 { "-" } else if matches!(self.sign_mode, SignMode::Plus) { "+" } else { "" }; | |
| 651 | - format!("{}{:.*}E{:+0ew$}", sign, decimals, rounded, base_exp, ew = ew + 1) | |
| 920 | + let sign = if v < 0.0 { | |
| 921 | + "-" | |
| 922 | + } else if matches!(self.sign_mode, SignMode::Plus) { | |
| 923 | + "+" | |
| 924 | + } else { | |
| 925 | + "" | |
| 926 | + }; | |
| 927 | + format!( | |
| 928 | + "{}{:.*}E{:+0ew$}", | |
| 929 | + sign, | |
| 930 | + decimals, | |
| 931 | + rounded, | |
| 932 | + base_exp, | |
| 933 | + ew = ew + 1 | |
| 934 | + ) | |
| 652 | 935 | } |
| 653 | 936 | |
| 654 | 937 | /// Replace '.' with ',' when decimal mode is DC (comma). |
@@ -663,14 +946,18 @@ impl FormatEngine { | ||
| 663 | 946 | // ---- Helpers ---- |
| 664 | 947 | |
| 665 | 948 | fn to_engineering(v: f64) -> (f64, i32) { |
| 666 | - if v == 0.0 { return (0.0, 0); } | |
| 949 | + if v == 0.0 { | |
| 950 | + return (0.0, 0); | |
| 951 | + } | |
| 667 | 952 | let exp = (v.abs().log10().floor() as i32) / 3 * 3; |
| 668 | 953 | let mantissa = v / 10f64.powi(exp); |
| 669 | 954 | (mantissa, exp) |
| 670 | 955 | } |
| 671 | 956 | |
| 672 | 957 | fn skip_spaces(chars: &mut std::iter::Peekable<std::str::Chars>) { |
| 673 | - while chars.peek() == Some(&' ') { chars.next(); } | |
| 958 | + while chars.peek() == Some(&' ') { | |
| 959 | + chars.next(); | |
| 960 | + } | |
| 674 | 961 | } |
| 675 | 962 | |
| 676 | 963 | fn parse_number(chars: &mut std::iter::Peekable<std::str::Chars>) -> Option<usize> { |
@@ -683,7 +970,11 @@ fn parse_number(chars: &mut std::iter::Peekable<std::str::Chars>) -> Option<usiz | ||
| 683 | 970 | break; |
| 684 | 971 | } |
| 685 | 972 | } |
| 686 | - if digits.is_empty() { None } else { digits.parse().ok() } | |
| 973 | + if digits.is_empty() { | |
| 974 | + None | |
| 975 | + } else { | |
| 976 | + digits.parse().ok() | |
| 977 | + } | |
| 687 | 978 | } |
| 688 | 979 | |
| 689 | 980 | fn parse_string_literal(chars: &mut std::iter::Peekable<std::str::Chars>, quote: char) -> String { |
@@ -711,10 +1002,14 @@ fn collect_until_matching_paren(chars: &mut std::iter::Peekable<std::str::Chars> | ||
| 711 | 1002 | let mut inner = String::new(); |
| 712 | 1003 | while let Some(&c) = chars.peek() { |
| 713 | 1004 | chars.next(); |
| 714 | - if c == '(' { depth += 1; } | |
| 1005 | + if c == '(' { | |
| 1006 | + depth += 1; | |
| 1007 | + } | |
| 715 | 1008 | if c == ')' { |
| 716 | 1009 | depth -= 1; |
| 717 | - if depth == 0 { break; } | |
| 1010 | + if depth == 0 { | |
| 1011 | + break; | |
| 1012 | + } | |
| 718 | 1013 | } |
| 719 | 1014 | inner.push(c); |
| 720 | 1015 | } |
@@ -729,8 +1024,20 @@ mod tests { | ||
| 729 | 1024 | fn parse_simple_format() { |
| 730 | 1025 | let descs = parse_format("(I5, F10.3, A)"); |
| 731 | 1026 | assert_eq!(descs.len(), 3); |
| 732 | - assert!(matches!(descs[0], FormatDesc::IntegerI { width: 5, min_digits: None })); | |
| 733 | - assert!(matches!(descs[1], FormatDesc::RealF { width: 10, decimals: 3 })); | |
| 1027 | + assert!(matches!( | |
| 1028 | + descs[0], | |
| 1029 | + FormatDesc::IntegerI { | |
| 1030 | + width: 5, | |
| 1031 | + min_digits: None | |
| 1032 | + } | |
| 1033 | + )); | |
| 1034 | + assert!(matches!( | |
| 1035 | + descs[1], | |
| 1036 | + FormatDesc::RealF { | |
| 1037 | + width: 10, | |
| 1038 | + decimals: 3 | |
| 1039 | + } | |
| 1040 | + )); | |
| 734 | 1041 | assert!(matches!(descs[2], FormatDesc::Character { width: None })); |
| 735 | 1042 | } |
| 736 | 1043 | |
@@ -757,14 +1064,30 @@ mod tests { | ||
| 757 | 1064 | assert_eq!(descs.len(), 2); |
| 758 | 1065 | if let FormatDesc::LiteralString(s) = &descs[0] { |
| 759 | 1066 | assert_eq!(s, "hello"); |
| 760 | - } else { panic!("expected literal"); } | |
| 1067 | + } else { | |
| 1068 | + panic!("expected literal"); | |
| 1069 | + } | |
| 761 | 1070 | } |
| 762 | 1071 | |
| 763 | 1072 | #[test] |
| 764 | 1073 | fn parse_es_en_format() { |
| 765 | 1074 | let descs = parse_format("(ES15.8, EN12.3)"); |
| 766 | - assert!(matches!(descs[0], FormatDesc::RealES { width: 15, decimals: 8, .. })); | |
| 767 | - assert!(matches!(descs[1], FormatDesc::RealEN { width: 12, decimals: 3, .. })); | |
| 1075 | + assert!(matches!( | |
| 1076 | + descs[0], | |
| 1077 | + FormatDesc::RealES { | |
| 1078 | + width: 15, | |
| 1079 | + decimals: 8, | |
| 1080 | + .. | |
| 1081 | + } | |
| 1082 | + )); | |
| 1083 | + assert!(matches!( | |
| 1084 | + descs[1], | |
| 1085 | + FormatDesc::RealEN { | |
| 1086 | + width: 12, | |
| 1087 | + decimals: 3, | |
| 1088 | + .. | |
| 1089 | + } | |
| 1090 | + )); | |
| 768 | 1091 | } |
| 769 | 1092 | |
| 770 | 1093 | #[test] |
@@ -869,7 +1192,9 @@ mod tests { | ||
| 869 | 1192 | let descs = parse_format("(*(I3, ','))"); |
| 870 | 1193 | let mut engine = FormatEngine::new(descs); |
| 871 | 1194 | let out = engine.format_values(&[ |
| 872 | - IoValue::Integer(1), IoValue::Integer(2), IoValue::Integer(3) | |
| 1195 | + IoValue::Integer(1), | |
| 1196 | + IoValue::Integer(2), | |
| 1197 | + IoValue::Integer(3), | |
| 873 | 1198 | ]); |
| 874 | 1199 | assert_eq!(out, " 1, 2, 3,"); |
| 875 | 1200 | } |
@@ -878,8 +1203,14 @@ mod tests { | ||
| 878 | 1203 | fn parse_dc_dp_decimal_mode() { |
| 879 | 1204 | let descs = parse_format("(DC, F8.3, DP, F8.3)"); |
| 880 | 1205 | assert_eq!(descs.len(), 4); |
| 881 | - assert!(matches!(descs[0], FormatDesc::DecimalMode(DecimalSep::Comma))); | |
| 882 | - assert!(matches!(descs[2], FormatDesc::DecimalMode(DecimalSep::Point))); | |
| 1206 | + assert!(matches!( | |
| 1207 | + descs[0], | |
| 1208 | + FormatDesc::DecimalMode(DecimalSep::Comma) | |
| 1209 | + )); | |
| 1210 | + assert!(matches!( | |
| 1211 | + descs[2], | |
| 1212 | + FormatDesc::DecimalMode(DecimalSep::Point) | |
| 1213 | + )); | |
| 883 | 1214 | } |
| 884 | 1215 | |
| 885 | 1216 | #[test] |
@@ -908,11 +1239,26 @@ mod tests { | ||
| 908 | 1239 | fn parse_rounding_modes() { |
| 909 | 1240 | let descs = parse_format("(RU, F8.3, RD, F8.3, RZ, F8.3, RN, F8.3, RC, F8.3, RP, F8.3)"); |
| 910 | 1241 | assert!(matches!(descs[0], FormatDesc::RoundingMode(RoundMode::Up))); |
| 911 | - assert!(matches!(descs[2], FormatDesc::RoundingMode(RoundMode::Down))); | |
| 912 | - assert!(matches!(descs[4], FormatDesc::RoundingMode(RoundMode::Zero))); | |
| 913 | - assert!(matches!(descs[6], FormatDesc::RoundingMode(RoundMode::Nearest))); | |
| 914 | - assert!(matches!(descs[8], FormatDesc::RoundingMode(RoundMode::Compatible))); | |
| 915 | - assert!(matches!(descs[10], FormatDesc::RoundingMode(RoundMode::ProcessorDefined))); | |
| 1242 | + assert!(matches!( | |
| 1243 | + descs[2], | |
| 1244 | + FormatDesc::RoundingMode(RoundMode::Down) | |
| 1245 | + )); | |
| 1246 | + assert!(matches!( | |
| 1247 | + descs[4], | |
| 1248 | + FormatDesc::RoundingMode(RoundMode::Zero) | |
| 1249 | + )); | |
| 1250 | + assert!(matches!( | |
| 1251 | + descs[6], | |
| 1252 | + FormatDesc::RoundingMode(RoundMode::Nearest) | |
| 1253 | + )); | |
| 1254 | + assert!(matches!( | |
| 1255 | + descs[8], | |
| 1256 | + FormatDesc::RoundingMode(RoundMode::Compatible) | |
| 1257 | + )); | |
| 1258 | + assert!(matches!( | |
| 1259 | + descs[10], | |
| 1260 | + FormatDesc::RoundingMode(RoundMode::ProcessorDefined) | |
| 1261 | + )); | |
| 916 | 1262 | } |
| 917 | 1263 | |
| 918 | 1264 | #[test] |
@@ -976,22 +1322,39 @@ mod tests { | ||
| 976 | 1322 | #[test] |
| 977 | 1323 | fn format_d_descriptor() { |
| 978 | 1324 | let descs = parse_format("(D12.5)"); |
| 979 | - assert!(matches!(descs[0], FormatDesc::RealD { width: 12, decimals: 5 })); | |
| 1325 | + assert!(matches!( | |
| 1326 | + descs[0], | |
| 1327 | + FormatDesc::RealD { | |
| 1328 | + width: 12, | |
| 1329 | + decimals: 5 | |
| 1330 | + } | |
| 1331 | + )); | |
| 980 | 1332 | } |
| 981 | 1333 | |
| 982 | 1334 | #[test] |
| 983 | 1335 | fn format_d_vs_dc() { |
| 984 | 1336 | // D12.5 is a real descriptor; DC is decimal comma mode. |
| 985 | 1337 | let descs = parse_format("(DC, D12.5)"); |
| 986 | - assert!(matches!(descs[0], FormatDesc::DecimalMode(DecimalSep::Comma))); | |
| 987 | - assert!(matches!(descs[1], FormatDesc::RealD { width: 12, decimals: 5 })); | |
| 1338 | + assert!(matches!( | |
| 1339 | + descs[0], | |
| 1340 | + FormatDesc::DecimalMode(DecimalSep::Comma) | |
| 1341 | + )); | |
| 1342 | + assert!(matches!( | |
| 1343 | + descs[1], | |
| 1344 | + FormatDesc::RealD { | |
| 1345 | + width: 12, | |
| 1346 | + decimals: 5 | |
| 1347 | + } | |
| 1348 | + )); | |
| 988 | 1349 | } |
| 989 | 1350 | |
| 990 | 1351 | #[test] |
| 991 | 1352 | fn format_integer16_full_width() { |
| 992 | 1353 | let descs = parse_format("(I40)"); |
| 993 | 1354 | let mut engine = FormatEngine::new(descs); |
| 994 | - let out = engine.format_values(&[IoValue::Integer(170141183460469231731687303715884105727i128)]); | |
| 1355 | + let out = engine.format_values(&[IoValue::Integer( | |
| 1356 | + 170141183460469231731687303715884105727i128, | |
| 1357 | + )]); | |
| 995 | 1358 | assert_eq!(out, " 170141183460469231731687303715884105727"); |
| 996 | 1359 | } |
| 997 | 1360 | } |
runtime/src/io_system.rsmodified@@ -11,7 +11,7 @@ | ||
| 11 | 11 | |
| 12 | 12 | use std::collections::HashMap; |
| 13 | 13 | use std::fs::{File, OpenOptions}; |
| 14 | -use std::io::{self, Read, Write, BufRead, BufReader, BufWriter, Seek, SeekFrom}; | |
| 14 | +use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; | |
| 15 | 15 | use std::sync::Mutex; |
| 16 | 16 | |
| 17 | 17 | // ---- Global I/O state ---- |
@@ -46,7 +46,6 @@ enum UnitStatus { | ||
| 46 | 46 | Open, |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | - | |
| 50 | 49 | #[derive(Debug, Clone, Copy, PartialEq)] |
| 51 | 50 | enum Access { |
| 52 | 51 | Sequential, |
@@ -110,7 +109,12 @@ impl Unit { | ||
| 110 | 109 | UnitStream::FileRaw(f) => { |
| 111 | 110 | f.write_all(data)?; |
| 112 | 111 | } |
| 113 | - _ => return Err(io::Error::new(io::ErrorKind::PermissionDenied, "unit not open for writing")), | |
| 112 | + _ => { | |
| 113 | + return Err(io::Error::new( | |
| 114 | + io::ErrorKind::PermissionDenied, | |
| 115 | + "unit not open for writing", | |
| 116 | + )) | |
| 117 | + } | |
| 114 | 118 | } |
| 115 | 119 | Ok(()) |
| 116 | 120 | } |
@@ -143,7 +147,12 @@ impl Unit { | ||
| 143 | 147 | } |
| 144 | 148 | } |
| 145 | 149 | } |
| 146 | - _ => return Err(io::Error::new(io::ErrorKind::PermissionDenied, "unit not open for reading")), | |
| 150 | + _ => { | |
| 151 | + return Err(io::Error::new( | |
| 152 | + io::ErrorKind::PermissionDenied, | |
| 153 | + "unit not open for reading", | |
| 154 | + )) | |
| 155 | + } | |
| 147 | 156 | } |
| 148 | 157 | Ok(line) |
| 149 | 158 | } |
@@ -197,44 +206,56 @@ impl IoState { | ||
| 197 | 206 | let mut units = HashMap::new(); |
| 198 | 207 | |
| 199 | 208 | // Preconnected units. |
| 200 | - units.insert(5, Unit { | |
| 201 | - _number: 5, | |
| 202 | - stream: UnitStream::Stdin, | |
| 203 | - filename: "stdin".into(), | |
| 204 | - _status: UnitStatus::Open, | |
| 205 | - access: Access::Sequential, | |
| 206 | - form: Form::Formatted, | |
| 207 | - action: Action::Read, | |
| 208 | - recl: None, | |
| 209 | - read_tokens: Vec::new(), | |
| 210 | - formatted_read_record: None, | |
| 211 | - }); | |
| 212 | - units.insert(6, Unit { | |
| 213 | - _number: 6, | |
| 214 | - stream: UnitStream::Stdout, | |
| 215 | - filename: "stdout".into(), | |
| 216 | - _status: UnitStatus::Open, | |
| 217 | - access: Access::Sequential, | |
| 218 | - form: Form::Formatted, | |
| 219 | - action: Action::Write, | |
| 220 | - recl: None, | |
| 221 | - read_tokens: Vec::new(), | |
| 222 | - formatted_read_record: None, | |
| 223 | - }); | |
| 224 | - units.insert(0, Unit { | |
| 225 | - _number: 0, | |
| 226 | - stream: UnitStream::Stderr, | |
| 227 | - filename: "stderr".into(), | |
| 228 | - _status: UnitStatus::Open, | |
| 229 | - access: Access::Sequential, | |
| 230 | - form: Form::Formatted, | |
| 231 | - action: Action::Write, | |
| 232 | - recl: None, | |
| 233 | - read_tokens: Vec::new(), | |
| 234 | - formatted_read_record: None, | |
| 235 | - }); | |
| 209 | + units.insert( | |
| 210 | + 5, | |
| 211 | + Unit { | |
| 212 | + _number: 5, | |
| 213 | + stream: UnitStream::Stdin, | |
| 214 | + filename: "stdin".into(), | |
| 215 | + _status: UnitStatus::Open, | |
| 216 | + access: Access::Sequential, | |
| 217 | + form: Form::Formatted, | |
| 218 | + action: Action::Read, | |
| 219 | + recl: None, | |
| 220 | + read_tokens: Vec::new(), | |
| 221 | + formatted_read_record: None, | |
| 222 | + }, | |
| 223 | + ); | |
| 224 | + units.insert( | |
| 225 | + 6, | |
| 226 | + Unit { | |
| 227 | + _number: 6, | |
| 228 | + stream: UnitStream::Stdout, | |
| 229 | + filename: "stdout".into(), | |
| 230 | + _status: UnitStatus::Open, | |
| 231 | + access: Access::Sequential, | |
| 232 | + form: Form::Formatted, | |
| 233 | + action: Action::Write, | |
| 234 | + recl: None, | |
| 235 | + read_tokens: Vec::new(), | |
| 236 | + formatted_read_record: None, | |
| 237 | + }, | |
| 238 | + ); | |
| 239 | + units.insert( | |
| 240 | + 0, | |
| 241 | + Unit { | |
| 242 | + _number: 0, | |
| 243 | + stream: UnitStream::Stderr, | |
| 244 | + filename: "stderr".into(), | |
| 245 | + _status: UnitStatus::Open, | |
| 246 | + access: Access::Sequential, | |
| 247 | + form: Form::Formatted, | |
| 248 | + action: Action::Write, | |
| 249 | + recl: None, | |
| 250 | + read_tokens: Vec::new(), | |
| 251 | + formatted_read_record: None, | |
| 252 | + }, | |
| 253 | + ); | |
| 236 | 254 | |
| 237 | - Self { units, next_newunit: -10 } | |
| 255 | + Self { | |
| 256 | + units, | |
| 257 | + next_newunit: -10, | |
| 258 | + } | |
| 238 | 259 | } |
| 239 | 260 | |
| 240 | 261 | fn get_unit(&mut self, unit_num: i32) -> Option<&mut Unit> { |
@@ -277,20 +298,30 @@ pub struct OpenControlBlock { | ||
| 277 | 298 | #[no_mangle] |
| 278 | 299 | pub extern "C" fn afs_open_simple( |
| 279 | 300 | unit: i32, |
| 280 | - filename: *const u8, filename_len: i64, | |
| 281 | - status: *const u8, status_len: i64, | |
| 282 | - action: *const u8, action_len: i64, | |
| 301 | + filename: *const u8, | |
| 302 | + filename_len: i64, | |
| 303 | + status: *const u8, | |
| 304 | + status_len: i64, | |
| 305 | + action: *const u8, | |
| 306 | + action_len: i64, | |
| 283 | 307 | ) { |
| 284 | 308 | let cb = OpenControlBlock { |
| 285 | - unit, filename, filename_len, | |
| 286 | - status, status_len, | |
| 287 | - action, action_len, | |
| 288 | - access: std::ptr::null(), access_len: 0, | |
| 289 | - form: std::ptr::null(), form_len: 0, | |
| 309 | + unit, | |
| 310 | + filename, | |
| 311 | + filename_len, | |
| 312 | + status, | |
| 313 | + status_len, | |
| 314 | + action, | |
| 315 | + action_len, | |
| 316 | + access: std::ptr::null(), | |
| 317 | + access_len: 0, | |
| 318 | + form: std::ptr::null(), | |
| 319 | + form_len: 0, | |
| 290 | 320 | recl: 0, |
| 291 | 321 | iostat: std::ptr::null_mut(), |
| 292 | 322 | newunit: std::ptr::null_mut(), |
| 293 | - position: std::ptr::null(), position_len: 0, | |
| 323 | + position: std::ptr::null(), | |
| 324 | + position_len: 0, | |
| 294 | 325 | }; |
| 295 | 326 | afs_open(&cb); |
| 296 | 327 | } |
@@ -299,7 +330,9 @@ pub extern "C" fn afs_open_simple( | ||
| 299 | 330 | /// exceeding the 8-register ARM64 calling convention limit. |
| 300 | 331 | #[no_mangle] |
| 301 | 332 | pub extern "C" fn afs_open(cb: *const OpenControlBlock) { |
| 302 | - if cb.is_null() { return; } | |
| 333 | + if cb.is_null() { | |
| 334 | + return; | |
| 335 | + } | |
| 303 | 336 | let cb = unsafe { &*cb }; |
| 304 | 337 | let unit = cb.unit; |
| 305 | 338 | let fname = unsafe_str(cb.filename, cb.filename_len); |
@@ -317,7 +350,9 @@ pub extern "C" fn afs_open(cb: *const OpenControlBlock) { | ||
| 317 | 350 | // NEWUNIT: allocate a new unit number. |
| 318 | 351 | let actual_unit = if !newunit.is_null() { |
| 319 | 352 | let u = state.alloc_newunit(); |
| 320 | - unsafe { *newunit = u; } | |
| 353 | + unsafe { | |
| 354 | + *newunit = u; | |
| 355 | + } | |
| 321 | 356 | u |
| 322 | 357 | } else { |
| 323 | 358 | unit |
@@ -326,11 +361,21 @@ pub extern "C" fn afs_open(cb: *const OpenControlBlock) { | ||
| 326 | 361 | // Build OpenOptions based on status/action. |
| 327 | 362 | let mut opts = OpenOptions::new(); |
| 328 | 363 | match status_str.trim() { |
| 329 | - "old" => { opts.read(true); } | |
| 330 | - "new" => { opts.write(true).create_new(true); } | |
| 331 | - "replace" => { opts.write(true).create(true).truncate(true); } | |
| 332 | - "scratch" | "unknown" | "" => { opts.read(true).write(true).create(true); } | |
| 333 | - _ => { opts.read(true).write(true).create(true); } | |
| 364 | + "old" => { | |
| 365 | + opts.read(true); | |
| 366 | + } | |
| 367 | + "new" => { | |
| 368 | + opts.write(true).create_new(true); | |
| 369 | + } | |
| 370 | + "replace" => { | |
| 371 | + opts.write(true).create(true).truncate(true); | |
| 372 | + } | |
| 373 | + "scratch" | "unknown" | "" => { | |
| 374 | + opts.read(true).write(true).create(true); | |
| 375 | + } | |
| 376 | + _ => { | |
| 377 | + opts.read(true).write(true).create(true); | |
| 378 | + } | |
| 334 | 379 | } |
| 335 | 380 | |
| 336 | 381 | // Determine action. Default depends on status: |
@@ -348,9 +393,15 @@ pub extern "C" fn afs_open(cb: *const OpenControlBlock) { | ||
| 348 | 393 | }; |
| 349 | 394 | |
| 350 | 395 | match effective_action { |
| 351 | - "read" => { opts.read(true); } | |
| 352 | - "write" => { opts.write(true).create(true); } | |
| 353 | - _ => { opts.read(true).write(true).create(true); } | |
| 396 | + "read" => { | |
| 397 | + opts.read(true); | |
| 398 | + } | |
| 399 | + "write" => { | |
| 400 | + opts.write(true).create(true); | |
| 401 | + } | |
| 402 | + _ => { | |
| 403 | + opts.read(true).write(true).create(true); | |
| 404 | + } | |
| 354 | 405 | } |
| 355 | 406 | |
| 356 | 407 | // Flush and close existing unit if re-opening. |
@@ -386,18 +437,21 @@ pub extern "C" fn afs_open(cb: *const OpenControlBlock) { | ||
| 386 | 437 | Action::ReadWrite => UnitStream::FileRaw(file), |
| 387 | 438 | }, |
| 388 | 439 | }; |
| 389 | - state.units.insert(actual_unit, Unit { | |
| 390 | - _number: actual_unit, | |
| 391 | - stream, | |
| 392 | - filename: fname, | |
| 393 | - _status: UnitStatus::Open, | |
| 394 | - access: file_access, | |
| 395 | - form: file_form, | |
| 396 | - action: file_action, | |
| 397 | - recl: if recl > 0 { Some(recl) } else { None }, | |
| 398 | - read_tokens: Vec::new(), | |
| 399 | - formatted_read_record: None, | |
| 400 | - }); | |
| 440 | + state.units.insert( | |
| 441 | + actual_unit, | |
| 442 | + Unit { | |
| 443 | + _number: actual_unit, | |
| 444 | + stream, | |
| 445 | + filename: fname, | |
| 446 | + _status: UnitStatus::Open, | |
| 447 | + access: file_access, | |
| 448 | + form: file_form, | |
| 449 | + action: file_action, | |
| 450 | + recl: if recl > 0 { Some(recl) } else { None }, | |
| 451 | + read_tokens: Vec::new(), | |
| 452 | + formatted_read_record: None, | |
| 453 | + }, | |
| 454 | + ); | |
| 401 | 455 | |
| 402 | 456 | // Apply POSITION specifier. |
| 403 | 457 | // Default: REWIND for sequential, ASIS for direct/stream. |
@@ -414,19 +468,31 @@ pub extern "C" fn afs_open(cb: *const OpenControlBlock) { | ||
| 414 | 468 | if let Some(seek) = pos { |
| 415 | 469 | if let Some(u) = state.get_unit(actual_unit) { |
| 416 | 470 | match &mut u.stream { |
| 417 | - UnitStream::FileRaw(f) => { let _ = f.seek(seek); } | |
| 418 | - UnitStream::FileRead(r) => { let _ = r.seek(seek); } | |
| 419 | - UnitStream::FileWrite(w) => { let _ = w.seek(seek); } | |
| 471 | + UnitStream::FileRaw(f) => { | |
| 472 | + let _ = f.seek(seek); | |
| 473 | + } | |
| 474 | + UnitStream::FileRead(r) => { | |
| 475 | + let _ = r.seek(seek); | |
| 476 | + } | |
| 477 | + UnitStream::FileWrite(w) => { | |
| 478 | + let _ = w.seek(seek); | |
| 479 | + } | |
| 420 | 480 | _ => {} |
| 421 | 481 | } |
| 422 | 482 | } |
| 423 | 483 | } |
| 424 | 484 | |
| 425 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 485 | + if !iostat.is_null() { | |
| 486 | + unsafe { | |
| 487 | + *iostat = 0; | |
| 488 | + } | |
| 489 | + } | |
| 426 | 490 | } |
| 427 | 491 | Err(e) => { |
| 428 | 492 | if !iostat.is_null() { |
| 429 | - unsafe { *iostat = e.raw_os_error().unwrap_or(1); } | |
| 493 | + unsafe { | |
| 494 | + *iostat = e.raw_os_error().unwrap_or(1); | |
| 495 | + } | |
| 430 | 496 | } else { |
| 431 | 497 | eprintln!("OPEN: {}: {}", fname, e); |
| 432 | 498 | std::process::exit(1); |
@@ -442,9 +508,17 @@ pub extern "C" fn afs_close(unit: i32, iostat: *mut i32) { | ||
| 442 | 508 | if let Some(mut u) = state.units.remove(&unit) { |
| 443 | 509 | let _ = u.flush(); |
| 444 | 510 | // File is dropped here, closing the handle. |
| 445 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 511 | + if !iostat.is_null() { | |
| 512 | + unsafe { | |
| 513 | + *iostat = 0; | |
| 514 | + } | |
| 515 | + } | |
| 446 | 516 | } else { |
| 447 | - if !iostat.is_null() { unsafe { *iostat = 0; } } // closing unopen unit is not an error | |
| 517 | + if !iostat.is_null() { | |
| 518 | + unsafe { | |
| 519 | + *iostat = 0; | |
| 520 | + } | |
| 521 | + } // closing unopen unit is not an error | |
| 448 | 522 | } |
| 449 | 523 | } |
| 450 | 524 | |
@@ -558,25 +632,49 @@ pub extern "C" fn afs_read_int(unit: i32, val: *mut i32, iostat: *mut i32) { | ||
| 558 | 632 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 559 | 633 | if let Some(u) = state.get_unit(unit) { |
| 560 | 634 | match u.next_read_token() { |
| 561 | - Ok(Some(token)) => { | |
| 562 | - match token.parse::<i32>() { | |
| 563 | - Ok(v) => { | |
| 564 | - if !val.is_null() { unsafe { *val = v; } } | |
| 565 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 635 | + Ok(Some(token)) => match token.parse::<i32>() { | |
| 636 | + Ok(v) => { | |
| 637 | + if !val.is_null() { | |
| 638 | + unsafe { | |
| 639 | + *val = v; | |
| 640 | + } | |
| 566 | 641 | } |
| 567 | - Err(_) => { | |
| 568 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 569 | - else { eprintln!("READ: cannot parse integer from '{}'", token); std::process::exit(1); } | |
| 642 | + if !iostat.is_null() { | |
| 643 | + unsafe { | |
| 644 | + *iostat = 0; | |
| 645 | + } | |
| 570 | 646 | } |
| 571 | 647 | } |
| 572 | - } | |
| 648 | + Err(_) => { | |
| 649 | + if !iostat.is_null() { | |
| 650 | + unsafe { | |
| 651 | + *iostat = 1; | |
| 652 | + } | |
| 653 | + } else { | |
| 654 | + eprintln!("READ: cannot parse integer from '{}'", token); | |
| 655 | + std::process::exit(1); | |
| 656 | + } | |
| 657 | + } | |
| 658 | + }, | |
| 573 | 659 | Ok(None) => { |
| 574 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 575 | - else { eprintln!("READ: end of file"); std::process::exit(1); } | |
| 660 | + if !iostat.is_null() { | |
| 661 | + unsafe { | |
| 662 | + *iostat = IOSTAT_END; | |
| 663 | + } | |
| 664 | + } else { | |
| 665 | + eprintln!("READ: end of file"); | |
| 666 | + std::process::exit(1); | |
| 667 | + } | |
| 576 | 668 | } |
| 577 | 669 | Err(e) => { |
| 578 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 579 | - else { eprintln!("READ: {}", e); std::process::exit(1); } | |
| 670 | + if !iostat.is_null() { | |
| 671 | + unsafe { | |
| 672 | + *iostat = 1; | |
| 673 | + } | |
| 674 | + } else { | |
| 675 | + eprintln!("READ: {}", e); | |
| 676 | + std::process::exit(1); | |
| 677 | + } | |
| 580 | 678 | } |
| 581 | 679 | } |
| 582 | 680 | } |
@@ -588,23 +686,43 @@ pub extern "C" fn afs_read_int64(unit: i32, val: *mut i64, iostat: *mut i32) { | ||
| 588 | 686 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 589 | 687 | if let Some(u) = state.get_unit(unit) { |
| 590 | 688 | match u.next_read_token() { |
| 591 | - Ok(Some(token)) => { | |
| 592 | - match token.parse::<i64>() { | |
| 593 | - Ok(v) => { | |
| 594 | - if !val.is_null() { unsafe { *val = v; } } | |
| 595 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 689 | + Ok(Some(token)) => match token.parse::<i64>() { | |
| 690 | + Ok(v) => { | |
| 691 | + if !val.is_null() { | |
| 692 | + unsafe { | |
| 693 | + *val = v; | |
| 694 | + } | |
| 596 | 695 | } |
| 597 | - Err(_) => { | |
| 598 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 599 | - else { eprintln!("READ: cannot parse integer from '{}'", token); std::process::exit(1); } | |
| 696 | + if !iostat.is_null() { | |
| 697 | + unsafe { | |
| 698 | + *iostat = 0; | |
| 699 | + } | |
| 600 | 700 | } |
| 601 | 701 | } |
| 602 | - } | |
| 702 | + Err(_) => { | |
| 703 | + if !iostat.is_null() { | |
| 704 | + unsafe { | |
| 705 | + *iostat = 1; | |
| 706 | + } | |
| 707 | + } else { | |
| 708 | + eprintln!("READ: cannot parse integer from '{}'", token); | |
| 709 | + std::process::exit(1); | |
| 710 | + } | |
| 711 | + } | |
| 712 | + }, | |
| 603 | 713 | Ok(None) => { |
| 604 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 714 | + if !iostat.is_null() { | |
| 715 | + unsafe { | |
| 716 | + *iostat = IOSTAT_END; | |
| 717 | + } | |
| 718 | + } | |
| 605 | 719 | } |
| 606 | 720 | Err(_) => { |
| 607 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 721 | + if !iostat.is_null() { | |
| 722 | + unsafe { | |
| 723 | + *iostat = 1; | |
| 724 | + } | |
| 725 | + } | |
| 608 | 726 | } |
| 609 | 727 | } |
| 610 | 728 | } |
@@ -616,23 +734,39 @@ pub extern "C" fn afs_read_int128(unit: i32, val: *mut i128, iostat: *mut i32) { | ||
| 616 | 734 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 617 | 735 | if let Some(u) = state.get_unit(unit) { |
| 618 | 736 | match u.next_read_token() { |
| 619 | - Ok(Some(token)) => { | |
| 620 | - match token.parse::<i128>() { | |
| 621 | - Ok(v) => { | |
| 622 | - write_i128_ptr(val, v); | |
| 623 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 737 | + Ok(Some(token)) => match token.parse::<i128>() { | |
| 738 | + Ok(v) => { | |
| 739 | + write_i128_ptr(val, v); | |
| 740 | + if !iostat.is_null() { | |
| 741 | + unsafe { | |
| 742 | + *iostat = 0; | |
| 743 | + } | |
| 624 | 744 | } |
| 625 | - Err(_) => { | |
| 626 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 627 | - else { eprintln!("READ: cannot parse integer from '{}'", token); std::process::exit(1); } | |
| 745 | + } | |
| 746 | + Err(_) => { | |
| 747 | + if !iostat.is_null() { | |
| 748 | + unsafe { | |
| 749 | + *iostat = 1; | |
| 750 | + } | |
| 751 | + } else { | |
| 752 | + eprintln!("READ: cannot parse integer from '{}'", token); | |
| 753 | + std::process::exit(1); | |
| 628 | 754 | } |
| 629 | 755 | } |
| 630 | - } | |
| 756 | + }, | |
| 631 | 757 | Ok(None) => { |
| 632 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 758 | + if !iostat.is_null() { | |
| 759 | + unsafe { | |
| 760 | + *iostat = IOSTAT_END; | |
| 761 | + } | |
| 762 | + } | |
| 633 | 763 | } |
| 634 | 764 | Err(_) => { |
| 635 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 765 | + if !iostat.is_null() { | |
| 766 | + unsafe { | |
| 767 | + *iostat = 1; | |
| 768 | + } | |
| 769 | + } | |
| 636 | 770 | } |
| 637 | 771 | } |
| 638 | 772 | } |
@@ -649,21 +783,45 @@ pub extern "C" fn afs_read_real(unit: i32, val: *mut f32, iostat: *mut i32) { | ||
| 649 | 783 | let normalized = token.replace('d', "e").replace('D', "E"); |
| 650 | 784 | match normalized.parse::<f32>() { |
| 651 | 785 | Ok(v) => { |
| 652 | - if !val.is_null() { unsafe { *val = v; } } | |
| 653 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 786 | + if !val.is_null() { | |
| 787 | + unsafe { | |
| 788 | + *val = v; | |
| 789 | + } | |
| 790 | + } | |
| 791 | + if !iostat.is_null() { | |
| 792 | + unsafe { | |
| 793 | + *iostat = 0; | |
| 794 | + } | |
| 795 | + } | |
| 654 | 796 | } |
| 655 | 797 | Err(_) => { |
| 656 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 657 | - else { eprintln!("READ: cannot parse real from '{}'", token); std::process::exit(1); } | |
| 798 | + if !iostat.is_null() { | |
| 799 | + unsafe { | |
| 800 | + *iostat = 1; | |
| 801 | + } | |
| 802 | + } else { | |
| 803 | + eprintln!("READ: cannot parse real from '{}'", token); | |
| 804 | + std::process::exit(1); | |
| 805 | + } | |
| 658 | 806 | } |
| 659 | 807 | } |
| 660 | 808 | } |
| 661 | 809 | Ok(None) => { |
| 662 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 663 | - else { eprintln!("READ: end of file"); std::process::exit(1); } | |
| 810 | + if !iostat.is_null() { | |
| 811 | + unsafe { | |
| 812 | + *iostat = IOSTAT_END; | |
| 813 | + } | |
| 814 | + } else { | |
| 815 | + eprintln!("READ: end of file"); | |
| 816 | + std::process::exit(1); | |
| 817 | + } | |
| 664 | 818 | } |
| 665 | 819 | Err(_) => { |
| 666 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 820 | + if !iostat.is_null() { | |
| 821 | + unsafe { | |
| 822 | + *iostat = 1; | |
| 823 | + } | |
| 824 | + } | |
| 667 | 825 | } |
| 668 | 826 | } |
| 669 | 827 | } |
@@ -679,20 +837,42 @@ pub extern "C" fn afs_read_real64(unit: i32, val: *mut f64, iostat: *mut i32) { | ||
| 679 | 837 | let normalized = token.replace('d', "e").replace('D', "E"); |
| 680 | 838 | match normalized.parse::<f64>() { |
| 681 | 839 | Ok(v) => { |
| 682 | - if !val.is_null() { unsafe { *val = v; } } | |
| 683 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 840 | + if !val.is_null() { | |
| 841 | + unsafe { | |
| 842 | + *val = v; | |
| 843 | + } | |
| 844 | + } | |
| 845 | + if !iostat.is_null() { | |
| 846 | + unsafe { | |
| 847 | + *iostat = 0; | |
| 848 | + } | |
| 849 | + } | |
| 684 | 850 | } |
| 685 | 851 | Err(_) => { |
| 686 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 687 | - else { eprintln!("READ: cannot parse real from '{}'", token); std::process::exit(1); } | |
| 852 | + if !iostat.is_null() { | |
| 853 | + unsafe { | |
| 854 | + *iostat = 1; | |
| 855 | + } | |
| 856 | + } else { | |
| 857 | + eprintln!("READ: cannot parse real from '{}'", token); | |
| 858 | + std::process::exit(1); | |
| 859 | + } | |
| 688 | 860 | } |
| 689 | 861 | } |
| 690 | 862 | } |
| 691 | 863 | Ok(None) => { |
| 692 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 864 | + if !iostat.is_null() { | |
| 865 | + unsafe { | |
| 866 | + *iostat = IOSTAT_END; | |
| 867 | + } | |
| 868 | + } | |
| 693 | 869 | } |
| 694 | 870 | Err(_) => { |
| 695 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 871 | + if !iostat.is_null() { | |
| 872 | + unsafe { | |
| 873 | + *iostat = 1; | |
| 874 | + } | |
| 875 | + } | |
| 696 | 876 | } |
| 697 | 877 | } |
| 698 | 878 | } |
@@ -716,22 +896,32 @@ impl Unit { | ||
| 716 | 896 | /// Record numbers are 1-based. Returns Ok(()) or Err on failure. |
| 717 | 897 | fn seek_to_record(&mut self, rec: i64) -> io::Result<()> { |
| 718 | 898 | if rec < 1 { |
| 719 | - return Err(io::Error::new(io::ErrorKind::InvalidInput, | |
| 720 | - "direct access record number must be >= 1")); | |
| 721 | - } | |
| 722 | - let recl = self.recl.ok_or_else(|| | |
| 723 | - io::Error::new(io::ErrorKind::InvalidInput, "direct access requires RECL"))?; | |
| 724 | - let offset = (rec - 1).checked_mul(recl).ok_or_else(|| | |
| 725 | - io::Error::new(io::ErrorKind::InvalidInput, "record offset overflow"))?; | |
| 899 | + return Err(io::Error::new( | |
| 900 | + io::ErrorKind::InvalidInput, | |
| 901 | + "direct access record number must be >= 1", | |
| 902 | + )); | |
| 903 | + } | |
| 904 | + let recl = self.recl.ok_or_else(|| { | |
| 905 | + io::Error::new(io::ErrorKind::InvalidInput, "direct access requires RECL") | |
| 906 | + })?; | |
| 907 | + let offset = (rec - 1) | |
| 908 | + .checked_mul(recl) | |
| 909 | + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "record offset overflow"))?; | |
| 726 | 910 | if offset < 0 { |
| 727 | - return Err(io::Error::new(io::ErrorKind::InvalidInput, "negative record offset")); | |
| 911 | + return Err(io::Error::new( | |
| 912 | + io::ErrorKind::InvalidInput, | |
| 913 | + "negative record offset", | |
| 914 | + )); | |
| 728 | 915 | } |
| 729 | 916 | match &mut self.stream { |
| 730 | 917 | UnitStream::FileRaw(f) => { |
| 731 | 918 | f.seek(SeekFrom::Start(offset as u64))?; |
| 732 | 919 | Ok(()) |
| 733 | 920 | } |
| 734 | - _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "unit not opened for direct access")), | |
| 921 | + _ => Err(io::Error::new( | |
| 922 | + io::ErrorKind::InvalidInput, | |
| 923 | + "unit not opened for direct access", | |
| 924 | + )), | |
| 735 | 925 | } |
| 736 | 926 | } |
| 737 | 927 | |
@@ -740,7 +930,10 @@ impl Unit { | ||
| 740 | 930 | match &mut self.stream { |
| 741 | 931 | UnitStream::FileRaw(f) => f.write_all(data), |
| 742 | 932 | UnitStream::FileWrite(w) => w.write_all(data), |
| 743 | - _ => Err(io::Error::new(io::ErrorKind::PermissionDenied, "unit not open for writing")), | |
| 933 | + _ => Err(io::Error::new( | |
| 934 | + io::ErrorKind::PermissionDenied, | |
| 935 | + "unit not open for writing", | |
| 936 | + )), | |
| 744 | 937 | } |
| 745 | 938 | } |
| 746 | 939 | |
@@ -749,7 +942,10 @@ impl Unit { | ||
| 749 | 942 | match &mut self.stream { |
| 750 | 943 | UnitStream::FileRaw(f) => f.read(buf), |
| 751 | 944 | UnitStream::FileRead(r) => r.read(buf), |
| 752 | - _ => Err(io::Error::new(io::ErrorKind::PermissionDenied, "unit not open for reading")), | |
| 945 | + _ => Err(io::Error::new( | |
| 946 | + io::ErrorKind::PermissionDenied, | |
| 947 | + "unit not open for reading", | |
| 948 | + )), | |
| 753 | 949 | } |
| 754 | 950 | } |
| 755 | 951 | } |
@@ -757,44 +953,72 @@ impl Unit { | ||
| 757 | 953 | /// Write a direct-access record (formatted string padded to recl). |
| 758 | 954 | #[no_mangle] |
| 759 | 955 | pub extern "C" fn afs_write_direct( |
| 760 | - unit: i32, rec: i64, | |
| 761 | - data: *const u8, data_len: i64, | |
| 956 | + unit: i32, | |
| 957 | + rec: i64, | |
| 958 | + data: *const u8, | |
| 959 | + data_len: i64, | |
| 762 | 960 | iostat: *mut i32, |
| 763 | 961 | ) { |
| 764 | 962 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 765 | 963 | if let Some(u) = state.get_unit(unit) { |
| 766 | 964 | if let Err(e) = u.seek_to_record(rec) { |
| 767 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 768 | - else { eprintln!("WRITE direct: {}", e); std::process::exit(1); } | |
| 965 | + if !iostat.is_null() { | |
| 966 | + unsafe { | |
| 967 | + *iostat = 1; | |
| 968 | + } | |
| 969 | + } else { | |
| 970 | + eprintln!("WRITE direct: {}", e); | |
| 971 | + std::process::exit(1); | |
| 972 | + } | |
| 769 | 973 | return; |
| 770 | 974 | } |
| 771 | 975 | let recl = u.recl.unwrap_or(0) as usize; |
| 772 | 976 | let mut record = vec![b' '; recl]; // space-padded |
| 773 | 977 | let copy_len = (data_len as usize).min(recl); |
| 774 | 978 | if !data.is_null() && copy_len > 0 { |
| 775 | - unsafe { std::ptr::copy_nonoverlapping(data, record.as_mut_ptr(), copy_len); } | |
| 979 | + unsafe { | |
| 980 | + std::ptr::copy_nonoverlapping(data, record.as_mut_ptr(), copy_len); | |
| 981 | + } | |
| 776 | 982 | } |
| 777 | 983 | if let Err(e) = u.write_raw(&record) { |
| 778 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 779 | - else { eprintln!("WRITE direct: {}", e); std::process::exit(1); } | |
| 984 | + if !iostat.is_null() { | |
| 985 | + unsafe { | |
| 986 | + *iostat = 1; | |
| 987 | + } | |
| 988 | + } else { | |
| 989 | + eprintln!("WRITE direct: {}", e); | |
| 990 | + std::process::exit(1); | |
| 991 | + } | |
| 780 | 992 | return; |
| 781 | 993 | } |
| 782 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 994 | + if !iostat.is_null() { | |
| 995 | + unsafe { | |
| 996 | + *iostat = 0; | |
| 997 | + } | |
| 998 | + } | |
| 783 | 999 | } |
| 784 | 1000 | } |
| 785 | 1001 | |
| 786 | 1002 | /// Read a direct-access record. |
| 787 | 1003 | #[no_mangle] |
| 788 | 1004 | pub extern "C" fn afs_read_direct( |
| 789 | - unit: i32, rec: i64, | |
| 790 | - data: *mut u8, data_len: i64, | |
| 1005 | + unit: i32, | |
| 1006 | + rec: i64, | |
| 1007 | + data: *mut u8, | |
| 1008 | + data_len: i64, | |
| 791 | 1009 | iostat: *mut i32, |
| 792 | 1010 | ) { |
| 793 | 1011 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 794 | 1012 | if let Some(u) = state.get_unit(unit) { |
| 795 | 1013 | if let Err(e) = u.seek_to_record(rec) { |
| 796 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 797 | - else { eprintln!("READ direct: {}", e); std::process::exit(1); } | |
| 1014 | + if !iostat.is_null() { | |
| 1015 | + unsafe { | |
| 1016 | + *iostat = 1; | |
| 1017 | + } | |
| 1018 | + } else { | |
| 1019 | + eprintln!("READ direct: {}", e); | |
| 1020 | + std::process::exit(1); | |
| 1021 | + } | |
| 798 | 1022 | return; |
| 799 | 1023 | } |
| 800 | 1024 | let recl = u.recl.unwrap_or(0) as usize; |
@@ -803,17 +1027,35 @@ pub extern "C" fn afs_read_direct( | ||
| 803 | 1027 | Ok(n) => { |
| 804 | 1028 | let copy_len = n.min(data_len as usize); |
| 805 | 1029 | if !data.is_null() && copy_len > 0 { |
| 806 | - unsafe { std::ptr::copy_nonoverlapping(record.as_ptr(), data, copy_len); } | |
| 1030 | + unsafe { | |
| 1031 | + std::ptr::copy_nonoverlapping(record.as_ptr(), data, copy_len); | |
| 1032 | + } | |
| 807 | 1033 | } |
| 808 | 1034 | // Pad remainder with spaces. |
| 809 | 1035 | if copy_len < data_len as usize { |
| 810 | - unsafe { std::ptr::write_bytes(data.add(copy_len), b' ', data_len as usize - copy_len); } | |
| 1036 | + unsafe { | |
| 1037 | + std::ptr::write_bytes( | |
| 1038 | + data.add(copy_len), | |
| 1039 | + b' ', | |
| 1040 | + data_len as usize - copy_len, | |
| 1041 | + ); | |
| 1042 | + } | |
| 1043 | + } | |
| 1044 | + if !iostat.is_null() { | |
| 1045 | + unsafe { | |
| 1046 | + *iostat = 0; | |
| 1047 | + } | |
| 811 | 1048 | } |
| 812 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 813 | 1049 | } |
| 814 | 1050 | Err(e) => { |
| 815 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 816 | - else { eprintln!("READ direct: {}", e); std::process::exit(1); } | |
| 1051 | + if !iostat.is_null() { | |
| 1052 | + unsafe { | |
| 1053 | + *iostat = 1; | |
| 1054 | + } | |
| 1055 | + } else { | |
| 1056 | + eprintln!("READ direct: {}", e); | |
| 1057 | + std::process::exit(1); | |
| 1058 | + } | |
| 817 | 1059 | } |
| 818 | 1060 | } |
| 819 | 1061 | } |
@@ -825,14 +1067,24 @@ pub extern "C" fn afs_read_direct( | ||
| 825 | 1067 | #[no_mangle] |
| 826 | 1068 | pub extern "C" fn afs_write_unformatted( |
| 827 | 1069 | unit: i32, |
| 828 | - data: *const u8, data_len: i64, | |
| 1070 | + data: *const u8, | |
| 1071 | + data_len: i64, | |
| 829 | 1072 | iostat: *mut i32, |
| 830 | 1073 | ) { |
| 831 | 1074 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 832 | 1075 | if let Some(u) = state.get_unit(unit) { |
| 833 | 1076 | if data_len < 0 || data_len > u32::MAX as i64 { |
| 834 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 835 | - else { eprintln!("WRITE unformatted: record length {} exceeds 4GB limit", data_len); std::process::exit(1); } | |
| 1077 | + if !iostat.is_null() { | |
| 1078 | + unsafe { | |
| 1079 | + *iostat = 1; | |
| 1080 | + } | |
| 1081 | + } else { | |
| 1082 | + eprintln!( | |
| 1083 | + "WRITE unformatted: record length {} exceeds 4GB limit", | |
| 1084 | + data_len | |
| 1085 | + ); | |
| 1086 | + std::process::exit(1); | |
| 1087 | + } | |
| 836 | 1088 | return; |
| 837 | 1089 | } |
| 838 | 1090 | let len_bytes = (data_len as u32).to_ne_bytes(); |
@@ -841,12 +1093,22 @@ pub extern "C" fn afs_write_unformatted( | ||
| 841 | 1093 | let r2 = if !data.is_null() && data_len > 0 { |
| 842 | 1094 | let slice = unsafe { std::slice::from_raw_parts(data, data_len as usize) }; |
| 843 | 1095 | u.write_raw(slice) |
| 844 | - } else { Ok(()) }; | |
| 1096 | + } else { | |
| 1097 | + Ok(()) | |
| 1098 | + }; | |
| 845 | 1099 | let r3 = u.write_raw(&len_bytes); |
| 846 | 1100 | if r1.is_err() || r2.is_err() || r3.is_err() { |
| 847 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1101 | + if !iostat.is_null() { | |
| 1102 | + unsafe { | |
| 1103 | + *iostat = 1; | |
| 1104 | + } | |
| 1105 | + } | |
| 848 | 1106 | } else { |
| 849 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1107 | + if !iostat.is_null() { | |
| 1108 | + unsafe { | |
| 1109 | + *iostat = 0; | |
| 1110 | + } | |
| 1111 | + } | |
| 850 | 1112 | } |
| 851 | 1113 | } |
| 852 | 1114 | } |
@@ -855,7 +1117,8 @@ pub extern "C" fn afs_write_unformatted( | ||
| 855 | 1117 | #[no_mangle] |
| 856 | 1118 | pub extern "C" fn afs_read_unformatted( |
| 857 | 1119 | unit: i32, |
| 858 | - data: *mut u8, max_len: i64, | |
| 1120 | + data: *mut u8, | |
| 1121 | + max_len: i64, | |
| 859 | 1122 | actual_len: *mut i64, |
| 860 | 1123 | iostat: *mut i32, |
| 861 | 1124 | ) { |
@@ -866,16 +1129,28 @@ pub extern "C" fn afs_read_unformatted( | ||
| 866 | 1129 | match u.read_raw(&mut len_buf) { |
| 867 | 1130 | Ok(4) => {} |
| 868 | 1131 | Ok(0) => { |
| 869 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 1132 | + if !iostat.is_null() { | |
| 1133 | + unsafe { | |
| 1134 | + *iostat = IOSTAT_END; | |
| 1135 | + } | |
| 1136 | + } | |
| 870 | 1137 | return; |
| 871 | 1138 | } |
| 872 | 1139 | _ => { |
| 873 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1140 | + if !iostat.is_null() { | |
| 1141 | + unsafe { | |
| 1142 | + *iostat = 1; | |
| 1143 | + } | |
| 1144 | + } | |
| 874 | 1145 | return; |
| 875 | 1146 | } |
| 876 | 1147 | } |
| 877 | 1148 | let record_len = u32::from_ne_bytes(len_buf) as usize; |
| 878 | - if !actual_len.is_null() { unsafe { *actual_len = record_len as i64; } } | |
| 1149 | + if !actual_len.is_null() { | |
| 1150 | + unsafe { | |
| 1151 | + *actual_len = record_len as i64; | |
| 1152 | + } | |
| 1153 | + } | |
| 879 | 1154 | |
| 880 | 1155 | // Read record data. |
| 881 | 1156 | let read_len = record_len.min(max_len as usize); |
@@ -892,7 +1167,11 @@ pub extern "C" fn afs_read_unformatted( | ||
| 892 | 1167 | |
| 893 | 1168 | // Read trailing length marker (and discard). |
| 894 | 1169 | let _ = u.read_raw(&mut len_buf); |
| 895 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1170 | + if !iostat.is_null() { | |
| 1171 | + unsafe { | |
| 1172 | + *iostat = 0; | |
| 1173 | + } | |
| 1174 | + } | |
| 896 | 1175 | } |
| 897 | 1176 | } |
| 898 | 1177 | |
@@ -900,18 +1179,26 @@ pub extern "C" fn afs_read_unformatted( | ||
| 900 | 1179 | |
| 901 | 1180 | /// Write raw bytes at the current stream position. |
| 902 | 1181 | #[no_mangle] |
| 903 | -pub extern "C" fn afs_write_stream( | |
| 904 | - unit: i32, | |
| 905 | - data: *const u8, data_len: i64, | |
| 906 | - iostat: *mut i32, | |
| 907 | -) { | |
| 1182 | +pub extern "C" fn afs_write_stream(unit: i32, data: *const u8, data_len: i64, iostat: *mut i32) { | |
| 908 | 1183 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 909 | 1184 | if let Some(u) = state.get_unit(unit) { |
| 910 | 1185 | if !data.is_null() && data_len > 0 { |
| 911 | 1186 | let slice = unsafe { std::slice::from_raw_parts(data, data_len as usize) }; |
| 912 | 1187 | match u.write_raw(slice) { |
| 913 | - Ok(()) => { if !iostat.is_null() { unsafe { *iostat = 0; } } } | |
| 914 | - Err(_) => { if !iostat.is_null() { unsafe { *iostat = 1; } } } | |
| 1188 | + Ok(()) => { | |
| 1189 | + if !iostat.is_null() { | |
| 1190 | + unsafe { | |
| 1191 | + *iostat = 0; | |
| 1192 | + } | |
| 1193 | + } | |
| 1194 | + } | |
| 1195 | + Err(_) => { | |
| 1196 | + if !iostat.is_null() { | |
| 1197 | + unsafe { | |
| 1198 | + *iostat = 1; | |
| 1199 | + } | |
| 1200 | + } | |
| 1201 | + } | |
| 915 | 1202 | } |
| 916 | 1203 | } |
| 917 | 1204 | } |
@@ -923,13 +1210,29 @@ pub extern "C" fn afs_seek_stream(unit: i32, pos: i64, iostat: *mut i32) { | ||
| 923 | 1210 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 924 | 1211 | if let Some(u) = state.get_unit(unit) { |
| 925 | 1212 | match &mut u.stream { |
| 926 | - UnitStream::FileRaw(f) => { | |
| 927 | - match f.seek(SeekFrom::Start(pos as u64)) { | |
| 928 | - Ok(_) => { if !iostat.is_null() { unsafe { *iostat = 0; } } } | |
| 929 | - Err(_) => { if !iostat.is_null() { unsafe { *iostat = 1; } } } | |
| 1213 | + UnitStream::FileRaw(f) => match f.seek(SeekFrom::Start(pos as u64)) { | |
| 1214 | + Ok(_) => { | |
| 1215 | + if !iostat.is_null() { | |
| 1216 | + unsafe { | |
| 1217 | + *iostat = 0; | |
| 1218 | + } | |
| 1219 | + } | |
| 1220 | + } | |
| 1221 | + Err(_) => { | |
| 1222 | + if !iostat.is_null() { | |
| 1223 | + unsafe { | |
| 1224 | + *iostat = 1; | |
| 1225 | + } | |
| 1226 | + } | |
| 1227 | + } | |
| 1228 | + }, | |
| 1229 | + _ => { | |
| 1230 | + if !iostat.is_null() { | |
| 1231 | + unsafe { | |
| 1232 | + *iostat = 1; | |
| 1233 | + } | |
| 930 | 1234 | } |
| 931 | 1235 | } |
| 932 | - _ => { if !iostat.is_null() { unsafe { *iostat = 1; } } } | |
| 933 | 1236 | } |
| 934 | 1237 | } |
| 935 | 1238 | } |
@@ -942,8 +1245,8 @@ pub struct NamelistEntry { | ||
| 942 | 1245 | pub name: *const u8, |
| 943 | 1246 | pub name_len: i64, |
| 944 | 1247 | pub data: *mut u8, |
| 945 | - pub data_type: i32, // 0=int, 1=real, 2=string, 3=logical | |
| 946 | - pub data_len: i64, // string length for type 2 | |
| 1248 | + pub data_type: i32, // 0=int, 1=real, 2=string, 3=logical | |
| 1249 | + pub data_len: i64, // string length for type 2 | |
| 947 | 1250 | } |
| 948 | 1251 | |
| 949 | 1252 | /// Write a NAMELIST group to a unit. |
@@ -951,8 +1254,10 @@ pub struct NamelistEntry { | ||
| 951 | 1254 | #[no_mangle] |
| 952 | 1255 | pub extern "C" fn afs_write_namelist( |
| 953 | 1256 | unit: i32, |
| 954 | - group_name: *const u8, group_name_len: i64, | |
| 955 | - entries: *const NamelistEntry, n_entries: i32, | |
| 1257 | + group_name: *const u8, | |
| 1258 | + group_name_len: i64, | |
| 1259 | + entries: *const NamelistEntry, | |
| 1260 | + n_entries: i32, | |
| 956 | 1261 | ) { |
| 957 | 1262 | let gname = unsafe_str(group_name, group_name_len); |
| 958 | 1263 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
@@ -965,19 +1270,23 @@ pub extern "C" fn afs_write_namelist( | ||
| 965 | 1270 | let name = unsafe_str(entry.name, entry.name_len); |
| 966 | 1271 | let sep = if i > 0 { "," } else { "" }; |
| 967 | 1272 | let val_str = match entry.data_type { |
| 968 | - 0 => { // integer | |
| 1273 | + 0 => { | |
| 1274 | + // integer | |
| 969 | 1275 | let v = unsafe { *(entry.data as *const i32) }; |
| 970 | 1276 | format!("{}", v) |
| 971 | 1277 | } |
| 972 | - 1 => { // real | |
| 1278 | + 1 => { | |
| 1279 | + // real | |
| 973 | 1280 | let v = unsafe { *(entry.data as *const f64) }; |
| 974 | 1281 | format!("{}", v) |
| 975 | 1282 | } |
| 976 | - 2 => { // string | |
| 1283 | + 2 => { | |
| 1284 | + // string | |
| 977 | 1285 | let s = unsafe_str(entry.data, entry.data_len); |
| 978 | 1286 | format!("'{}'", s.trim_end()) |
| 979 | 1287 | } |
| 980 | - 3 => { // logical | |
| 1288 | + 3 => { | |
| 1289 | + // logical | |
| 981 | 1290 | let v = unsafe { *(entry.data as *const i32) }; |
| 982 | 1291 | (if v != 0 { ".TRUE." } else { ".FALSE." }).to_string() |
| 983 | 1292 | } |
@@ -996,8 +1305,10 @@ pub extern "C" fn afs_write_namelist( | ||
| 996 | 1305 | #[no_mangle] |
| 997 | 1306 | pub extern "C" fn afs_read_namelist( |
| 998 | 1307 | unit: i32, |
| 999 | - group_name: *const u8, group_name_len: i64, | |
| 1000 | - entries: *const NamelistEntry, n_entries: i32, | |
| 1308 | + group_name: *const u8, | |
| 1309 | + group_name_len: i64, | |
| 1310 | + entries: *const NamelistEntry, | |
| 1311 | + n_entries: i32, | |
| 1001 | 1312 | iostat: *mut i32, |
| 1002 | 1313 | ) { |
| 1003 | 1314 | let gname = unsafe_str(group_name, group_name_len).to_lowercase(); |
@@ -1022,7 +1333,11 @@ pub extern "C" fn afs_read_namelist( | ||
| 1022 | 1333 | } |
| 1023 | 1334 | } |
| 1024 | 1335 | Err(_) => { |
| 1025 | - if !iostat.is_null() { unsafe { *iostat = IOSTAT_END; } } | |
| 1336 | + if !iostat.is_null() { | |
| 1337 | + unsafe { | |
| 1338 | + *iostat = IOSTAT_END; | |
| 1339 | + } | |
| 1340 | + } | |
| 1026 | 1341 | return; |
| 1027 | 1342 | } |
| 1028 | 1343 | } |
@@ -1036,8 +1351,12 @@ pub extern "C" fn afs_read_namelist( | ||
| 1036 | 1351 | let after_name = &all_lines[start + gname.len()..]; |
| 1037 | 1352 | if let Some(end) = after_name.find('/') { |
| 1038 | 1353 | &after_name[..end] |
| 1039 | - } else { after_name } | |
| 1040 | - } else { "" }; | |
| 1354 | + } else { | |
| 1355 | + after_name | |
| 1356 | + } | |
| 1357 | + } else { | |
| 1358 | + "" | |
| 1359 | + }; | |
| 1041 | 1360 | |
| 1042 | 1361 | // Parse var=val pairs. Supports: |
| 1043 | 1362 | // var=val — simple scalar assignment |
@@ -1047,12 +1366,12 @@ pub extern "C" fn afs_read_namelist( | ||
| 1047 | 1366 | let pair = pair.trim(); |
| 1048 | 1367 | if let Some(eq_pos) = pair.find('=') { |
| 1049 | 1368 | let lhs = pair[..eq_pos].trim().to_lowercase(); |
| 1050 | - let val_str = pair[eq_pos+1..].trim(); | |
| 1369 | + let val_str = pair[eq_pos + 1..].trim(); | |
| 1051 | 1370 | |
| 1052 | 1371 | // Parse array index from "var(idx)" syntax. |
| 1053 | 1372 | let (var_name, array_index) = if let Some(paren) = lhs.find('(') { |
| 1054 | 1373 | let name = lhs[..paren].trim(); |
| 1055 | - let idx_str = lhs[paren+1..].trim_end_matches(')').trim(); | |
| 1374 | + let idx_str = lhs[paren + 1..].trim_end_matches(')').trim(); | |
| 1056 | 1375 | let idx = idx_str.parse::<usize>().unwrap_or(1); |
| 1057 | 1376 | (name.to_string(), Some(idx)) |
| 1058 | 1377 | } else { |
@@ -1064,7 +1383,7 @@ pub extern "C" fn afs_read_namelist( | ||
| 1064 | 1383 | // Make sure * is preceded by digits (not part of a number like 1.5E*). |
| 1065 | 1384 | let before = val_str[..star].trim(); |
| 1066 | 1385 | if let Ok(n) = before.parse::<usize>() { |
| 1067 | - (n, val_str[star+1..].trim()) | |
| 1386 | + (n, val_str[star + 1..].trim()) | |
| 1068 | 1387 | } else { |
| 1069 | 1388 | (1, val_str) |
| 1070 | 1389 | } |
@@ -1074,7 +1393,9 @@ pub extern "C" fn afs_read_namelist( | ||
| 1074 | 1393 | |
| 1075 | 1394 | // Find the matching entry. |
| 1076 | 1395 | for entry in entries_slice { |
| 1077 | - if entry.data.is_null() { continue; } | |
| 1396 | + if entry.data.is_null() { | |
| 1397 | + continue; | |
| 1398 | + } | |
| 1078 | 1399 | let ename = unsafe_str(entry.name, entry.name_len).to_lowercase(); |
| 1079 | 1400 | if ename == var_name { |
| 1080 | 1401 | namelist_assign_value(entry, actual_val, array_index, repeat_count); |
@@ -1084,37 +1405,55 @@ pub extern "C" fn afs_read_namelist( | ||
| 1084 | 1405 | } |
| 1085 | 1406 | } |
| 1086 | 1407 | } |
| 1087 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1408 | + if !iostat.is_null() { | |
| 1409 | + unsafe { | |
| 1410 | + *iostat = 0; | |
| 1411 | + } | |
| 1412 | + } | |
| 1088 | 1413 | } |
| 1089 | 1414 | } |
| 1090 | 1415 | |
| 1091 | 1416 | /// Assign a parsed NAMELIST value to an entry, handling array indexing and repeat. |
| 1092 | -fn namelist_assign_value(entry: &NamelistEntry, val_str: &str, index: Option<usize>, repeat: usize) { | |
| 1417 | +fn namelist_assign_value( | |
| 1418 | + entry: &NamelistEntry, | |
| 1419 | + val_str: &str, | |
| 1420 | + index: Option<usize>, | |
| 1421 | + repeat: usize, | |
| 1422 | +) { | |
| 1093 | 1423 | // For array elements, compute byte offset from 1-based index. |
| 1094 | 1424 | let elem_size = match entry.data_type { |
| 1095 | - 0 => 4, // integer (i32) | |
| 1096 | - 1 => 8, // real (f64) | |
| 1097 | - 3 => 4, // logical (i32) | |
| 1098 | - _ => 1, // string | |
| 1425 | + 0 => 4, // integer (i32) | |
| 1426 | + 1 => 8, // real (f64) | |
| 1427 | + 3 => 4, // logical (i32) | |
| 1428 | + _ => 1, // string | |
| 1099 | 1429 | }; |
| 1100 | - let base_offset = index.map(|i| (i.saturating_sub(1)) * elem_size).unwrap_or(0); | |
| 1430 | + let base_offset = index | |
| 1431 | + .map(|i| (i.saturating_sub(1)) * elem_size) | |
| 1432 | + .unwrap_or(0); | |
| 1101 | 1433 | |
| 1102 | 1434 | for r in 0..repeat { |
| 1103 | 1435 | let offset = base_offset + r * elem_size; |
| 1104 | 1436 | let ptr = unsafe { entry.data.add(offset) }; |
| 1105 | 1437 | match entry.data_type { |
| 1106 | - 0 => { // integer | |
| 1438 | + 0 => { | |
| 1439 | + // integer | |
| 1107 | 1440 | if let Ok(v) = val_str.parse::<i32>() { |
| 1108 | - unsafe { *(ptr as *mut i32) = v; } | |
| 1441 | + unsafe { | |
| 1442 | + *(ptr as *mut i32) = v; | |
| 1443 | + } | |
| 1109 | 1444 | } |
| 1110 | 1445 | } |
| 1111 | - 1 => { // real | |
| 1446 | + 1 => { | |
| 1447 | + // real | |
| 1112 | 1448 | let normalized = val_str.replace('d', "e").replace('D', "E"); |
| 1113 | 1449 | if let Ok(v) = normalized.parse::<f64>() { |
| 1114 | - unsafe { *(ptr as *mut f64) = v; } | |
| 1450 | + unsafe { | |
| 1451 | + *(ptr as *mut f64) = v; | |
| 1452 | + } | |
| 1115 | 1453 | } |
| 1116 | 1454 | } |
| 1117 | - 2 => { // string (only first element for repeat, no array stride for strings) | |
| 1455 | + 2 => { | |
| 1456 | + // string (only first element for repeat, no array stride for strings) | |
| 1118 | 1457 | let s = val_str.trim_matches('\'').trim_matches('"'); |
| 1119 | 1458 | let bytes = s.as_bytes(); |
| 1120 | 1459 | let copy_len = bytes.len().min(entry.data_len as usize); |
@@ -1122,17 +1461,23 @@ fn namelist_assign_value(entry: &NamelistEntry, val_str: &str, index: Option<usi | ||
| 1122 | 1461 | unsafe { |
| 1123 | 1462 | std::ptr::copy_nonoverlapping(bytes.as_ptr(), entry.data, copy_len); |
| 1124 | 1463 | if copy_len < entry.data_len as usize { |
| 1125 | - std::ptr::write_bytes(entry.data.add(copy_len), b' ', | |
| 1126 | - entry.data_len as usize - copy_len); | |
| 1464 | + std::ptr::write_bytes( | |
| 1465 | + entry.data.add(copy_len), | |
| 1466 | + b' ', | |
| 1467 | + entry.data_len as usize - copy_len, | |
| 1468 | + ); | |
| 1127 | 1469 | } |
| 1128 | 1470 | } |
| 1129 | 1471 | } |
| 1130 | 1472 | return; // string repeat doesn't make sense |
| 1131 | 1473 | } |
| 1132 | - 3 => { // logical | |
| 1474 | + 3 => { | |
| 1475 | + // logical | |
| 1133 | 1476 | let lower = val_str.to_lowercase(); |
| 1134 | 1477 | let v = lower.starts_with(".t") || lower.starts_with("t"); |
| 1135 | - unsafe { *(ptr as *mut i32) = v as i32; } | |
| 1478 | + unsafe { | |
| 1479 | + *(ptr as *mut i32) = v as i32; | |
| 1480 | + } | |
| 1136 | 1481 | } |
| 1137 | 1482 | _ => {} |
| 1138 | 1483 | } |
@@ -1144,64 +1489,85 @@ fn namelist_assign_value(entry: &NamelistEntry, val_str: &str, index: Option<usi | ||
| 1144 | 1489 | /// Write a formatted integer to a character buffer (internal I/O). |
| 1145 | 1490 | #[no_mangle] |
| 1146 | 1491 | pub extern "C" fn afs_write_internal_int( |
| 1147 | - buf: *mut u8, buf_len: i64, | |
| 1492 | + buf: *mut u8, | |
| 1493 | + buf_len: i64, | |
| 1148 | 1494 | val: i32, |
| 1149 | 1495 | pos: *mut i64, // current write position, updated after write |
| 1150 | 1496 | ) { |
| 1151 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1497 | + if buf.is_null() || buf_len <= 0 { | |
| 1498 | + return; | |
| 1499 | + } | |
| 1152 | 1500 | let s = format!(" {}", val); |
| 1153 | - let start = if !pos.is_null() { (unsafe { *pos }) as usize } else { 0 }; | |
| 1501 | + let start = if !pos.is_null() { | |
| 1502 | + (unsafe { *pos }) as usize | |
| 1503 | + } else { | |
| 1504 | + 0 | |
| 1505 | + }; | |
| 1154 | 1506 | write_to_buffer(buf, buf_len as usize, start, s.as_bytes(), pos); |
| 1155 | 1507 | } |
| 1156 | 1508 | |
| 1157 | 1509 | /// Write a formatted i64 to a character buffer (internal I/O). |
| 1158 | 1510 | #[no_mangle] |
| 1159 | -pub extern "C" fn afs_write_internal_int64( | |
| 1160 | - buf: *mut u8, buf_len: i64, | |
| 1161 | - val: i64, | |
| 1162 | - pos: *mut i64, | |
| 1163 | -) { | |
| 1164 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1511 | +pub extern "C" fn afs_write_internal_int64(buf: *mut u8, buf_len: i64, val: i64, pos: *mut i64) { | |
| 1512 | + if buf.is_null() || buf_len <= 0 { | |
| 1513 | + return; | |
| 1514 | + } | |
| 1165 | 1515 | let s = format!(" {}", val); |
| 1166 | - let start = if !pos.is_null() { (unsafe { *pos }) as usize } else { 0 }; | |
| 1516 | + let start = if !pos.is_null() { | |
| 1517 | + (unsafe { *pos }) as usize | |
| 1518 | + } else { | |
| 1519 | + 0 | |
| 1520 | + }; | |
| 1167 | 1521 | write_to_buffer(buf, buf_len as usize, start, s.as_bytes(), pos); |
| 1168 | 1522 | } |
| 1169 | 1523 | |
| 1170 | 1524 | /// Write a formatted integer(16) to a character buffer (internal I/O). |
| 1171 | 1525 | #[no_mangle] |
| 1172 | -pub extern "C" fn afs_write_internal_int128( | |
| 1173 | - buf: *mut u8, buf_len: i64, | |
| 1174 | - val: i128, | |
| 1175 | - pos: *mut i64, | |
| 1176 | -) { | |
| 1177 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1526 | +pub extern "C" fn afs_write_internal_int128(buf: *mut u8, buf_len: i64, val: i128, pos: *mut i64) { | |
| 1527 | + if buf.is_null() || buf_len <= 0 { | |
| 1528 | + return; | |
| 1529 | + } | |
| 1178 | 1530 | let s = format!(" {}", val); |
| 1179 | - let start = if !pos.is_null() { (unsafe { *pos }) as usize } else { 0 }; | |
| 1531 | + let start = if !pos.is_null() { | |
| 1532 | + (unsafe { *pos }) as usize | |
| 1533 | + } else { | |
| 1534 | + 0 | |
| 1535 | + }; | |
| 1180 | 1536 | write_to_buffer(buf, buf_len as usize, start, s.as_bytes(), pos); |
| 1181 | 1537 | } |
| 1182 | 1538 | |
| 1183 | 1539 | /// Write a formatted real to a character buffer (internal I/O). |
| 1184 | 1540 | #[no_mangle] |
| 1185 | -pub extern "C" fn afs_write_internal_real64( | |
| 1186 | - buf: *mut u8, buf_len: i64, | |
| 1187 | - val: f64, | |
| 1188 | - pos: *mut i64, | |
| 1189 | -) { | |
| 1190 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1541 | +pub extern "C" fn afs_write_internal_real64(buf: *mut u8, buf_len: i64, val: f64, pos: *mut i64) { | |
| 1542 | + if buf.is_null() || buf_len <= 0 { | |
| 1543 | + return; | |
| 1544 | + } | |
| 1191 | 1545 | let s = format!(" {}", val); |
| 1192 | - let start = if !pos.is_null() { (unsafe { *pos }) as usize } else { 0 }; | |
| 1546 | + let start = if !pos.is_null() { | |
| 1547 | + (unsafe { *pos }) as usize | |
| 1548 | + } else { | |
| 1549 | + 0 | |
| 1550 | + }; | |
| 1193 | 1551 | write_to_buffer(buf, buf_len as usize, start, s.as_bytes(), pos); |
| 1194 | 1552 | } |
| 1195 | 1553 | |
| 1196 | 1554 | /// Write a formatted string to a character buffer (internal I/O). |
| 1197 | 1555 | #[no_mangle] |
| 1198 | 1556 | pub extern "C" fn afs_write_internal_string( |
| 1199 | - buf: *mut u8, buf_len: i64, | |
| 1200 | - src: *const u8, src_len: i64, | |
| 1557 | + buf: *mut u8, | |
| 1558 | + buf_len: i64, | |
| 1559 | + src: *const u8, | |
| 1560 | + src_len: i64, | |
| 1201 | 1561 | pos: *mut i64, |
| 1202 | 1562 | ) { |
| 1203 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1204 | - let start = if !pos.is_null() { (unsafe { *pos }) as usize } else { 0 }; | |
| 1563 | + if buf.is_null() || buf_len <= 0 { | |
| 1564 | + return; | |
| 1565 | + } | |
| 1566 | + let start = if !pos.is_null() { | |
| 1567 | + (unsafe { *pos }) as usize | |
| 1568 | + } else { | |
| 1569 | + 0 | |
| 1570 | + }; | |
| 1205 | 1571 | let mut data = vec![b' ']; |
| 1206 | 1572 | if !src.is_null() && src_len > 0 { |
| 1207 | 1573 | let slice = unsafe { std::slice::from_raw_parts(src, src_len as usize) }; |
@@ -1228,7 +1594,9 @@ fn next_internal_token(buf: *const u8, buf_len: i64, pos: *mut i64) -> Option<St | ||
| 1228 | 1594 | |
| 1229 | 1595 | if idx >= slice.len() { |
| 1230 | 1596 | if !pos.is_null() { |
| 1231 | - unsafe { *pos = idx as i64; } | |
| 1597 | + unsafe { | |
| 1598 | + *pos = idx as i64; | |
| 1599 | + } | |
| 1232 | 1600 | } |
| 1233 | 1601 | return None; |
| 1234 | 1602 | } |
@@ -1239,7 +1607,9 @@ fn next_internal_token(buf: *const u8, buf_len: i64, pos: *mut i64) -> Option<St | ||
| 1239 | 1607 | } |
| 1240 | 1608 | |
| 1241 | 1609 | if !pos.is_null() { |
| 1242 | - unsafe { *pos = idx as i64; } | |
| 1610 | + unsafe { | |
| 1611 | + *pos = idx as i64; | |
| 1612 | + } | |
| 1243 | 1613 | } |
| 1244 | 1614 | |
| 1245 | 1615 | Some(String::from_utf8_lossy(&slice[start..idx]).into_owned()) |
@@ -1248,7 +1618,8 @@ fn next_internal_token(buf: *const u8, buf_len: i64, pos: *mut i64) -> Option<St | ||
| 1248 | 1618 | /// Read an integer from a character buffer (internal I/O). |
| 1249 | 1619 | #[no_mangle] |
| 1250 | 1620 | pub extern "C" fn afs_read_internal_int( |
| 1251 | - buf: *const u8, buf_len: i64, | |
| 1621 | + buf: *const u8, | |
| 1622 | + buf_len: i64, | |
| 1252 | 1623 | pos: *mut i64, |
| 1253 | 1624 | val: *mut i32, |
| 1254 | 1625 | iostat: *mut i32, |
@@ -1256,22 +1627,39 @@ pub extern "C" fn afs_read_internal_int( | ||
| 1256 | 1627 | if let Some(token) = next_internal_token(buf, buf_len, pos) { |
| 1257 | 1628 | match token.replace(',', "").parse::<i32>() { |
| 1258 | 1629 | Ok(v) => { |
| 1259 | - if !val.is_null() { unsafe { *val = v; } } | |
| 1260 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1630 | + if !val.is_null() { | |
| 1631 | + unsafe { | |
| 1632 | + *val = v; | |
| 1633 | + } | |
| 1634 | + } | |
| 1635 | + if !iostat.is_null() { | |
| 1636 | + unsafe { | |
| 1637 | + *iostat = 0; | |
| 1638 | + } | |
| 1639 | + } | |
| 1261 | 1640 | } |
| 1262 | 1641 | Err(_) => { |
| 1263 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1642 | + if !iostat.is_null() { | |
| 1643 | + unsafe { | |
| 1644 | + *iostat = 1; | |
| 1645 | + } | |
| 1646 | + } | |
| 1264 | 1647 | } |
| 1265 | 1648 | } |
| 1266 | 1649 | } else { |
| 1267 | - if !iostat.is_null() { unsafe { *iostat = -1; } } | |
| 1650 | + if !iostat.is_null() { | |
| 1651 | + unsafe { | |
| 1652 | + *iostat = -1; | |
| 1653 | + } | |
| 1654 | + } | |
| 1268 | 1655 | } |
| 1269 | 1656 | } |
| 1270 | 1657 | |
| 1271 | 1658 | /// Read an i64 from a character buffer (internal I/O). |
| 1272 | 1659 | #[no_mangle] |
| 1273 | 1660 | pub extern "C" fn afs_read_internal_int64( |
| 1274 | - buf: *const u8, buf_len: i64, | |
| 1661 | + buf: *const u8, | |
| 1662 | + buf_len: i64, | |
| 1275 | 1663 | pos: *mut i64, |
| 1276 | 1664 | val: *mut i64, |
| 1277 | 1665 | iostat: *mut i32, |
@@ -1279,22 +1667,39 @@ pub extern "C" fn afs_read_internal_int64( | ||
| 1279 | 1667 | if let Some(token) = next_internal_token(buf, buf_len, pos) { |
| 1280 | 1668 | match token.replace(',', "").parse::<i64>() { |
| 1281 | 1669 | Ok(v) => { |
| 1282 | - if !val.is_null() { unsafe { *val = v; } } | |
| 1283 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1670 | + if !val.is_null() { | |
| 1671 | + unsafe { | |
| 1672 | + *val = v; | |
| 1673 | + } | |
| 1674 | + } | |
| 1675 | + if !iostat.is_null() { | |
| 1676 | + unsafe { | |
| 1677 | + *iostat = 0; | |
| 1678 | + } | |
| 1679 | + } | |
| 1284 | 1680 | } |
| 1285 | 1681 | Err(_) => { |
| 1286 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1682 | + if !iostat.is_null() { | |
| 1683 | + unsafe { | |
| 1684 | + *iostat = 1; | |
| 1685 | + } | |
| 1686 | + } | |
| 1287 | 1687 | } |
| 1288 | 1688 | } |
| 1289 | 1689 | } else { |
| 1290 | - if !iostat.is_null() { unsafe { *iostat = -1; } } | |
| 1690 | + if !iostat.is_null() { | |
| 1691 | + unsafe { | |
| 1692 | + *iostat = -1; | |
| 1693 | + } | |
| 1694 | + } | |
| 1291 | 1695 | } |
| 1292 | 1696 | } |
| 1293 | 1697 | |
| 1294 | 1698 | /// Read an integer(16) from a character buffer (internal I/O). |
| 1295 | 1699 | #[no_mangle] |
| 1296 | 1700 | pub extern "C" fn afs_read_internal_int128( |
| 1297 | - buf: *const u8, buf_len: i64, | |
| 1701 | + buf: *const u8, | |
| 1702 | + buf_len: i64, | |
| 1298 | 1703 | pos: *mut i64, |
| 1299 | 1704 | val: *mut i128, |
| 1300 | 1705 | iostat: *mut i32, |
@@ -1303,21 +1708,34 @@ pub extern "C" fn afs_read_internal_int128( | ||
| 1303 | 1708 | match token.replace(',', "").parse::<i128>() { |
| 1304 | 1709 | Ok(v) => { |
| 1305 | 1710 | write_i128_ptr(val, v); |
| 1306 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1711 | + if !iostat.is_null() { | |
| 1712 | + unsafe { | |
| 1713 | + *iostat = 0; | |
| 1714 | + } | |
| 1715 | + } | |
| 1307 | 1716 | } |
| 1308 | 1717 | Err(_) => { |
| 1309 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1718 | + if !iostat.is_null() { | |
| 1719 | + unsafe { | |
| 1720 | + *iostat = 1; | |
| 1721 | + } | |
| 1722 | + } | |
| 1310 | 1723 | } |
| 1311 | 1724 | } |
| 1312 | 1725 | } else { |
| 1313 | - if !iostat.is_null() { unsafe { *iostat = -1; } } | |
| 1726 | + if !iostat.is_null() { | |
| 1727 | + unsafe { | |
| 1728 | + *iostat = -1; | |
| 1729 | + } | |
| 1730 | + } | |
| 1314 | 1731 | } |
| 1315 | 1732 | } |
| 1316 | 1733 | |
| 1317 | 1734 | /// Read a real from a character buffer (internal I/O). |
| 1318 | 1735 | #[no_mangle] |
| 1319 | 1736 | pub extern "C" fn afs_read_internal_real( |
| 1320 | - buf: *const u8, buf_len: i64, | |
| 1737 | + buf: *const u8, | |
| 1738 | + buf_len: i64, | |
| 1321 | 1739 | pos: *mut i64, |
| 1322 | 1740 | val: *mut f64, |
| 1323 | 1741 | iostat: *mut i32, |
@@ -1326,15 +1744,31 @@ pub extern "C" fn afs_read_internal_real( | ||
| 1326 | 1744 | let normalized = token.replace('d', "e").replace('D', "E").replace(',', ""); |
| 1327 | 1745 | match normalized.parse::<f64>() { |
| 1328 | 1746 | Ok(v) => { |
| 1329 | - if !val.is_null() { unsafe { *val = v; } } | |
| 1330 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1747 | + if !val.is_null() { | |
| 1748 | + unsafe { | |
| 1749 | + *val = v; | |
| 1750 | + } | |
| 1751 | + } | |
| 1752 | + if !iostat.is_null() { | |
| 1753 | + unsafe { | |
| 1754 | + *iostat = 0; | |
| 1755 | + } | |
| 1756 | + } | |
| 1331 | 1757 | } |
| 1332 | 1758 | Err(_) => { |
| 1333 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 1759 | + if !iostat.is_null() { | |
| 1760 | + unsafe { | |
| 1761 | + *iostat = 1; | |
| 1762 | + } | |
| 1763 | + } | |
| 1334 | 1764 | } |
| 1335 | 1765 | } |
| 1336 | 1766 | } else { |
| 1337 | - if !iostat.is_null() { unsafe { *iostat = -1; } } | |
| 1767 | + if !iostat.is_null() { | |
| 1768 | + unsafe { | |
| 1769 | + *iostat = -1; | |
| 1770 | + } | |
| 1771 | + } | |
| 1338 | 1772 | } |
| 1339 | 1773 | } |
| 1340 | 1774 | |
@@ -1349,10 +1783,14 @@ fn write_to_buffer(buf: *mut u8, buf_len: usize, start: usize, data: &[u8], pos: | ||
| 1349 | 1783 | // Space-pad remaining buffer. |
| 1350 | 1784 | let end = start + copy_len; |
| 1351 | 1785 | if end < buf_len { |
| 1352 | - unsafe { std::ptr::write_bytes(buf.add(end), b' ', buf_len - end); } | |
| 1786 | + unsafe { | |
| 1787 | + std::ptr::write_bytes(buf.add(end), b' ', buf_len - end); | |
| 1788 | + } | |
| 1353 | 1789 | } |
| 1354 | 1790 | if !pos.is_null() { |
| 1355 | - unsafe { *pos = end as i64; } | |
| 1791 | + unsafe { | |
| 1792 | + *pos = end as i64; | |
| 1793 | + } | |
| 1356 | 1794 | } |
| 1357 | 1795 | } |
| 1358 | 1796 | |
@@ -1370,7 +1808,11 @@ pub extern "C" fn afs_backspace(unit: i32, iostat: *mut i32) { | ||
| 1370 | 1808 | let pos = f.stream_position().unwrap_or(0); |
| 1371 | 1809 | if pos <= 1 { |
| 1372 | 1810 | let _ = f.seek(SeekFrom::Start(0)); |
| 1373 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1811 | + if !iostat.is_null() { | |
| 1812 | + unsafe { | |
| 1813 | + *iostat = 0; | |
| 1814 | + } | |
| 1815 | + } | |
| 1374 | 1816 | // Clear stale read tokens. |
| 1375 | 1817 | u.read_tokens.clear(); |
| 1376 | 1818 | return; |
@@ -1380,7 +1822,9 @@ pub extern "C" fn afs_backspace(unit: i32, iostat: *mut i32) { | ||
| 1380 | 1822 | loop { |
| 1381 | 1823 | f.seek(SeekFrom::Start(search_pos)).ok(); |
| 1382 | 1824 | let mut byte = [0u8; 1]; |
| 1383 | - if f.read(&mut byte).unwrap_or(0) == 0 { break; } | |
| 1825 | + if f.read(&mut byte).unwrap_or(0) == 0 { | |
| 1826 | + break; | |
| 1827 | + } | |
| 1384 | 1828 | if byte[0] == b'\n' { |
| 1385 | 1829 | f.seek(SeekFrom::Start(search_pos + 1)).ok(); |
| 1386 | 1830 | break; |
@@ -1393,11 +1837,19 @@ pub extern "C" fn afs_backspace(unit: i32, iostat: *mut i32) { | ||
| 1393 | 1837 | } |
| 1394 | 1838 | // Clear stale read tokens after repositioning. |
| 1395 | 1839 | u.read_tokens.clear(); |
| 1396 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1840 | + if !iostat.is_null() { | |
| 1841 | + unsafe { | |
| 1842 | + *iostat = 0; | |
| 1843 | + } | |
| 1844 | + } | |
| 1397 | 1845 | } |
| 1398 | 1846 | _ => { |
| 1399 | 1847 | // Sequential buffered files: backspace is not well-supported. |
| 1400 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1848 | + if !iostat.is_null() { | |
| 1849 | + unsafe { | |
| 1850 | + *iostat = 0; | |
| 1851 | + } | |
| 1852 | + } | |
| 1401 | 1853 | } |
| 1402 | 1854 | } |
| 1403 | 1855 | } |
@@ -1413,7 +1865,11 @@ pub extern "C" fn afs_endfile(unit: i32, iostat: *mut i32) { | ||
| 1413 | 1865 | let pos = f.stream_position().unwrap_or(0); |
| 1414 | 1866 | let _ = f.set_len(pos); // truncate at current position |
| 1415 | 1867 | } |
| 1416 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1868 | + if !iostat.is_null() { | |
| 1869 | + unsafe { | |
| 1870 | + *iostat = 0; | |
| 1871 | + } | |
| 1872 | + } | |
| 1417 | 1873 | } |
| 1418 | 1874 | } |
| 1419 | 1875 | |
@@ -1429,7 +1885,9 @@ pub const IOSTAT_EOR: i32 = -2; | ||
| 1429 | 1885 | /// Write a Fortran-style string result into a caller-provided buffer. |
| 1430 | 1886 | /// Pads with spaces to buf_len (Fortran CHARACTER semantics). |
| 1431 | 1887 | fn write_inquire_string(buf: *mut u8, buf_len: i64, value: &str) { |
| 1432 | - if buf.is_null() || buf_len <= 0 { return; } | |
| 1888 | + if buf.is_null() || buf_len <= 0 { | |
| 1889 | + return; | |
| 1890 | + } | |
| 1433 | 1891 | let n = buf_len as usize; |
| 1434 | 1892 | let val_bytes = value.as_bytes(); |
| 1435 | 1893 | let copy_len = val_bytes.len().min(n); |
@@ -1445,15 +1903,20 @@ fn write_inquire_string(buf: *mut u8, buf_len: i64, value: &str) { | ||
| 1445 | 1903 | /// INQUIRE by file: check if a file exists, report its properties. |
| 1446 | 1904 | #[no_mangle] |
| 1447 | 1905 | pub extern "C" fn afs_inquire_file( |
| 1448 | - filename: *const u8, filename_len: i64, | |
| 1906 | + filename: *const u8, | |
| 1907 | + filename_len: i64, | |
| 1449 | 1908 | exist: *mut i32, |
| 1450 | 1909 | opened: *mut i32, |
| 1451 | 1910 | iostat: *mut i32, |
| 1452 | 1911 | // Extended specifiers — pass null for any not needed. |
| 1453 | - name_buf: *mut u8, name_buf_len: i64, | |
| 1454 | - access_buf: *mut u8, access_buf_len: i64, | |
| 1455 | - form_buf: *mut u8, form_buf_len: i64, | |
| 1456 | - action_buf: *mut u8, action_buf_len: i64, | |
| 1912 | + name_buf: *mut u8, | |
| 1913 | + name_buf_len: i64, | |
| 1914 | + access_buf: *mut u8, | |
| 1915 | + access_buf_len: i64, | |
| 1916 | + form_buf: *mut u8, | |
| 1917 | + form_buf_len: i64, | |
| 1918 | + action_buf: *mut u8, | |
| 1919 | + action_buf_len: i64, | |
| 1457 | 1920 | recl_out: *mut i64, |
| 1458 | 1921 | size_out: *mut i64, |
| 1459 | 1922 | ) { |
@@ -1461,7 +1924,9 @@ pub extern "C" fn afs_inquire_file( | ||
| 1461 | 1924 | |
| 1462 | 1925 | let file_exists = std::path::Path::new(&fname).exists(); |
| 1463 | 1926 | if !exist.is_null() { |
| 1464 | - unsafe { *exist = file_exists as i32; } | |
| 1927 | + unsafe { | |
| 1928 | + *exist = file_exists as i32; | |
| 1929 | + } | |
| 1465 | 1930 | } |
| 1466 | 1931 | |
| 1467 | 1932 | // Find unit connected to this file (if any). |
@@ -1469,14 +1934,24 @@ pub extern "C" fn afs_inquire_file( | ||
| 1469 | 1934 | let connected_unit = state.units.values().find(|u| u.filename == fname); |
| 1470 | 1935 | |
| 1471 | 1936 | if !opened.is_null() { |
| 1472 | - unsafe { *opened = connected_unit.is_some() as i32; } | |
| 1937 | + unsafe { | |
| 1938 | + *opened = connected_unit.is_some() as i32; | |
| 1939 | + } | |
| 1473 | 1940 | } |
| 1474 | 1941 | |
| 1475 | 1942 | write_inquire_string(name_buf, name_buf_len, &fname); |
| 1476 | 1943 | |
| 1477 | 1944 | if let Some(u) = connected_unit { |
| 1478 | - write_unit_properties(u, access_buf, access_buf_len, form_buf, form_buf_len, | |
| 1479 | - action_buf, action_buf_len, recl_out); | |
| 1945 | + write_unit_properties( | |
| 1946 | + u, | |
| 1947 | + access_buf, | |
| 1948 | + access_buf_len, | |
| 1949 | + form_buf, | |
| 1950 | + form_buf_len, | |
| 1951 | + action_buf, | |
| 1952 | + action_buf_len, | |
| 1953 | + recl_out, | |
| 1954 | + ); | |
| 1480 | 1955 | } else { |
| 1481 | 1956 | write_inquire_string(access_buf, access_buf_len, "UNDEFINED"); |
| 1482 | 1957 | write_inquire_string(form_buf, form_buf_len, "UNDEFINED"); |
@@ -1485,12 +1960,18 @@ pub extern "C" fn afs_inquire_file( | ||
| 1485 | 1960 | |
| 1486 | 1961 | // File size via metadata. |
| 1487 | 1962 | if !size_out.is_null() { |
| 1488 | - let sz = std::fs::metadata(&fname).map(|m| m.len() as i64).unwrap_or(-1); | |
| 1489 | - unsafe { *size_out = sz; } | |
| 1963 | + let sz = std::fs::metadata(&fname) | |
| 1964 | + .map(|m| m.len() as i64) | |
| 1965 | + .unwrap_or(-1); | |
| 1966 | + unsafe { | |
| 1967 | + *size_out = sz; | |
| 1968 | + } | |
| 1490 | 1969 | } |
| 1491 | 1970 | |
| 1492 | 1971 | if !iostat.is_null() { |
| 1493 | - unsafe { *iostat = 0; } | |
| 1972 | + unsafe { | |
| 1973 | + *iostat = 0; | |
| 1974 | + } | |
| 1494 | 1975 | } |
| 1495 | 1976 | } |
| 1496 | 1977 | |
@@ -1502,10 +1983,14 @@ pub extern "C" fn afs_inquire_unit( | ||
| 1502 | 1983 | opened: *mut i32, |
| 1503 | 1984 | iostat: *mut i32, |
| 1504 | 1985 | // Extended specifiers. |
| 1505 | - name_buf: *mut u8, name_buf_len: i64, | |
| 1506 | - access_buf: *mut u8, access_buf_len: i64, | |
| 1507 | - form_buf: *mut u8, form_buf_len: i64, | |
| 1508 | - action_buf: *mut u8, action_buf_len: i64, | |
| 1986 | + name_buf: *mut u8, | |
| 1987 | + name_buf_len: i64, | |
| 1988 | + access_buf: *mut u8, | |
| 1989 | + access_buf_len: i64, | |
| 1990 | + form_buf: *mut u8, | |
| 1991 | + form_buf_len: i64, | |
| 1992 | + action_buf: *mut u8, | |
| 1993 | + action_buf_len: i64, | |
| 1509 | 1994 | recl_out: *mut i64, |
| 1510 | 1995 | size_out: *mut i64, |
| 1511 | 1996 | ) { |
@@ -1513,42 +1998,69 @@ pub extern "C" fn afs_inquire_unit( | ||
| 1513 | 1998 | let unit_entry = state.units.get(&unit); |
| 1514 | 1999 | |
| 1515 | 2000 | if !exist.is_null() { |
| 1516 | - unsafe { *exist = unit_entry.is_some() as i32; } | |
| 2001 | + unsafe { | |
| 2002 | + *exist = unit_entry.is_some() as i32; | |
| 2003 | + } | |
| 1517 | 2004 | } |
| 1518 | 2005 | if !opened.is_null() { |
| 1519 | - unsafe { *opened = unit_entry.is_some() as i32; } | |
| 2006 | + unsafe { | |
| 2007 | + *opened = unit_entry.is_some() as i32; | |
| 2008 | + } | |
| 1520 | 2009 | } |
| 1521 | 2010 | |
| 1522 | 2011 | if let Some(u) = unit_entry { |
| 1523 | 2012 | write_inquire_string(name_buf, name_buf_len, &u.filename); |
| 1524 | - write_unit_properties(u, access_buf, access_buf_len, form_buf, form_buf_len, | |
| 1525 | - action_buf, action_buf_len, recl_out); | |
| 2013 | + write_unit_properties( | |
| 2014 | + u, | |
| 2015 | + access_buf, | |
| 2016 | + access_buf_len, | |
| 2017 | + form_buf, | |
| 2018 | + form_buf_len, | |
| 2019 | + action_buf, | |
| 2020 | + action_buf_len, | |
| 2021 | + recl_out, | |
| 2022 | + ); | |
| 1526 | 2023 | |
| 1527 | 2024 | if !size_out.is_null() { |
| 1528 | 2025 | let sz = if !u.filename.is_empty() { |
| 1529 | - std::fs::metadata(&u.filename).map(|m| m.len() as i64).unwrap_or(-1) | |
| 1530 | - } else { -1 }; | |
| 1531 | - unsafe { *size_out = sz; } | |
| 2026 | + std::fs::metadata(&u.filename) | |
| 2027 | + .map(|m| m.len() as i64) | |
| 2028 | + .unwrap_or(-1) | |
| 2029 | + } else { | |
| 2030 | + -1 | |
| 2031 | + }; | |
| 2032 | + unsafe { | |
| 2033 | + *size_out = sz; | |
| 2034 | + } | |
| 1532 | 2035 | } |
| 1533 | 2036 | } else { |
| 1534 | 2037 | write_inquire_string(name_buf, name_buf_len, ""); |
| 1535 | 2038 | write_inquire_string(access_buf, access_buf_len, "UNDEFINED"); |
| 1536 | 2039 | write_inquire_string(form_buf, form_buf_len, "UNDEFINED"); |
| 1537 | 2040 | write_inquire_string(action_buf, action_buf_len, "UNDEFINED"); |
| 1538 | - if !size_out.is_null() { unsafe { *size_out = -1; } } | |
| 2041 | + if !size_out.is_null() { | |
| 2042 | + unsafe { | |
| 2043 | + *size_out = -1; | |
| 2044 | + } | |
| 2045 | + } | |
| 1539 | 2046 | } |
| 1540 | 2047 | |
| 1541 | 2048 | if !iostat.is_null() { |
| 1542 | - unsafe { *iostat = 0; } | |
| 2049 | + unsafe { | |
| 2050 | + *iostat = 0; | |
| 2051 | + } | |
| 1543 | 2052 | } |
| 1544 | 2053 | } |
| 1545 | 2054 | |
| 1546 | 2055 | /// Write ACCESS, FORM, ACTION, RECL for a connected unit. |
| 1547 | 2056 | fn write_unit_properties( |
| 1548 | 2057 | u: &Unit, |
| 1549 | - access_buf: *mut u8, access_buf_len: i64, | |
| 1550 | - form_buf: *mut u8, form_buf_len: i64, | |
| 1551 | - action_buf: *mut u8, action_buf_len: i64, | |
| 2058 | + access_buf: *mut u8, | |
| 2059 | + access_buf_len: i64, | |
| 2060 | + form_buf: *mut u8, | |
| 2061 | + form_buf_len: i64, | |
| 2062 | + action_buf: *mut u8, | |
| 2063 | + action_buf_len: i64, | |
| 1552 | 2064 | recl_out: *mut i64, |
| 1553 | 2065 | ) { |
| 1554 | 2066 | let access_str = match u.access { |
@@ -1572,7 +2084,9 @@ fn write_unit_properties( | ||
| 1572 | 2084 | write_inquire_string(action_buf, action_buf_len, action_str); |
| 1573 | 2085 | |
| 1574 | 2086 | if !recl_out.is_null() { |
| 1575 | - unsafe { *recl_out = u.recl.unwrap_or(-1); } | |
| 2087 | + unsafe { | |
| 2088 | + *recl_out = u.recl.unwrap_or(-1); | |
| 2089 | + } | |
| 1576 | 2090 | } |
| 1577 | 2091 | } |
| 1578 | 2092 | |
@@ -1584,10 +2098,18 @@ pub extern "C" fn afs_flush(unit: i32, iostat: *mut i32) { | ||
| 1584 | 2098 | let mut state = io_state().lock().unwrap_or_else(|e| e.into_inner()); |
| 1585 | 2099 | if let Some(u) = state.get_unit(unit) { |
| 1586 | 2100 | match u.flush() { |
| 1587 | - Ok(()) => { if !iostat.is_null() { unsafe { *iostat = 0; } } } | |
| 2101 | + Ok(()) => { | |
| 2102 | + if !iostat.is_null() { | |
| 2103 | + unsafe { | |
| 2104 | + *iostat = 0; | |
| 2105 | + } | |
| 2106 | + } | |
| 2107 | + } | |
| 1588 | 2108 | Err(e) => { |
| 1589 | 2109 | if !iostat.is_null() { |
| 1590 | - unsafe { *iostat = e.raw_os_error().unwrap_or(1); } | |
| 2110 | + unsafe { | |
| 2111 | + *iostat = e.raw_os_error().unwrap_or(1); | |
| 2112 | + } | |
| 1591 | 2113 | } |
| 1592 | 2114 | } |
| 1593 | 2115 | } |
@@ -1616,7 +2138,11 @@ pub extern "C" fn afs_rewind(unit: i32, iostat: *mut i32) { | ||
| 1616 | 2138 | } |
| 1617 | 2139 | // Clear stale read tokens so subsequent reads come from file start. |
| 1618 | 2140 | u.read_tokens.clear(); |
| 1619 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2141 | + if !iostat.is_null() { | |
| 2142 | + unsafe { | |
| 2143 | + *iostat = 0; | |
| 2144 | + } | |
| 2145 | + } | |
| 1620 | 2146 | } |
| 1621 | 2147 | } |
| 1622 | 2148 | |
@@ -1649,8 +2175,8 @@ pub extern "C" fn afs_io_finalize() { | ||
| 1649 | 2175 | // afs_fmt_push_int(val) / afs_fmt_push_int128(&val) / afs_fmt_push_real(val) / ... |
| 1650 | 2176 | // afs_fmt_end() |
| 1651 | 2177 | |
| 1652 | -use std::cell::RefCell; | |
| 1653 | 2178 | use crate::format::{parse_format, FormatDesc, FormatEngine, IoValue}; |
| 2179 | +use std::cell::RefCell; | |
| 1654 | 2180 | |
| 1655 | 2181 | enum FmtSink { |
| 1656 | 2182 | Unit(i32), |
@@ -1857,7 +2383,10 @@ fn extract_nth_formatted_field( | ||
| 1857 | 2383 | ) -> Option<(FormatDesc, String)> { |
| 1858 | 2384 | for desc in descs { |
| 1859 | 2385 | match desc { |
| 1860 | - FormatDesc::Group { repeat, descriptors } => { | |
| 2386 | + FormatDesc::Group { | |
| 2387 | + repeat, | |
| 2388 | + descriptors, | |
| 2389 | + } => { | |
| 1861 | 2390 | for _ in 0..*repeat { |
| 1862 | 2391 | if let Some(found) = extract_nth_formatted_field( |
| 1863 | 2392 | descriptors, |
@@ -1970,20 +2499,42 @@ pub extern "C" fn afs_fmt_read_int( | ||
| 1970 | 2499 | match formatted_read_record_for_unit(unit, data_index) |
| 1971 | 2500 | .and_then(|line| parse_nth_formatted_record(line.as_bytes(), fmt_str, fmt_len, data_index)) |
| 1972 | 2501 | { |
| 1973 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i32>() { | |
| 1974 | - Ok(v) => { | |
| 1975 | - if !val.is_null() { unsafe { *val = v; } } | |
| 1976 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 1977 | - } | |
| 1978 | - Err(_) => { | |
| 1979 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2502 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2503 | + match field.trim().replace(',', "").parse::<i32>() { | |
| 2504 | + Ok(v) => { | |
| 2505 | + if !val.is_null() { | |
| 2506 | + unsafe { | |
| 2507 | + *val = v; | |
| 2508 | + } | |
| 2509 | + } | |
| 2510 | + if !iostat.is_null() { | |
| 2511 | + unsafe { | |
| 2512 | + *iostat = 0; | |
| 2513 | + } | |
| 2514 | + } | |
| 2515 | + } | |
| 2516 | + Err(_) => { | |
| 2517 | + if !iostat.is_null() { | |
| 2518 | + unsafe { | |
| 2519 | + *iostat = 1; | |
| 2520 | + } | |
| 2521 | + } | |
| 2522 | + } | |
| 1980 | 2523 | } |
| 1981 | - }, | |
| 2524 | + } | |
| 1982 | 2525 | Ok(_) => { |
| 1983 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2526 | + if !iostat.is_null() { | |
| 2527 | + unsafe { | |
| 2528 | + *iostat = 1; | |
| 2529 | + } | |
| 2530 | + } | |
| 1984 | 2531 | } |
| 1985 | 2532 | Err(code) => { |
| 1986 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2533 | + if !iostat.is_null() { | |
| 2534 | + unsafe { | |
| 2535 | + *iostat = code; | |
| 2536 | + } | |
| 2537 | + } | |
| 1987 | 2538 | } |
| 1988 | 2539 | } |
| 1989 | 2540 | } |
@@ -2000,20 +2551,42 @@ pub extern "C" fn afs_fmt_read_int64( | ||
| 2000 | 2551 | match formatted_read_record_for_unit(unit, data_index) |
| 2001 | 2552 | .and_then(|line| parse_nth_formatted_record(line.as_bytes(), fmt_str, fmt_len, data_index)) |
| 2002 | 2553 | { |
| 2003 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i64>() { | |
| 2004 | - Ok(v) => { | |
| 2005 | - if !val.is_null() { unsafe { *val = v; } } | |
| 2006 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2007 | - } | |
| 2008 | - Err(_) => { | |
| 2009 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2554 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2555 | + match field.trim().replace(',', "").parse::<i64>() { | |
| 2556 | + Ok(v) => { | |
| 2557 | + if !val.is_null() { | |
| 2558 | + unsafe { | |
| 2559 | + *val = v; | |
| 2560 | + } | |
| 2561 | + } | |
| 2562 | + if !iostat.is_null() { | |
| 2563 | + unsafe { | |
| 2564 | + *iostat = 0; | |
| 2565 | + } | |
| 2566 | + } | |
| 2567 | + } | |
| 2568 | + Err(_) => { | |
| 2569 | + if !iostat.is_null() { | |
| 2570 | + unsafe { | |
| 2571 | + *iostat = 1; | |
| 2572 | + } | |
| 2573 | + } | |
| 2574 | + } | |
| 2010 | 2575 | } |
| 2011 | - }, | |
| 2576 | + } | |
| 2012 | 2577 | Ok(_) => { |
| 2013 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2578 | + if !iostat.is_null() { | |
| 2579 | + unsafe { | |
| 2580 | + *iostat = 1; | |
| 2581 | + } | |
| 2582 | + } | |
| 2014 | 2583 | } |
| 2015 | 2584 | Err(code) => { |
| 2016 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2585 | + if !iostat.is_null() { | |
| 2586 | + unsafe { | |
| 2587 | + *iostat = code; | |
| 2588 | + } | |
| 2589 | + } | |
| 2017 | 2590 | } |
| 2018 | 2591 | } |
| 2019 | 2592 | } |
@@ -2030,20 +2603,38 @@ pub extern "C" fn afs_fmt_read_int128( | ||
| 2030 | 2603 | match formatted_read_record_for_unit(unit, data_index) |
| 2031 | 2604 | .and_then(|line| parse_nth_formatted_record(line.as_bytes(), fmt_str, fmt_len, data_index)) |
| 2032 | 2605 | { |
| 2033 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i128>() { | |
| 2034 | - Ok(v) => { | |
| 2035 | - write_i128_ptr(val, v); | |
| 2036 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2037 | - } | |
| 2038 | - Err(_) => { | |
| 2039 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2606 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2607 | + match field.trim().replace(',', "").parse::<i128>() { | |
| 2608 | + Ok(v) => { | |
| 2609 | + write_i128_ptr(val, v); | |
| 2610 | + if !iostat.is_null() { | |
| 2611 | + unsafe { | |
| 2612 | + *iostat = 0; | |
| 2613 | + } | |
| 2614 | + } | |
| 2615 | + } | |
| 2616 | + Err(_) => { | |
| 2617 | + if !iostat.is_null() { | |
| 2618 | + unsafe { | |
| 2619 | + *iostat = 1; | |
| 2620 | + } | |
| 2621 | + } | |
| 2622 | + } | |
| 2040 | 2623 | } |
| 2041 | - }, | |
| 2624 | + } | |
| 2042 | 2625 | Ok(_) => { |
| 2043 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2626 | + if !iostat.is_null() { | |
| 2627 | + unsafe { | |
| 2628 | + *iostat = 1; | |
| 2629 | + } | |
| 2630 | + } | |
| 2044 | 2631 | } |
| 2045 | 2632 | Err(code) => { |
| 2046 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2633 | + if !iostat.is_null() { | |
| 2634 | + unsafe { | |
| 2635 | + *iostat = code; | |
| 2636 | + } | |
| 2637 | + } | |
| 2047 | 2638 | } |
| 2048 | 2639 | } |
| 2049 | 2640 | } |
@@ -2067,22 +2658,46 @@ pub extern "C" fn afs_fmt_read_real( | ||
| 2067 | 2658 | | Ok((FormatDesc::RealEX { .. }, field)) |
| 2068 | 2659 | | Ok((FormatDesc::RealD { .. }, field)) |
| 2069 | 2660 | | Ok((FormatDesc::RealG { .. }, field)) => { |
| 2070 | - let normalized = field.trim().replace('d', "e").replace('D', "E").replace(',', ""); | |
| 2661 | + let normalized = field | |
| 2662 | + .trim() | |
| 2663 | + .replace('d', "e") | |
| 2664 | + .replace('D', "E") | |
| 2665 | + .replace(',', ""); | |
| 2071 | 2666 | match normalized.parse::<f64>() { |
| 2072 | 2667 | Ok(v) => { |
| 2073 | - if !val.is_null() { unsafe { *val = v; } } | |
| 2074 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2668 | + if !val.is_null() { | |
| 2669 | + unsafe { | |
| 2670 | + *val = v; | |
| 2671 | + } | |
| 2672 | + } | |
| 2673 | + if !iostat.is_null() { | |
| 2674 | + unsafe { | |
| 2675 | + *iostat = 0; | |
| 2676 | + } | |
| 2677 | + } | |
| 2075 | 2678 | } |
| 2076 | 2679 | Err(_) => { |
| 2077 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2680 | + if !iostat.is_null() { | |
| 2681 | + unsafe { | |
| 2682 | + *iostat = 1; | |
| 2683 | + } | |
| 2684 | + } | |
| 2078 | 2685 | } |
| 2079 | 2686 | } |
| 2080 | 2687 | } |
| 2081 | 2688 | Ok(_) => { |
| 2082 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2689 | + if !iostat.is_null() { | |
| 2690 | + unsafe { | |
| 2691 | + *iostat = 1; | |
| 2692 | + } | |
| 2693 | + } | |
| 2083 | 2694 | } |
| 2084 | 2695 | Err(code) => { |
| 2085 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2696 | + if !iostat.is_null() { | |
| 2697 | + unsafe { | |
| 2698 | + *iostat = code; | |
| 2699 | + } | |
| 2700 | + } | |
| 2086 | 2701 | } |
| 2087 | 2702 | } |
| 2088 | 2703 | } |
@@ -2098,20 +2713,42 @@ pub extern "C" fn afs_fmt_read_int_internal( | ||
| 2098 | 2713 | iostat: *mut i32, |
| 2099 | 2714 | ) { |
| 2100 | 2715 | match parse_nth_formatted_internal_field(buf, buf_len, fmt_str, fmt_len, data_index) { |
| 2101 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i32>() { | |
| 2102 | - Ok(v) => { | |
| 2103 | - if !val.is_null() { unsafe { *val = v; } } | |
| 2104 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2105 | - } | |
| 2106 | - Err(_) => { | |
| 2107 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2716 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2717 | + match field.trim().replace(',', "").parse::<i32>() { | |
| 2718 | + Ok(v) => { | |
| 2719 | + if !val.is_null() { | |
| 2720 | + unsafe { | |
| 2721 | + *val = v; | |
| 2722 | + } | |
| 2723 | + } | |
| 2724 | + if !iostat.is_null() { | |
| 2725 | + unsafe { | |
| 2726 | + *iostat = 0; | |
| 2727 | + } | |
| 2728 | + } | |
| 2729 | + } | |
| 2730 | + Err(_) => { | |
| 2731 | + if !iostat.is_null() { | |
| 2732 | + unsafe { | |
| 2733 | + *iostat = 1; | |
| 2734 | + } | |
| 2735 | + } | |
| 2736 | + } | |
| 2108 | 2737 | } |
| 2109 | - }, | |
| 2738 | + } | |
| 2110 | 2739 | Ok(_) => { |
| 2111 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2740 | + if !iostat.is_null() { | |
| 2741 | + unsafe { | |
| 2742 | + *iostat = 1; | |
| 2743 | + } | |
| 2744 | + } | |
| 2112 | 2745 | } |
| 2113 | 2746 | Err(code) => { |
| 2114 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2747 | + if !iostat.is_null() { | |
| 2748 | + unsafe { | |
| 2749 | + *iostat = code; | |
| 2750 | + } | |
| 2751 | + } | |
| 2115 | 2752 | } |
| 2116 | 2753 | } |
| 2117 | 2754 | } |
@@ -2127,20 +2764,42 @@ pub extern "C" fn afs_fmt_read_int64_internal( | ||
| 2127 | 2764 | iostat: *mut i32, |
| 2128 | 2765 | ) { |
| 2129 | 2766 | match parse_nth_formatted_internal_field(buf, buf_len, fmt_str, fmt_len, data_index) { |
| 2130 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i64>() { | |
| 2131 | - Ok(v) => { | |
| 2132 | - if !val.is_null() { unsafe { *val = v; } } | |
| 2133 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2134 | - } | |
| 2135 | - Err(_) => { | |
| 2136 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2767 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2768 | + match field.trim().replace(',', "").parse::<i64>() { | |
| 2769 | + Ok(v) => { | |
| 2770 | + if !val.is_null() { | |
| 2771 | + unsafe { | |
| 2772 | + *val = v; | |
| 2773 | + } | |
| 2774 | + } | |
| 2775 | + if !iostat.is_null() { | |
| 2776 | + unsafe { | |
| 2777 | + *iostat = 0; | |
| 2778 | + } | |
| 2779 | + } | |
| 2780 | + } | |
| 2781 | + Err(_) => { | |
| 2782 | + if !iostat.is_null() { | |
| 2783 | + unsafe { | |
| 2784 | + *iostat = 1; | |
| 2785 | + } | |
| 2786 | + } | |
| 2787 | + } | |
| 2137 | 2788 | } |
| 2138 | - }, | |
| 2789 | + } | |
| 2139 | 2790 | Ok(_) => { |
| 2140 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2791 | + if !iostat.is_null() { | |
| 2792 | + unsafe { | |
| 2793 | + *iostat = 1; | |
| 2794 | + } | |
| 2795 | + } | |
| 2141 | 2796 | } |
| 2142 | 2797 | Err(code) => { |
| 2143 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2798 | + if !iostat.is_null() { | |
| 2799 | + unsafe { | |
| 2800 | + *iostat = code; | |
| 2801 | + } | |
| 2802 | + } | |
| 2144 | 2803 | } |
| 2145 | 2804 | } |
| 2146 | 2805 | } |
@@ -2156,20 +2815,38 @@ pub extern "C" fn afs_fmt_read_int128_internal( | ||
| 2156 | 2815 | iostat: *mut i32, |
| 2157 | 2816 | ) { |
| 2158 | 2817 | match parse_nth_formatted_internal_field(buf, buf_len, fmt_str, fmt_len, data_index) { |
| 2159 | - Ok((FormatDesc::IntegerI { .. }, field)) => match field.trim().replace(',', "").parse::<i128>() { | |
| 2160 | - Ok(v) => { | |
| 2161 | - write_i128_ptr(val, v); | |
| 2162 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2163 | - } | |
| 2164 | - Err(_) => { | |
| 2165 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2818 | + Ok((FormatDesc::IntegerI { .. }, field)) => { | |
| 2819 | + match field.trim().replace(',', "").parse::<i128>() { | |
| 2820 | + Ok(v) => { | |
| 2821 | + write_i128_ptr(val, v); | |
| 2822 | + if !iostat.is_null() { | |
| 2823 | + unsafe { | |
| 2824 | + *iostat = 0; | |
| 2825 | + } | |
| 2826 | + } | |
| 2827 | + } | |
| 2828 | + Err(_) => { | |
| 2829 | + if !iostat.is_null() { | |
| 2830 | + unsafe { | |
| 2831 | + *iostat = 1; | |
| 2832 | + } | |
| 2833 | + } | |
| 2834 | + } | |
| 2166 | 2835 | } |
| 2167 | - }, | |
| 2836 | + } | |
| 2168 | 2837 | Ok(_) => { |
| 2169 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2838 | + if !iostat.is_null() { | |
| 2839 | + unsafe { | |
| 2840 | + *iostat = 1; | |
| 2841 | + } | |
| 2842 | + } | |
| 2170 | 2843 | } |
| 2171 | 2844 | Err(code) => { |
| 2172 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2845 | + if !iostat.is_null() { | |
| 2846 | + unsafe { | |
| 2847 | + *iostat = code; | |
| 2848 | + } | |
| 2849 | + } | |
| 2173 | 2850 | } |
| 2174 | 2851 | } |
| 2175 | 2852 | } |
@@ -2192,22 +2869,46 @@ pub extern "C" fn afs_fmt_read_real_internal( | ||
| 2192 | 2869 | | Ok((FormatDesc::RealEX { .. }, field)) |
| 2193 | 2870 | | Ok((FormatDesc::RealD { .. }, field)) |
| 2194 | 2871 | | Ok((FormatDesc::RealG { .. }, field)) => { |
| 2195 | - let normalized = field.trim().replace('d', "e").replace('D', "E").replace(',', ""); | |
| 2872 | + let normalized = field | |
| 2873 | + .trim() | |
| 2874 | + .replace('d', "e") | |
| 2875 | + .replace('D', "E") | |
| 2876 | + .replace(',', ""); | |
| 2196 | 2877 | match normalized.parse::<f64>() { |
| 2197 | 2878 | Ok(v) => { |
| 2198 | - if !val.is_null() { unsafe { *val = v; } } | |
| 2199 | - if !iostat.is_null() { unsafe { *iostat = 0; } } | |
| 2879 | + if !val.is_null() { | |
| 2880 | + unsafe { | |
| 2881 | + *val = v; | |
| 2882 | + } | |
| 2883 | + } | |
| 2884 | + if !iostat.is_null() { | |
| 2885 | + unsafe { | |
| 2886 | + *iostat = 0; | |
| 2887 | + } | |
| 2888 | + } | |
| 2200 | 2889 | } |
| 2201 | 2890 | Err(_) => { |
| 2202 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2891 | + if !iostat.is_null() { | |
| 2892 | + unsafe { | |
| 2893 | + *iostat = 1; | |
| 2894 | + } | |
| 2895 | + } | |
| 2203 | 2896 | } |
| 2204 | 2897 | } |
| 2205 | 2898 | } |
| 2206 | 2899 | Ok(_) => { |
| 2207 | - if !iostat.is_null() { unsafe { *iostat = 1; } } | |
| 2900 | + if !iostat.is_null() { | |
| 2901 | + unsafe { | |
| 2902 | + *iostat = 1; | |
| 2903 | + } | |
| 2904 | + } | |
| 2208 | 2905 | } |
| 2209 | 2906 | Err(code) => { |
| 2210 | - if !iostat.is_null() { unsafe { *iostat = code; } } | |
| 2907 | + if !iostat.is_null() { | |
| 2908 | + unsafe { | |
| 2909 | + *iostat = code; | |
| 2910 | + } | |
| 2911 | + } | |
| 2211 | 2912 | } |
| 2212 | 2913 | } |
| 2213 | 2914 | } |
@@ -2245,9 +2946,12 @@ mod tests { | ||
| 2245 | 2946 | let path = "/tmp/afs_write_i128_test.dat"; |
| 2246 | 2947 | afs_open_simple( |
| 2247 | 2948 | 97, |
| 2248 | - path.as_ptr(), path.len() as i64, | |
| 2249 | - "replace".as_ptr(), 7, | |
| 2250 | - std::ptr::null(), 0, | |
| 2949 | + path.as_ptr(), | |
| 2950 | + path.len() as i64, | |
| 2951 | + "replace".as_ptr(), | |
| 2952 | + 7, | |
| 2953 | + std::ptr::null(), | |
| 2954 | + 0, | |
| 2251 | 2955 | ); |
| 2252 | 2956 | |
| 2253 | 2957 | afs_write_int128(97, 170141183460469231731687303715884105727i128); |
@@ -2269,9 +2973,12 @@ mod tests { | ||
| 2269 | 2973 | |
| 2270 | 2974 | afs_open_simple( |
| 2271 | 2975 | 95, |
| 2272 | - path.as_ptr(), path.len() as i64, | |
| 2273 | - "old".as_ptr(), 3, | |
| 2274 | - "read".as_ptr(), 4, | |
| 2976 | + path.as_ptr(), | |
| 2977 | + path.len() as i64, | |
| 2978 | + "old".as_ptr(), | |
| 2979 | + 3, | |
| 2980 | + "read".as_ptr(), | |
| 2981 | + 4, | |
| 2275 | 2982 | ); |
| 2276 | 2983 | |
| 2277 | 2984 | let mut value = 0i128; |
@@ -2335,13 +3042,7 @@ mod tests { | ||
| 2335 | 3042 | let ptr = unsafe { raw.as_mut_ptr().add(1) as *mut i128 }; |
| 2336 | 3043 | let mut iostat = -99i32; |
| 2337 | 3044 | |
| 2338 | - afs_read_internal_int128( | |
| 2339 | - buf.as_ptr(), | |
| 2340 | - buf.len() as i64, | |
| 2341 | - &mut pos, | |
| 2342 | - ptr, | |
| 2343 | - &mut iostat, | |
| 2344 | - ); | |
| 3045 | + afs_read_internal_int128(buf.as_ptr(), buf.len() as i64, &mut pos, ptr, &mut iostat); | |
| 2345 | 3046 | |
| 2346 | 3047 | assert_eq!(iostat, 0, "expected internal i128 read to succeed"); |
| 2347 | 3048 | let value = unsafe { std::ptr::read_unaligned(ptr) }; |
@@ -2353,9 +3054,12 @@ mod tests { | ||
| 2353 | 3054 | let path = "/tmp/afs_fmt_test.dat"; |
| 2354 | 3055 | afs_open_simple( |
| 2355 | 3056 | 99, |
| 2356 | - path.as_ptr(), path.len() as i64, | |
| 2357 | - "replace".as_ptr(), 7, | |
| 2358 | - std::ptr::null(), 0, | |
| 3057 | + path.as_ptr(), | |
| 3058 | + path.len() as i64, | |
| 3059 | + "replace".as_ptr(), | |
| 3060 | + 7, | |
| 3061 | + std::ptr::null(), | |
| 3062 | + 0, | |
| 2359 | 3063 | ); |
| 2360 | 3064 | |
| 2361 | 3065 | afs_fmt_begin(99, "(I5, F8.2)".as_ptr(), 10); |
@@ -2376,9 +3080,12 @@ mod tests { | ||
| 2376 | 3080 | let wide = 170141183460469231731687303715884105727i128; |
| 2377 | 3081 | afs_open_simple( |
| 2378 | 3082 | 96, |
| 2379 | - path.as_ptr(), path.len() as i64, | |
| 2380 | - "replace".as_ptr(), 7, | |
| 2381 | - std::ptr::null(), 0, | |
| 3083 | + path.as_ptr(), | |
| 3084 | + path.len() as i64, | |
| 3085 | + "replace".as_ptr(), | |
| 3086 | + 7, | |
| 3087 | + std::ptr::null(), | |
| 3088 | + 0, | |
| 2382 | 3089 | ); |
| 2383 | 3090 | |
| 2384 | 3091 | afs_fmt_begin(96, "(I40)".as_ptr(), 5); |
@@ -2404,7 +3111,12 @@ mod tests { | ||
| 2404 | 3111 | |
| 2405 | 3112 | unsafe { std::ptr::write_unaligned(ptr, wide) }; |
| 2406 | 3113 | |
| 2407 | - afs_fmt_begin_internal(rendered.as_mut_ptr(), rendered.len() as i64, "(I40)".as_ptr(), 5); | |
| 3114 | + afs_fmt_begin_internal( | |
| 3115 | + rendered.as_mut_ptr(), | |
| 3116 | + rendered.len() as i64, | |
| 3117 | + "(I40)".as_ptr(), | |
| 3118 | + 5, | |
| 3119 | + ); | |
| 2408 | 3120 | afs_fmt_push_int128(ptr); |
| 2409 | 3121 | afs_fmt_end(0); |
| 2410 | 3122 | |
@@ -2453,7 +3165,10 @@ mod tests { | ||
| 2453 | 3165 | &mut iostat, |
| 2454 | 3166 | ); |
| 2455 | 3167 | |
| 2456 | - assert_eq!(iostat, 0, "expected formatted internal i128 read to succeed"); | |
| 3168 | + assert_eq!( | |
| 3169 | + iostat, 0, | |
| 3170 | + "expected formatted internal i128 read to succeed" | |
| 3171 | + ); | |
| 2457 | 3172 | assert_eq!(value, 170141183460469231731687303715884105727i128); |
| 2458 | 3173 | } |
| 2459 | 3174 | |
@@ -2474,7 +3189,10 @@ mod tests { | ||
| 2474 | 3189 | &mut iostat, |
| 2475 | 3190 | ); |
| 2476 | 3191 | |
| 2477 | - assert_eq!(iostat, 0, "expected formatted internal i128 read to succeed"); | |
| 3192 | + assert_eq!( | |
| 3193 | + iostat, 0, | |
| 3194 | + "expected formatted internal i128 read to succeed" | |
| 3195 | + ); | |
| 2478 | 3196 | let value = unsafe { std::ptr::read_unaligned(ptr) }; |
| 2479 | 3197 | assert_eq!(value, 170141183460469231731687303715884105727i128); |
| 2480 | 3198 | } |
@@ -2518,9 +3236,12 @@ mod tests { | ||
| 2518 | 3236 | |
| 2519 | 3237 | afs_open_simple( |
| 2520 | 3238 | 94, |
| 2521 | - path.as_ptr(), path.len() as i64, | |
| 2522 | - "old".as_ptr(), 3, | |
| 2523 | - "read".as_ptr(), 4, | |
| 3239 | + path.as_ptr(), | |
| 3240 | + path.len() as i64, | |
| 3241 | + "old".as_ptr(), | |
| 3242 | + 3, | |
| 3243 | + "read".as_ptr(), | |
| 3244 | + 4, | |
| 2524 | 3245 | ); |
| 2525 | 3246 | |
| 2526 | 3247 | let mut value = 0i128; |
@@ -2539,9 +3260,12 @@ mod tests { | ||
| 2539 | 3260 | |
| 2540 | 3261 | afs_open_simple( |
| 2541 | 3262 | 93, |
| 2542 | - path.as_ptr(), path.len() as i64, | |
| 2543 | - "old".as_ptr(), 3, | |
| 2544 | - "read".as_ptr(), 4, | |
| 3263 | + path.as_ptr(), | |
| 3264 | + path.len() as i64, | |
| 3265 | + "old".as_ptr(), | |
| 3266 | + 3, | |
| 3267 | + "read".as_ptr(), | |
| 3268 | + 4, | |
| 2545 | 3269 | ); |
| 2546 | 3270 | |
| 2547 | 3271 | let mut first = 0i128; |
@@ -2569,9 +3293,12 @@ mod tests { | ||
| 2569 | 3293 | |
| 2570 | 3294 | afs_open_simple( |
| 2571 | 3295 | 92, |
| 2572 | - path.as_ptr(), path.len() as i64, | |
| 2573 | - "old".as_ptr(), 3, | |
| 2574 | - "readwrite".as_ptr(), 9, | |
| 3296 | + path.as_ptr(), | |
| 3297 | + path.len() as i64, | |
| 3298 | + "old".as_ptr(), | |
| 3299 | + 3, | |
| 3300 | + "readwrite".as_ptr(), | |
| 3301 | + 9, | |
| 2575 | 3302 | ); |
| 2576 | 3303 | |
| 2577 | 3304 | let mut first = 0i128; |
@@ -2579,12 +3306,18 @@ mod tests { | ||
| 2579 | 3306 | let mut iostat = -99i32; |
| 2580 | 3307 | |
| 2581 | 3308 | afs_fmt_read_int128(92, "(I40)".as_ptr(), 5, 0, &mut first, &mut iostat); |
| 2582 | - assert_eq!(iostat, 0, "expected first formatted readwrite-unit read to succeed"); | |
| 3309 | + assert_eq!( | |
| 3310 | + iostat, 0, | |
| 3311 | + "expected first formatted readwrite-unit read to succeed" | |
| 3312 | + ); | |
| 2583 | 3313 | |
| 2584 | 3314 | afs_fmt_read_int128(92, "(I40)".as_ptr(), 5, 0, &mut second, &mut iostat); |
| 2585 | 3315 | afs_close(92, std::ptr::null_mut()); |
| 2586 | 3316 | |
| 2587 | - assert_eq!(iostat, 0, "expected second formatted readwrite-unit read to succeed"); | |
| 3317 | + assert_eq!( | |
| 3318 | + iostat, 0, | |
| 3319 | + "expected second formatted readwrite-unit read to succeed" | |
| 3320 | + ); | |
| 2588 | 3321 | assert_eq!(first, 170141183460469231731687303715884105727i128); |
| 2589 | 3322 | assert_eq!(second, -170141183460469231731687303715884105727i128); |
| 2590 | 3323 | } |
@@ -2594,9 +3327,12 @@ mod tests { | ||
| 2594 | 3327 | let path = "/tmp/afs_fmt_noadv_test.dat"; |
| 2595 | 3328 | afs_open_simple( |
| 2596 | 3329 | 98, |
| 2597 | - path.as_ptr(), path.len() as i64, | |
| 2598 | - "replace".as_ptr(), 7, | |
| 2599 | - std::ptr::null(), 0, | |
| 3330 | + path.as_ptr(), | |
| 3331 | + path.len() as i64, | |
| 3332 | + "replace".as_ptr(), | |
| 3333 | + 7, | |
| 3334 | + std::ptr::null(), | |
| 3335 | + 0, | |
| 2600 | 3336 | ); |
| 2601 | 3337 | |
| 2602 | 3338 | afs_fmt_begin(98, "('hello')".as_ptr(), 9); |
runtime/src/lib.rsmodified@@ -11,12 +11,12 @@ | ||
| 11 | 11 | #![allow(clippy::not_unsafe_ptr_arg_deref)] |
| 12 | 12 | #![allow(clippy::missing_safety_doc)] |
| 13 | 13 | |
| 14 | -pub mod descriptor; | |
| 15 | 14 | pub mod array; |
| 16 | -pub mod string; | |
| 15 | +pub mod descriptor; | |
| 17 | 16 | pub mod format; |
| 18 | -pub mod io_system; | |
| 19 | -pub mod system; | |
| 20 | 17 | mod io; |
| 21 | -mod mem; | |
| 18 | +pub mod io_system; | |
| 22 | 19 | mod lifecycle; |
| 20 | +mod mem; | |
| 21 | +pub mod string; | |
| 22 | +pub mod system; | |
runtime/src/mem.rsmodified@@ -41,10 +41,7 @@ pub extern "C" fn afs_deallocate(ptr: *mut u8) { | ||
| 41 | 41 | /// Concatenate two strings. Returns a newly allocated string. |
| 42 | 42 | /// Caller is responsible for freeing the result. |
| 43 | 43 | #[no_mangle] |
| 44 | -pub extern "C" fn afs_string_concat( | |
| 45 | - a: *const u8, alen: i64, | |
| 46 | - b: *const u8, blen: i64, | |
| 47 | -) -> *mut u8 { | |
| 44 | +pub extern "C" fn afs_string_concat(a: *const u8, alen: i64, b: *const u8, blen: i64) -> *mut u8 { | |
| 48 | 45 | let total = (alen + blen) as usize; |
| 49 | 46 | let result = afs_allocate(total as i64); |
| 50 | 47 | if !a.is_null() && alen > 0 { |
@@ -59,10 +56,7 @@ pub extern "C" fn afs_string_concat( | ||
| 59 | 56 | /// Copy a string into a fixed-length buffer, padding with spaces. |
| 60 | 57 | /// Used for character assignment to fixed-length variables. |
| 61 | 58 | #[no_mangle] |
| 62 | -pub extern "C" fn afs_string_copy( | |
| 63 | - dest: *mut u8, dest_len: i64, | |
| 64 | - src: *const u8, src_len: i64, | |
| 65 | -) { | |
| 59 | +pub extern "C" fn afs_string_copy(dest: *mut u8, dest_len: i64, src: *const u8, src_len: i64) { | |
| 66 | 60 | if dest.is_null() || dest_len <= 0 { |
| 67 | 61 | return; |
| 68 | 62 | } |
@@ -81,15 +75,16 @@ pub extern "C" fn afs_string_copy( | ||
| 81 | 75 | /// Compare two strings lexicographically. |
| 82 | 76 | /// Returns negative, zero, or positive (like strcmp but for counted strings). |
| 83 | 77 | #[no_mangle] |
| 84 | -pub extern "C" fn afs_string_compare( | |
| 85 | - a: *const u8, alen: i64, | |
| 86 | - b: *const u8, blen: i64, | |
| 87 | -) -> i32 { | |
| 78 | +pub extern "C" fn afs_string_compare(a: *const u8, alen: i64, b: *const u8, blen: i64) -> i32 { | |
| 88 | 79 | let sa = if !a.is_null() && alen > 0 { |
| 89 | 80 | unsafe { std::slice::from_raw_parts(a, alen as usize) } |
| 90 | - } else { &[] }; | |
| 81 | + } else { | |
| 82 | + &[] | |
| 83 | + }; | |
| 91 | 84 | let sb = if !b.is_null() && blen > 0 { |
| 92 | 85 | unsafe { std::slice::from_raw_parts(b, blen as usize) } |
| 93 | - } else { &[] }; | |
| 86 | + } else { | |
| 87 | + &[] | |
| 88 | + }; | |
| 94 | 89 | sa.cmp(sb) as i32 |
| 95 | 90 | } |
runtime/src/system.rsmodified@@ -10,27 +10,48 @@ pub extern "C" fn afs_system_clock(count: *mut i64, count_rate: *mut i64, count_ | ||
| 10 | 10 | .unwrap_or_default(); |
| 11 | 11 | let nanos = now.as_nanos() as i64; |
| 12 | 12 | |
| 13 | - if !count.is_null() { unsafe { *count = nanos; } } | |
| 14 | - if !count_rate.is_null() { unsafe { *count_rate = 1_000_000_000; } } | |
| 15 | - if !count_max.is_null() { unsafe { *count_max = i64::MAX; } } | |
| 13 | + if !count.is_null() { | |
| 14 | + unsafe { | |
| 15 | + *count = nanos; | |
| 16 | + } | |
| 17 | + } | |
| 18 | + if !count_rate.is_null() { | |
| 19 | + unsafe { | |
| 20 | + *count_rate = 1_000_000_000; | |
| 21 | + } | |
| 22 | + } | |
| 23 | + if !count_max.is_null() { | |
| 24 | + unsafe { | |
| 25 | + *count_max = i64::MAX; | |
| 26 | + } | |
| 27 | + } | |
| 16 | 28 | } |
| 17 | 29 | |
| 18 | 30 | /// CPU_TIME: returns processor time in seconds. |
| 19 | 31 | #[no_mangle] |
| 20 | 32 | pub extern "C" fn afs_cpu_time(time: *mut f64) { |
| 21 | - if time.is_null() { return; } | |
| 22 | - extern "C" { fn clock() -> i64; } | |
| 33 | + if time.is_null() { | |
| 34 | + return; | |
| 35 | + } | |
| 36 | + extern "C" { | |
| 37 | + fn clock() -> i64; | |
| 38 | + } | |
| 23 | 39 | const CLOCKS_PER_SEC: i64 = 1_000_000; // POSIX value on macOS |
| 24 | 40 | let ticks = unsafe { clock() }; |
| 25 | - unsafe { *time = ticks as f64 / CLOCKS_PER_SEC as f64; } | |
| 41 | + unsafe { | |
| 42 | + *time = ticks as f64 / CLOCKS_PER_SEC as f64; | |
| 43 | + } | |
| 26 | 44 | } |
| 27 | 45 | |
| 28 | 46 | /// DATE_AND_TIME: returns date, time, timezone, and 8-element values array. |
| 29 | 47 | #[no_mangle] |
| 30 | 48 | pub extern "C" fn afs_date_and_time( |
| 31 | - date_buf: *mut u8, date_len: i64, | |
| 32 | - time_buf: *mut u8, time_len: i64, | |
| 33 | - zone_buf: *mut u8, zone_len: i64, | |
| 49 | + date_buf: *mut u8, | |
| 50 | + date_len: i64, | |
| 51 | + time_buf: *mut u8, | |
| 52 | + time_len: i64, | |
| 53 | + zone_buf: *mut u8, | |
| 54 | + zone_len: i64, | |
| 34 | 55 | values: *mut i32, |
| 35 | 56 | ) { |
| 36 | 57 | use std::time::SystemTime; |
@@ -43,10 +64,16 @@ pub extern "C" fn afs_date_and_time( | ||
| 43 | 64 | // Use POSIX localtime_r to decompose. |
| 44 | 65 | #[repr(C)] |
| 45 | 66 | struct Tm { |
| 46 | - tm_sec: i32, tm_min: i32, tm_hour: i32, tm_mday: i32, | |
| 47 | - tm_mon: i32, tm_year: i32, tm_wday: i32, tm_yday: i32, | |
| 67 | + tm_sec: i32, | |
| 68 | + tm_min: i32, | |
| 69 | + tm_hour: i32, | |
| 70 | + tm_mday: i32, | |
| 71 | + tm_mon: i32, | |
| 72 | + tm_year: i32, | |
| 73 | + tm_wday: i32, | |
| 74 | + tm_yday: i32, | |
| 48 | 75 | tm_isdst: i32, |
| 49 | - tm_gmtoff: i64, // macOS: long (8 bytes on ARM64) | |
| 76 | + tm_gmtoff: i64, // macOS: long (8 bytes on ARM64) | |
| 50 | 77 | tm_zone: *const u8, |
| 51 | 78 | } |
| 52 | 79 | extern "C" { |
@@ -54,7 +81,9 @@ pub extern "C" fn afs_date_and_time( | ||
| 54 | 81 | } |
| 55 | 82 | let mut tm = unsafe { std::mem::zeroed::<Tm>() }; |
| 56 | 83 | let time_t = secs; |
| 57 | - unsafe { localtime_r(&time_t, &mut tm); } | |
| 84 | + unsafe { | |
| 85 | + localtime_r(&time_t, &mut tm); | |
| 86 | + } | |
| 58 | 87 | |
| 59 | 88 | let year = tm.tm_year + 1900; |
| 60 | 89 | let month = tm.tm_mon + 1; |
@@ -69,9 +98,13 @@ pub extern "C" fn afs_date_and_time( | ||
| 69 | 98 | let s = format!("{:04}{:02}{:02}", year, month, day); |
| 70 | 99 | let bytes = s.as_bytes(); |
| 71 | 100 | let n = bytes.len().min(date_len as usize); |
| 72 | - unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), date_buf, n); } | |
| 101 | + unsafe { | |
| 102 | + std::ptr::copy_nonoverlapping(bytes.as_ptr(), date_buf, n); | |
| 103 | + } | |
| 73 | 104 | if n < date_len as usize { |
| 74 | - unsafe { std::ptr::write_bytes(date_buf.add(n), b' ', date_len as usize - n); } | |
| 105 | + unsafe { | |
| 106 | + std::ptr::write_bytes(date_buf.add(n), b' ', date_len as usize - n); | |
| 107 | + } | |
| 75 | 108 | } |
| 76 | 109 | } |
| 77 | 110 | |
@@ -80,9 +113,13 @@ pub extern "C" fn afs_date_and_time( | ||
| 80 | 113 | let s = format!("{:02}{:02}{:02}.{:03}", hour, minute, second, millis); |
| 81 | 114 | let bytes = s.as_bytes(); |
| 82 | 115 | let n = bytes.len().min(time_len as usize); |
| 83 | - unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), time_buf, n); } | |
| 116 | + unsafe { | |
| 117 | + std::ptr::copy_nonoverlapping(bytes.as_ptr(), time_buf, n); | |
| 118 | + } | |
| 84 | 119 | if n < time_len as usize { |
| 85 | - unsafe { std::ptr::write_bytes(time_buf.add(n), b' ', time_len as usize - n); } | |
| 120 | + unsafe { | |
| 121 | + std::ptr::write_bytes(time_buf.add(n), b' ', time_len as usize - n); | |
| 122 | + } | |
| 86 | 123 | } |
| 87 | 124 | } |
| 88 | 125 | |
@@ -93,9 +130,13 @@ pub extern "C" fn afs_date_and_time( | ||
| 93 | 130 | let s = format!("{}{:02}{:02}", sign, abs_min / 60, abs_min % 60); |
| 94 | 131 | let bytes = s.as_bytes(); |
| 95 | 132 | let n = bytes.len().min(zone_len as usize); |
| 96 | - unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), zone_buf, n); } | |
| 133 | + unsafe { | |
| 134 | + std::ptr::copy_nonoverlapping(bytes.as_ptr(), zone_buf, n); | |
| 135 | + } | |
| 97 | 136 | if n < zone_len as usize { |
| 98 | - unsafe { std::ptr::write_bytes(zone_buf.add(n), b' ', zone_len as usize - n); } | |
| 137 | + unsafe { | |
| 138 | + std::ptr::write_bytes(zone_buf.add(n), b' ', zone_len as usize - n); | |
| 139 | + } | |
| 99 | 140 | } |
| 100 | 141 | } |
| 101 | 142 | |
@@ -124,14 +165,23 @@ pub extern "C" fn afs_command_argument_count() -> i32 { | ||
| 124 | 165 | #[no_mangle] |
| 125 | 166 | pub extern "C" fn afs_get_command_argument( |
| 126 | 167 | number: i32, |
| 127 | - value: *mut u8, value_len: i64, | |
| 168 | + value: *mut u8, | |
| 169 | + value_len: i64, | |
| 128 | 170 | length: *mut i32, |
| 129 | 171 | status: *mut i32, |
| 130 | 172 | ) { |
| 131 | 173 | let args: Vec<String> = std::env::args().collect(); |
| 132 | 174 | if number < 0 || number as usize >= args.len() { |
| 133 | - if !status.is_null() { unsafe { *status = 1; } } | |
| 134 | - if !length.is_null() { unsafe { *length = 0; } } | |
| 175 | + if !status.is_null() { | |
| 176 | + unsafe { | |
| 177 | + *status = 1; | |
| 178 | + } | |
| 179 | + } | |
| 180 | + if !length.is_null() { | |
| 181 | + unsafe { | |
| 182 | + *length = 0; | |
| 183 | + } | |
| 184 | + } | |
| 135 | 185 | return; |
| 136 | 186 | } |
| 137 | 187 | |
@@ -139,7 +189,9 @@ pub extern "C" fn afs_get_command_argument( | ||
| 139 | 189 | let bytes = arg.as_bytes(); |
| 140 | 190 | |
| 141 | 191 | if !length.is_null() { |
| 142 | - unsafe { *length = bytes.len() as i32; } | |
| 192 | + unsafe { | |
| 193 | + *length = bytes.len() as i32; | |
| 194 | + } | |
| 143 | 195 | } |
| 144 | 196 | |
| 145 | 197 | if !value.is_null() && value_len > 0 { |
@@ -152,20 +204,29 @@ pub extern "C" fn afs_get_command_argument( | ||
| 152 | 204 | } |
| 153 | 205 | } |
| 154 | 206 | |
| 155 | - if !status.is_null() { unsafe { *status = 0; } } | |
| 207 | + if !status.is_null() { | |
| 208 | + unsafe { | |
| 209 | + *status = 0; | |
| 210 | + } | |
| 211 | + } | |
| 156 | 212 | } |
| 157 | 213 | |
| 158 | 214 | /// GET_COMMAND: retrieve the full command line. |
| 159 | 215 | #[no_mangle] |
| 160 | 216 | pub extern "C" fn afs_get_command( |
| 161 | - command: *mut u8, cmd_len: i64, | |
| 217 | + command: *mut u8, | |
| 218 | + cmd_len: i64, | |
| 162 | 219 | length: *mut i32, |
| 163 | 220 | status: *mut i32, |
| 164 | 221 | ) { |
| 165 | 222 | let full: String = std::env::args().collect::<Vec<_>>().join(" "); |
| 166 | 223 | let bytes = full.as_bytes(); |
| 167 | 224 | |
| 168 | - if !length.is_null() { unsafe { *length = bytes.len() as i32; } } | |
| 225 | + if !length.is_null() { | |
| 226 | + unsafe { | |
| 227 | + *length = bytes.len() as i32; | |
| 228 | + } | |
| 229 | + } | |
| 169 | 230 | if !command.is_null() && cmd_len > 0 { |
| 170 | 231 | let n = bytes.len().min(cmd_len as usize); |
| 171 | 232 | unsafe { |
@@ -175,14 +236,20 @@ pub extern "C" fn afs_get_command( | ||
| 175 | 236 | } |
| 176 | 237 | } |
| 177 | 238 | } |
| 178 | - if !status.is_null() { unsafe { *status = 0; } } | |
| 239 | + if !status.is_null() { | |
| 240 | + unsafe { | |
| 241 | + *status = 0; | |
| 242 | + } | |
| 243 | + } | |
| 179 | 244 | } |
| 180 | 245 | |
| 181 | 246 | /// GET_ENVIRONMENT_VARIABLE: retrieve an environment variable by name. |
| 182 | 247 | #[no_mangle] |
| 183 | 248 | pub extern "C" fn afs_get_environment_variable( |
| 184 | - name: *const u8, name_len: i64, | |
| 185 | - value: *mut u8, value_len: i64, | |
| 249 | + name: *const u8, | |
| 250 | + name_len: i64, | |
| 251 | + value: *mut u8, | |
| 252 | + value_len: i64, | |
| 186 | 253 | length: *mut i32, |
| 187 | 254 | status: *mut i32, |
| 188 | 255 | ) { |
@@ -190,14 +257,22 @@ pub extern "C" fn afs_get_environment_variable( | ||
| 190 | 257 | let slice = unsafe { std::slice::from_raw_parts(name, name_len as usize) }; |
| 191 | 258 | String::from_utf8_lossy(slice).trim().to_string() |
| 192 | 259 | } else { |
| 193 | - if !status.is_null() { unsafe { *status = 1; } } | |
| 260 | + if !status.is_null() { | |
| 261 | + unsafe { | |
| 262 | + *status = 1; | |
| 263 | + } | |
| 264 | + } | |
| 194 | 265 | return; |
| 195 | 266 | }; |
| 196 | 267 | |
| 197 | 268 | match std::env::var(&var_name) { |
| 198 | 269 | Ok(val) => { |
| 199 | 270 | let bytes = val.as_bytes(); |
| 200 | - if !length.is_null() { unsafe { *length = bytes.len() as i32; } } | |
| 271 | + if !length.is_null() { | |
| 272 | + unsafe { | |
| 273 | + *length = bytes.len() as i32; | |
| 274 | + } | |
| 275 | + } | |
| 201 | 276 | if !value.is_null() && value_len > 0 { |
| 202 | 277 | let n = bytes.len().min(value_len as usize); |
| 203 | 278 | unsafe { |
@@ -207,11 +282,23 @@ pub extern "C" fn afs_get_environment_variable( | ||
| 207 | 282 | } |
| 208 | 283 | } |
| 209 | 284 | } |
| 210 | - if !status.is_null() { unsafe { *status = 0; } } | |
| 285 | + if !status.is_null() { | |
| 286 | + unsafe { | |
| 287 | + *status = 0; | |
| 288 | + } | |
| 289 | + } | |
| 211 | 290 | } |
| 212 | 291 | Err(_) => { |
| 213 | - if !length.is_null() { unsafe { *length = 0; } } | |
| 214 | - if !status.is_null() { unsafe { *status = 1; } } | |
| 292 | + if !length.is_null() { | |
| 293 | + unsafe { | |
| 294 | + *length = 0; | |
| 295 | + } | |
| 296 | + } | |
| 297 | + if !status.is_null() { | |
| 298 | + unsafe { | |
| 299 | + *status = 1; | |
| 300 | + } | |
| 301 | + } | |
| 215 | 302 | } |
| 216 | 303 | } |
| 217 | 304 | } |
@@ -219,7 +306,8 @@ pub extern "C" fn afs_get_environment_variable( | ||
| 219 | 306 | /// EXECUTE_COMMAND_LINE: run a shell command. |
| 220 | 307 | #[no_mangle] |
| 221 | 308 | pub extern "C" fn afs_execute_command_line( |
| 222 | - command: *const u8, cmd_len: i64, | |
| 309 | + command: *const u8, | |
| 310 | + cmd_len: i64, | |
| 223 | 311 | wait: i32, |
| 224 | 312 | exitstat: *mut i32, |
| 225 | 313 | cmdstat: *mut i32, |
@@ -228,7 +316,11 @@ pub extern "C" fn afs_execute_command_line( | ||
| 228 | 316 | let slice = unsafe { std::slice::from_raw_parts(command, cmd_len as usize) }; |
| 229 | 317 | String::from_utf8_lossy(slice).trim().to_string() |
| 230 | 318 | } else { |
| 231 | - if !cmdstat.is_null() { unsafe { *cmdstat = 1; } } | |
| 319 | + if !cmdstat.is_null() { | |
| 320 | + unsafe { | |
| 321 | + *cmdstat = 1; | |
| 322 | + } | |
| 323 | + } | |
| 232 | 324 | return; |
| 233 | 325 | }; |
| 234 | 326 | |
@@ -236,17 +328,41 @@ pub extern "C" fn afs_execute_command_line( | ||
| 236 | 328 | if wait != 0 { |
| 237 | 329 | match Command::new("sh").arg("-c").arg(&cmd).status() { |
| 238 | 330 | Ok(status) => { |
| 239 | - if !exitstat.is_null() { unsafe { *exitstat = status.code().unwrap_or(-1); } } | |
| 240 | - if !cmdstat.is_null() { unsafe { *cmdstat = 0; } } | |
| 331 | + if !exitstat.is_null() { | |
| 332 | + unsafe { | |
| 333 | + *exitstat = status.code().unwrap_or(-1); | |
| 334 | + } | |
| 335 | + } | |
| 336 | + if !cmdstat.is_null() { | |
| 337 | + unsafe { | |
| 338 | + *cmdstat = 0; | |
| 339 | + } | |
| 340 | + } | |
| 241 | 341 | } |
| 242 | 342 | Err(_) => { |
| 243 | - if !cmdstat.is_null() { unsafe { *cmdstat = -1; } } | |
| 343 | + if !cmdstat.is_null() { | |
| 344 | + unsafe { | |
| 345 | + *cmdstat = -1; | |
| 346 | + } | |
| 347 | + } | |
| 244 | 348 | } |
| 245 | 349 | } |
| 246 | 350 | } else { |
| 247 | 351 | match Command::new("sh").arg("-c").arg(&cmd).spawn() { |
| 248 | - Ok(_) => { if !cmdstat.is_null() { unsafe { *cmdstat = 0; } } } | |
| 249 | - Err(_) => { if !cmdstat.is_null() { unsafe { *cmdstat = -1; } } } | |
| 352 | + Ok(_) => { | |
| 353 | + if !cmdstat.is_null() { | |
| 354 | + unsafe { | |
| 355 | + *cmdstat = 0; | |
| 356 | + } | |
| 357 | + } | |
| 358 | + } | |
| 359 | + Err(_) => { | |
| 360 | + if !cmdstat.is_null() { | |
| 361 | + unsafe { | |
| 362 | + *cmdstat = -1; | |
| 363 | + } | |
| 364 | + } | |
| 365 | + } | |
| 250 | 366 | } |
| 251 | 367 | } |
| 252 | 368 | } |
@@ -260,12 +376,18 @@ thread_local! { | ||
| 260 | 376 | /// RANDOM_NUMBER: fill a scalar with a random value in [0, 1). |
| 261 | 377 | #[no_mangle] |
| 262 | 378 | pub extern "C" fn afs_random_number_f64(harvest: *mut f64) { |
| 263 | - if harvest.is_null() { return; } | |
| 379 | + if harvest.is_null() { | |
| 380 | + return; | |
| 381 | + } | |
| 264 | 382 | RNG_SEED.with(|s| { |
| 265 | 383 | let mut x = s.get(); |
| 266 | - x = x.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); | |
| 384 | + x = x | |
| 385 | + .wrapping_mul(6364136223846793005) | |
| 386 | + .wrapping_add(1442695040888963407); | |
| 267 | 387 | s.set(x); |
| 268 | - unsafe { *harvest = (x >> 11) as f64 / (1u64 << 53) as f64; } | |
| 388 | + unsafe { | |
| 389 | + *harvest = (x >> 11) as f64 / (1u64 << 53) as f64; | |
| 390 | + } | |
| 269 | 391 | }); |
| 270 | 392 | } |
| 271 | 393 | |
@@ -293,7 +415,9 @@ mod tests { | ||
| 293 | 415 | afs_system_clock(&mut c1, &mut rate, std::ptr::null_mut()); |
| 294 | 416 | // Busy loop to ensure time passes. |
| 295 | 417 | let mut sum = 0u64; |
| 296 | - for i in 0..100000 { sum = sum.wrapping_add(i); } | |
| 418 | + for i in 0..100000 { | |
| 419 | + sum = sum.wrapping_add(i); | |
| 420 | + } | |
| 297 | 421 | let _ = sum; |
| 298 | 422 | afs_system_clock(&mut c2, std::ptr::null_mut(), std::ptr::null_mut()); |
| 299 | 423 | assert!(c2 >= c1, "clock should not go backwards: {} vs {}", c1, c2); |
src/ast/decl.rsmodified@@ -3,8 +3,8 @@ | ||
| 3 | 3 | //! Types, attributes, entity declarations, USE statements, IMPLICIT, |
| 4 | 4 | //! derived type definitions, and legacy declaration forms. |
| 5 | 5 | |
| 6 | -use super::Spanned; | |
| 7 | 6 | use super::expr::SpannedExpr; |
| 7 | +use super::Spanned; | |
| 8 | 8 | |
| 9 | 9 | /// A spanned declaration. |
| 10 | 10 | pub type SpannedDecl = Spanned<Decl>; |
@@ -26,18 +26,16 @@ pub enum Decl { | ||
| 26 | 26 | |
| 27 | 27 | /// `PUBLIC :: name1, name2` or `PRIVATE :: name1, name2` — sets |
| 28 | 28 | /// access on specific names. |
| 29 | - AccessList { access: Attribute, names: Vec<String> }, | |
| 29 | + AccessList { | |
| 30 | + access: Attribute, | |
| 31 | + names: Vec<String>, | |
| 32 | + }, | |
| 30 | 33 | |
| 31 | 34 | /// `implicit none` or `implicit none(type, external)` |
| 32 | - ImplicitNone { | |
| 33 | - external: bool, | |
| 34 | - type_: bool, | |
| 35 | - }, | |
| 35 | + ImplicitNone { external: bool, type_: bool }, | |
| 36 | 36 | |
| 37 | 37 | /// `implicit double precision (a-h, o-z)` |
| 38 | - ImplicitStmt { | |
| 39 | - specs: Vec<ImplicitSpec>, | |
| 40 | - }, | |
| 38 | + ImplicitStmt { specs: Vec<ImplicitSpec> }, | |
| 41 | 39 | |
| 42 | 40 | /// Derived type definition |
| 43 | 41 | DerivedTypeDef { |
@@ -58,9 +56,7 @@ pub enum Decl { | ||
| 58 | 56 | }, |
| 59 | 57 | |
| 60 | 58 | /// `parameter (pi = 3.14159, e = 2.71828)` |
| 61 | - ParameterStmt { | |
| 62 | - pairs: Vec<(String, SpannedExpr)>, | |
| 63 | - }, | |
| 59 | + ParameterStmt { pairs: Vec<(String, SpannedExpr)> }, | |
| 64 | 60 | |
| 65 | 61 | /// `common /block_name/ x, y, z` |
| 66 | 62 | CommonBlock { |
@@ -69,14 +65,10 @@ pub enum Decl { | ||
| 69 | 65 | }, |
| 70 | 66 | |
| 71 | 67 | /// `equivalence (a, b), (c, d)` |
| 72 | - EquivalenceStmt { | |
| 73 | - groups: Vec<Vec<SpannedExpr>>, | |
| 74 | - }, | |
| 68 | + EquivalenceStmt { groups: Vec<Vec<SpannedExpr>> }, | |
| 75 | 69 | |
| 76 | 70 | /// `data x /1.0/, y /2.0/` |
| 77 | - DataStmt { | |
| 78 | - sets: Vec<DataSet>, | |
| 79 | - }, | |
| 71 | + DataStmt { sets: Vec<DataSet> }, | |
| 80 | 72 | |
| 81 | 73 | /// `enum, bind(c)` |
| 82 | 74 | EnumDef { |
@@ -130,8 +122,8 @@ pub struct CharSelector { | ||
| 130 | 122 | #[derive(Debug, Clone, PartialEq)] |
| 131 | 123 | pub enum LenSpec { |
| 132 | 124 | Expr(SpannedExpr), |
| 133 | - Star, // len=* (assumed length) | |
| 134 | - Colon, // len=: (deferred length) | |
| 125 | + Star, // len=* (assumed length) | |
| 126 | + Colon, // len=: (deferred length) | |
| 135 | 127 | } |
| 136 | 128 | |
| 137 | 129 | // ---- Attributes ---- |
@@ -176,13 +168,9 @@ pub enum ArraySpec { | ||
| 176 | 168 | upper: SpannedExpr, |
| 177 | 169 | }, |
| 178 | 170 | /// `(:)` — assumed shape (for dummy arguments) |
| 179 | - AssumedShape { | |
| 180 | - lower: Option<SpannedExpr>, | |
| 181 | - }, | |
| 171 | + AssumedShape { lower: Option<SpannedExpr> }, | |
| 182 | 172 | /// `(*)` — assumed size (last dimension only) |
| 183 | - AssumedSize { | |
| 184 | - lower: Option<SpannedExpr>, | |
| 185 | - }, | |
| 173 | + AssumedSize { lower: Option<SpannedExpr> }, | |
| 186 | 174 | /// `(:)` with allocatable/pointer — deferred shape |
| 187 | 175 | Deferred, |
| 188 | 176 | /// `(..)` — assumed rank (F2018) |
@@ -196,9 +184,9 @@ pub enum ArraySpec { | ||
| 196 | 184 | pub struct EntityDecl { |
| 197 | 185 | pub name: String, |
| 198 | 186 | pub array_spec: Option<Vec<ArraySpec>>, |
| 199 | - pub char_len: Option<LenSpec>, // character entity-specific length | |
| 200 | - pub init: Option<SpannedExpr>, // = expr | |
| 201 | - pub ptr_init: Option<SpannedExpr>, // => expr | |
| 187 | + pub char_len: Option<LenSpec>, // character entity-specific length | |
| 188 | + pub init: Option<SpannedExpr>, // = expr | |
| 189 | + pub ptr_init: Option<SpannedExpr>, // => expr | |
| 202 | 190 | } |
| 203 | 191 | |
| 204 | 192 | // ---- Derived type parts ---- |
@@ -217,8 +205,8 @@ pub enum TypeAttr { | ||
| 217 | 205 | #[derive(Debug, Clone, PartialEq)] |
| 218 | 206 | pub struct TypeBoundProc { |
| 219 | 207 | pub name: String, |
| 220 | - pub binding: Option<String>, // procedure :: name => binding | |
| 221 | - pub attrs: Vec<String>, // pass, nopass, deferred, etc. | |
| 208 | + pub binding: Option<String>, // procedure :: name => binding | |
| 209 | + pub attrs: Vec<String>, // pass, nopass, deferred, etc. | |
| 222 | 210 | pub is_generic: bool, |
| 223 | 211 | } |
| 224 | 212 | |
src/ast/expr.rsmodified@@ -3,50 +3,30 @@ | ||
| 3 | 3 | //! Represents all Fortran expression forms: literals, names, operators, |
| 4 | 4 | //! function calls, array constructors, component access, and more. |
| 5 | 5 | |
| 6 | - | |
| 7 | 6 | /// A Fortran expression. |
| 8 | 7 | #[derive(Debug, Clone, PartialEq)] |
| 9 | 8 | #[allow(clippy::enum_variant_names)] |
| 10 | 9 | pub enum Expr { |
| 11 | 10 | // ---- Literals ---- |
| 12 | - | |
| 13 | 11 | /// Integer literal: `42`, `42_8`, `42_int64` |
| 14 | - IntegerLiteral { | |
| 15 | - text: String, | |
| 16 | - kind: Option<String>, | |
| 17 | - }, | |
| 12 | + IntegerLiteral { text: String, kind: Option<String> }, | |
| 18 | 13 | /// Real literal: `3.14`, `1.0d0`, `6.022e23`, `1.0_8`, `.5`, `5.` |
| 19 | - RealLiteral { | |
| 20 | - text: String, | |
| 21 | - kind: Option<String>, | |
| 22 | - }, | |
| 14 | + RealLiteral { text: String, kind: Option<String> }, | |
| 23 | 15 | /// String literal: `'hello'`, `"hello"`, `'it''s'` |
| 24 | - StringLiteral { | |
| 25 | - value: String, | |
| 26 | - kind: Option<String>, | |
| 27 | - }, | |
| 16 | + StringLiteral { value: String, kind: Option<String> }, | |
| 28 | 17 | /// Logical literal: `.true.`, `.false.`, `.true._4` |
| 29 | - LogicalLiteral { | |
| 30 | - value: bool, | |
| 31 | - kind: Option<String>, | |
| 32 | - }, | |
| 18 | + LogicalLiteral { value: bool, kind: Option<String> }, | |
| 33 | 19 | /// Complex literal: `(1.0, 2.0)` |
| 34 | 20 | ComplexLiteral { |
| 35 | 21 | real: Box<SpannedExpr>, |
| 36 | 22 | imag: Box<SpannedExpr>, |
| 37 | 23 | }, |
| 38 | 24 | /// BOZ literal: `B'1010'`, `O'777'`, `Z'FF'` |
| 39 | - BozLiteral { | |
| 40 | - text: String, | |
| 41 | - base: BozBase, | |
| 42 | - }, | |
| 25 | + BozLiteral { text: String, base: BozBase }, | |
| 43 | 26 | |
| 44 | 27 | // ---- Names and access ---- |
| 45 | - | |
| 46 | 28 | /// Simple name (variable, function, type, etc.) |
| 47 | - Name { | |
| 48 | - name: String, | |
| 49 | - }, | |
| 29 | + Name { name: String }, | |
| 50 | 30 | /// Component access: `x%field`, `x%inner%deep` |
| 51 | 31 | ComponentAccess { |
| 52 | 32 | base: Box<SpannedExpr>, |
@@ -54,7 +34,6 @@ pub enum Expr { | ||
| 54 | 34 | }, |
| 55 | 35 | |
| 56 | 36 | // ---- Operations ---- |
| 57 | - | |
| 58 | 37 | /// Unary operation: `-x`, `.not. x`, `+x` |
| 59 | 38 | UnaryOp { |
| 60 | 39 | op: UnaryOp, |
@@ -70,7 +49,6 @@ pub enum Expr { | ||
| 70 | 49 | // ---- Calls and subscripts ---- |
| 71 | 50 | // Note: A(I) is ambiguous — could be array access, function call, or substring. |
| 72 | 51 | // The parser produces FunctionCall for all of them; sema disambiguates. |
| 73 | - | |
| 74 | 52 | /// Function call or array access: `sin(x)`, `a(i,j)`, `s(1:5)` |
| 75 | 53 | FunctionCall { |
| 76 | 54 | callee: Box<SpannedExpr>, |
@@ -78,19 +56,15 @@ pub enum Expr { | ||
| 78 | 56 | }, |
| 79 | 57 | |
| 80 | 58 | // ---- Array constructors ---- |
| 81 | - | |
| 82 | 59 | /// Array constructor: `[1, 2, 3]` or `(/ 1, 2, 3 /)` |
| 83 | 60 | ArrayConstructor { |
| 84 | - type_spec: Option<String>, // [integer :: 1, 2, 3] | |
| 61 | + type_spec: Option<String>, // [integer :: 1, 2, 3] | |
| 85 | 62 | values: Vec<AcValue>, |
| 86 | 63 | }, |
| 87 | 64 | |
| 88 | 65 | // ---- Parenthesized ---- |
| 89 | - | |
| 90 | 66 | /// Parenthesized expression: `(x + y)` |
| 91 | - ParenExpr { | |
| 92 | - inner: Box<SpannedExpr>, | |
| 93 | - }, | |
| 67 | + ParenExpr { inner: Box<SpannedExpr> }, | |
| 94 | 68 | } |
| 95 | 69 | |
| 96 | 70 | /// An expression with source location. |
@@ -104,7 +78,11 @@ impl SpannedExpr { | ||
| 104 | 78 | Expr::RealLiteral { text, .. } => text.clone(), |
| 105 | 79 | Expr::StringLiteral { value, .. } => format!("'{}'", value), |
| 106 | 80 | Expr::LogicalLiteral { value, .. } => { |
| 107 | - if *value { ".true.".into() } else { ".false.".into() } | |
| 81 | + if *value { | |
| 82 | + ".true.".into() | |
| 83 | + } else { | |
| 84 | + ".false.".into() | |
| 85 | + } | |
| 108 | 86 | } |
| 109 | 87 | Expr::ComplexLiteral { real, imag } => { |
| 110 | 88 | format!("({}, {})", real.to_sexpr(), imag.to_sexpr()) |
@@ -121,27 +99,47 @@ impl SpannedExpr { | ||
| 121 | 99 | format!("({} {} {})", left.to_sexpr(), op, right.to_sexpr()) |
| 122 | 100 | } |
| 123 | 101 | Expr::FunctionCall { callee, args } => { |
| 124 | - let args_str: Vec<String> = args.iter().map(|a| { | |
| 125 | - if let Some(kw) = &a.keyword { | |
| 126 | - format!("{}={}", kw, a.value.to_sexpr()) | |
| 127 | - } else { | |
| 128 | - a.value.to_sexpr() | |
| 129 | - } | |
| 130 | - }).collect(); | |
| 102 | + let args_str: Vec<String> = args | |
| 103 | + .iter() | |
| 104 | + .map(|a| { | |
| 105 | + if let Some(kw) = &a.keyword { | |
| 106 | + format!("{}={}", kw, a.value.to_sexpr()) | |
| 107 | + } else { | |
| 108 | + a.value.to_sexpr() | |
| 109 | + } | |
| 110 | + }) | |
| 111 | + .collect(); | |
| 131 | 112 | format!("{}({})", callee.to_sexpr(), args_str.join(", ")) |
| 132 | 113 | } |
| 133 | 114 | Expr::ArrayConstructor { values, type_spec } => { |
| 134 | - let vals: Vec<String> = values.iter().map(|v| match v { | |
| 135 | - AcValue::Expr(e) => e.to_sexpr(), | |
| 136 | - AcValue::ImpliedDo(ido) => { | |
| 137 | - let vals: Vec<String> = ido.values.iter().map(|v| match v { | |
| 138 | - AcValue::Expr(e) => e.to_sexpr(), | |
| 139 | - AcValue::ImpliedDo(_) => "(nested-implied-do)".into(), | |
| 140 | - }).collect(); | |
| 141 | - let step_str = ido.step.as_ref().map_or(String::new(), |s| format!(", {}", s.to_sexpr())); | |
| 142 | - format!("({}, {}={}, {}{})", vals.join(", "), ido.var, ido.start.to_sexpr(), ido.end.to_sexpr(), step_str) | |
| 143 | - } | |
| 144 | - }).collect(); | |
| 115 | + let vals: Vec<String> = values | |
| 116 | + .iter() | |
| 117 | + .map(|v| match v { | |
| 118 | + AcValue::Expr(e) => e.to_sexpr(), | |
| 119 | + AcValue::ImpliedDo(ido) => { | |
| 120 | + let vals: Vec<String> = ido | |
| 121 | + .values | |
| 122 | + .iter() | |
| 123 | + .map(|v| match v { | |
| 124 | + AcValue::Expr(e) => e.to_sexpr(), | |
| 125 | + AcValue::ImpliedDo(_) => "(nested-implied-do)".into(), | |
| 126 | + }) | |
| 127 | + .collect(); | |
| 128 | + let step_str = ido | |
| 129 | + .step | |
| 130 | + .as_ref() | |
| 131 | + .map_or(String::new(), |s| format!(", {}", s.to_sexpr())); | |
| 132 | + format!( | |
| 133 | + "({}, {}={}, {}{})", | |
| 134 | + vals.join(", "), | |
| 135 | + ido.var, | |
| 136 | + ido.start.to_sexpr(), | |
| 137 | + ido.end.to_sexpr(), | |
| 138 | + step_str | |
| 139 | + ) | |
| 140 | + } | |
| 141 | + }) | |
| 142 | + .collect(); | |
| 145 | 143 | if let Some(ts) = type_spec { |
| 146 | 144 | format!("[{} :: {}]", ts, vals.join(", ")) |
| 147 | 145 | } else { |
@@ -166,9 +164,9 @@ pub enum BozBase { | ||
| 166 | 164 | /// Unary operators. |
| 167 | 165 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 168 | 166 | pub enum UnaryOp { |
| 169 | - Plus, // + | |
| 170 | - Minus, // - | |
| 171 | - Not, // .not. | |
| 167 | + Plus, // + | |
| 168 | + Minus, // - | |
| 169 | + Not, // .not. | |
| 172 | 170 | Defined(String), // .myop. |
| 173 | 171 | } |
| 174 | 172 | |
@@ -187,28 +185,28 @@ impl std::fmt::Display for UnaryOp { | ||
| 187 | 185 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 188 | 186 | pub enum BinaryOp { |
| 189 | 187 | // Arithmetic |
| 190 | - Add, // + | |
| 191 | - Sub, // - | |
| 192 | - Mul, // * | |
| 193 | - Div, // / | |
| 194 | - Pow, // ** | |
| 188 | + Add, // + | |
| 189 | + Sub, // - | |
| 190 | + Mul, // * | |
| 191 | + Div, // / | |
| 192 | + Pow, // ** | |
| 195 | 193 | |
| 196 | 194 | // String |
| 197 | - Concat, // // | |
| 195 | + Concat, // // | |
| 198 | 196 | |
| 199 | 197 | // Comparison |
| 200 | - Eq, // == or .eq. | |
| 201 | - Ne, // /= or .ne. | |
| 202 | - Lt, // < or .lt. | |
| 203 | - Le, // <= or .le. | |
| 204 | - Gt, // > or .gt. | |
| 205 | - Ge, // >= or .ge. | |
| 198 | + Eq, // == or .eq. | |
| 199 | + Ne, // /= or .ne. | |
| 200 | + Lt, // < or .lt. | |
| 201 | + Le, // <= or .le. | |
| 202 | + Gt, // > or .gt. | |
| 203 | + Ge, // >= or .ge. | |
| 206 | 204 | |
| 207 | 205 | // Logical |
| 208 | - And, // .and. | |
| 209 | - Or, // .or. | |
| 210 | - Eqv, // .eqv. | |
| 211 | - Neqv, // .neqv. | |
| 206 | + And, // .and. | |
| 207 | + Or, // .or. | |
| 208 | + Eqv, // .eqv. | |
| 209 | + Neqv, // .neqv. | |
| 212 | 210 | |
| 213 | 211 | // User-defined |
| 214 | 212 | Defined(String), // .myop. |
src/ast/mod.rsmodified@@ -4,8 +4,8 @@ | ||
| 4 | 4 | //! program units, and all Fortran constructs. Every node |
| 5 | 5 | //! carries a Span for source location tracking. |
| 6 | 6 | |
| 7 | -pub mod expr; | |
| 8 | 7 | pub mod decl; |
| 8 | +pub mod expr; | |
| 9 | 9 | pub mod stmt; |
| 10 | 10 | pub mod unit; |
| 11 | 11 | |
src/ast/stmt.rsmodified@@ -2,9 +2,9 @@ | ||
| 2 | 2 | //! |
| 3 | 3 | //! Control flow, assignments, calls, and all executable Fortran statements. |
| 4 | 4 | |
| 5 | -use super::Spanned; | |
| 6 | -use super::expr::SpannedExpr; | |
| 7 | 5 | use super::decl::SpannedDecl; |
| 6 | +use super::expr::SpannedExpr; | |
| 7 | +use super::Spanned; | |
| 8 | 8 | |
| 9 | 9 | /// A spanned statement. |
| 10 | 10 | pub type SpannedStmt = Spanned<Stmt>; |
@@ -15,8 +15,14 @@ pub type SpannedStmt = Spanned<Stmt>; | ||
| 15 | 15 | #[allow(clippy::enum_variant_names)] |
| 16 | 16 | pub enum Stmt { |
| 17 | 17 | // ---- Assignment ---- |
| 18 | - Assignment { target: SpannedExpr, value: SpannedExpr }, | |
| 19 | - PointerAssignment { target: SpannedExpr, value: SpannedExpr }, | |
| 18 | + Assignment { | |
| 19 | + target: SpannedExpr, | |
| 20 | + value: SpannedExpr, | |
| 21 | + }, | |
| 22 | + PointerAssignment { | |
| 23 | + target: SpannedExpr, | |
| 24 | + value: SpannedExpr, | |
| 25 | + }, | |
| 20 | 26 | |
| 21 | 27 | // ---- IF ---- |
| 22 | 28 | IfConstruct { |
@@ -26,7 +32,10 @@ pub enum Stmt { | ||
| 26 | 32 | else_ifs: Vec<(SpannedExpr, Vec<SpannedStmt>)>, |
| 27 | 33 | else_body: Option<Vec<SpannedStmt>>, |
| 28 | 34 | }, |
| 29 | - IfStmt { condition: SpannedExpr, action: Box<SpannedStmt> }, | |
| 35 | + IfStmt { | |
| 36 | + condition: SpannedExpr, | |
| 37 | + action: Box<SpannedStmt>, | |
| 38 | + }, | |
| 30 | 39 | |
| 31 | 40 | // ---- DO loops ---- |
| 32 | 41 | DoLoop { |
@@ -72,14 +81,21 @@ pub enum Stmt { | ||
| 72 | 81 | body: Vec<SpannedStmt>, |
| 73 | 82 | elsewhere: Vec<(Option<SpannedExpr>, Vec<SpannedStmt>)>, |
| 74 | 83 | }, |
| 75 | - WhereStmt { mask: SpannedExpr, stmt: Box<SpannedStmt> }, | |
| 84 | + WhereStmt { | |
| 85 | + mask: SpannedExpr, | |
| 86 | + stmt: Box<SpannedStmt>, | |
| 87 | + }, | |
| 76 | 88 | ForallConstruct { |
| 77 | 89 | name: Option<String>, |
| 78 | 90 | specs: Vec<ForallSpec>, |
| 79 | 91 | mask: Option<SpannedExpr>, |
| 80 | 92 | body: Vec<SpannedStmt>, |
| 81 | 93 | }, |
| 82 | - ForallStmt { specs: Vec<ForallSpec>, mask: Option<SpannedExpr>, stmt: Box<SpannedStmt> }, | |
| 94 | + ForallStmt { | |
| 95 | + specs: Vec<ForallSpec>, | |
| 96 | + mask: Option<SpannedExpr>, | |
| 97 | + stmt: Box<SpannedStmt>, | |
| 98 | + }, | |
| 83 | 99 | |
| 84 | 100 | // ---- BLOCK / ASSOCIATE ---- |
| 85 | 101 | Block { |
@@ -99,43 +115,113 @@ pub enum Stmt { | ||
| 99 | 115 | decls: Vec<super::decl::SpannedDecl>, |
| 100 | 116 | body: Vec<SpannedStmt>, |
| 101 | 117 | }, |
| 102 | - Associate { name: Option<String>, assocs: Vec<(String, SpannedExpr)>, body: Vec<SpannedStmt> }, | |
| 118 | + Associate { | |
| 119 | + name: Option<String>, | |
| 120 | + assocs: Vec<(String, SpannedExpr)>, | |
| 121 | + body: Vec<SpannedStmt>, | |
| 122 | + }, | |
| 103 | 123 | |
| 104 | 124 | // ---- Branch/transfer ---- |
| 105 | - Exit { name: Option<String> }, | |
| 106 | - Cycle { name: Option<String> }, | |
| 107 | - Stop { code: Option<SpannedExpr>, quiet: bool }, | |
| 108 | - ErrorStop { code: Option<SpannedExpr>, quiet: bool }, | |
| 109 | - Return { value: Option<SpannedExpr> }, | |
| 110 | - Goto { label: u64 }, | |
| 111 | - ComputedGoto { labels: Vec<u64>, selector: SpannedExpr }, | |
| 112 | - ArithmeticIf { expr: SpannedExpr, neg: u64, zero: u64, pos: u64 }, | |
| 125 | + Exit { | |
| 126 | + name: Option<String>, | |
| 127 | + }, | |
| 128 | + Cycle { | |
| 129 | + name: Option<String>, | |
| 130 | + }, | |
| 131 | + Stop { | |
| 132 | + code: Option<SpannedExpr>, | |
| 133 | + quiet: bool, | |
| 134 | + }, | |
| 135 | + ErrorStop { | |
| 136 | + code: Option<SpannedExpr>, | |
| 137 | + quiet: bool, | |
| 138 | + }, | |
| 139 | + Return { | |
| 140 | + value: Option<SpannedExpr>, | |
| 141 | + }, | |
| 142 | + Goto { | |
| 143 | + label: u64, | |
| 144 | + }, | |
| 145 | + ComputedGoto { | |
| 146 | + labels: Vec<u64>, | |
| 147 | + selector: SpannedExpr, | |
| 148 | + }, | |
| 149 | + ArithmeticIf { | |
| 150 | + expr: SpannedExpr, | |
| 151 | + neg: u64, | |
| 152 | + zero: u64, | |
| 153 | + pos: u64, | |
| 154 | + }, | |
| 113 | 155 | /// Statement label on any executable statement: `10 i = i + 1`. |
| 114 | 156 | /// The parser emits this when it sees an integer literal at statement start. |
| 115 | - Labeled { label: u64, stmt: Box<SpannedStmt> }, | |
| 157 | + Labeled { | |
| 158 | + label: u64, | |
| 159 | + stmt: Box<SpannedStmt>, | |
| 160 | + }, | |
| 116 | 161 | |
| 117 | 162 | // ---- I/O ---- |
| 118 | - Write { controls: Vec<IoControl>, items: Vec<SpannedExpr> }, | |
| 119 | - Read { controls: Vec<IoControl>, items: Vec<SpannedExpr> }, | |
| 120 | - Open { specs: Vec<IoControl> }, | |
| 121 | - Close { specs: Vec<IoControl> }, | |
| 122 | - Inquire { specs: Vec<IoControl>, items: Vec<SpannedExpr> }, | |
| 123 | - Rewind { specs: Vec<IoControl> }, | |
| 124 | - Backspace { specs: Vec<IoControl> }, | |
| 125 | - Endfile { specs: Vec<IoControl> }, | |
| 126 | - Flush { specs: Vec<IoControl> }, | |
| 127 | - Wait { specs: Vec<IoControl> }, | |
| 163 | + Write { | |
| 164 | + controls: Vec<IoControl>, | |
| 165 | + items: Vec<SpannedExpr>, | |
| 166 | + }, | |
| 167 | + Read { | |
| 168 | + controls: Vec<IoControl>, | |
| 169 | + items: Vec<SpannedExpr>, | |
| 170 | + }, | |
| 171 | + Open { | |
| 172 | + specs: Vec<IoControl>, | |
| 173 | + }, | |
| 174 | + Close { | |
| 175 | + specs: Vec<IoControl>, | |
| 176 | + }, | |
| 177 | + Inquire { | |
| 178 | + specs: Vec<IoControl>, | |
| 179 | + items: Vec<SpannedExpr>, | |
| 180 | + }, | |
| 181 | + Rewind { | |
| 182 | + specs: Vec<IoControl>, | |
| 183 | + }, | |
| 184 | + Backspace { | |
| 185 | + specs: Vec<IoControl>, | |
| 186 | + }, | |
| 187 | + Endfile { | |
| 188 | + specs: Vec<IoControl>, | |
| 189 | + }, | |
| 190 | + Flush { | |
| 191 | + specs: Vec<IoControl>, | |
| 192 | + }, | |
| 193 | + Wait { | |
| 194 | + specs: Vec<IoControl>, | |
| 195 | + }, | |
| 128 | 196 | |
| 129 | 197 | // ---- Memory ---- |
| 130 | - Allocate { items: Vec<SpannedExpr>, opts: Vec<IoControl> }, | |
| 131 | - Deallocate { items: Vec<SpannedExpr>, opts: Vec<IoControl> }, | |
| 132 | - Nullify { items: Vec<SpannedExpr> }, | |
| 198 | + Allocate { | |
| 199 | + items: Vec<SpannedExpr>, | |
| 200 | + opts: Vec<IoControl>, | |
| 201 | + }, | |
| 202 | + Deallocate { | |
| 203 | + items: Vec<SpannedExpr>, | |
| 204 | + opts: Vec<IoControl>, | |
| 205 | + }, | |
| 206 | + Nullify { | |
| 207 | + items: Vec<SpannedExpr>, | |
| 208 | + }, | |
| 133 | 209 | |
| 134 | 210 | // ---- Other executable ---- |
| 135 | - Continue { label: Option<u64> }, | |
| 136 | - Call { callee: SpannedExpr, args: Vec<crate::ast::expr::Argument> }, | |
| 137 | - Print { format: SpannedExpr, items: Vec<SpannedExpr> }, | |
| 138 | - Namelist { groups: Vec<(String, Vec<String>)> }, | |
| 211 | + Continue { | |
| 212 | + label: Option<u64>, | |
| 213 | + }, | |
| 214 | + Call { | |
| 215 | + callee: SpannedExpr, | |
| 216 | + args: Vec<crate::ast::expr::Argument>, | |
| 217 | + }, | |
| 218 | + Print { | |
| 219 | + format: SpannedExpr, | |
| 220 | + items: Vec<SpannedExpr>, | |
| 221 | + }, | |
| 222 | + Namelist { | |
| 223 | + groups: Vec<(String, Vec<String>)>, | |
| 224 | + }, | |
| 139 | 225 | |
| 140 | 226 | // ---- Declaration (embedded in statement context) ---- |
| 141 | 227 | Declaration(SpannedDecl), |
@@ -156,9 +242,15 @@ pub struct IoControl { | ||
| 156 | 242 | #[derive(Debug, Clone, PartialEq)] |
| 157 | 243 | pub enum TypeGuard { |
| 158 | 244 | /// TYPE IS (type_name) — exact type match. |
| 159 | - TypeIs { type_name: String, body: Vec<SpannedStmt> }, | |
| 245 | + TypeIs { | |
| 246 | + type_name: String, | |
| 247 | + body: Vec<SpannedStmt>, | |
| 248 | + }, | |
| 160 | 249 | /// CLASS IS (type_name) — matches type or any extension. |
| 161 | - ClassIs { type_name: String, body: Vec<SpannedStmt> }, | |
| 250 | + ClassIs { | |
| 251 | + type_name: String, | |
| 252 | + body: Vec<SpannedStmt>, | |
| 253 | + }, | |
| 162 | 254 | /// CLASS DEFAULT — fallback. |
| 163 | 255 | ClassDefault { body: Vec<SpannedStmt> }, |
| 164 | 256 | } |
@@ -174,7 +266,10 @@ pub struct CaseBlock { | ||
| 174 | 266 | #[derive(Debug, Clone, PartialEq)] |
| 175 | 267 | pub enum CaseSelector { |
| 176 | 268 | Value(SpannedExpr), |
| 177 | - Range { low: Option<SpannedExpr>, high: Option<SpannedExpr> }, | |
| 269 | + Range { | |
| 270 | + low: Option<SpannedExpr>, | |
| 271 | + high: Option<SpannedExpr>, | |
| 272 | + }, | |
| 178 | 273 | Default, |
| 179 | 274 | } |
| 180 | 275 | |
src/ast/unit.rsmodified@@ -3,9 +3,9 @@ | ||
| 3 | 3 | //! Top-level compilation units: programs, modules, submodules, |
| 4 | 4 | //! subroutines, functions, block data, and interface blocks. |
| 5 | 5 | |
| 6 | -use super::Spanned; | |
| 7 | 6 | use super::decl::{SpannedDecl, TypeSpec}; |
| 8 | 7 | use super::stmt::SpannedStmt; |
| 8 | +use super::Spanned; | |
| 9 | 9 | |
| 10 | 10 | /// A spanned program unit. |
| 11 | 11 | pub type SpannedUnit = Spanned<ProgramUnit>; |
@@ -119,8 +119,8 @@ pub enum InterfaceBody { | ||
| 119 | 119 | /// IMPORT statement. |
| 120 | 120 | #[derive(Debug, Clone, PartialEq)] |
| 121 | 121 | pub enum ImportStmt { |
| 122 | - Default(Vec<String>), // import :: name1, name2 | |
| 123 | - All, // import, all | |
| 124 | - None, // import, none | |
| 125 | - Only(Vec<String>), // import, only: name1, name2 | |
| 122 | + Default(Vec<String>), // import :: name1, name2 | |
| 123 | + All, // import, all | |
| 124 | + None, // import, none | |
| 125 | + Only(Vec<String>), // import, only: name1, name2 | |
| 126 | 126 | } |
src/codegen/linearscan.rsmodified@@ -8,9 +8,9 @@ | ||
| 8 | 8 | //! Callee-saved registers (x19-x28, d8-d15) are tracked — if used, the function |
| 9 | 9 | //! prologue/epilogue must save/restore them. |
| 10 | 10 | |
| 11 | -use std::collections::{HashMap, HashSet}; | |
| 12 | -use super::mir::*; | |
| 13 | 11 | use super::liveness::compute_liveness; |
| 12 | +use super::mir::*; | |
| 13 | +use std::collections::{HashMap, HashSet}; | |
| 14 | 14 | |
| 15 | 15 | /// GP registers available for allocation (excludes x18, x29, x30, x31/sp). |
| 16 | 16 | /// Ordered: caller-saved first (prefer these to avoid save/restore overhead), |
@@ -32,8 +32,8 @@ use super::liveness::compute_liveness; | ||
| 32 | 32 | /// of pressure and guarantees reloads never clash. |
| 33 | 33 | const GP_ALLOC_ORDER: [u8; 22] = [ |
| 34 | 34 | // Caller-saved (temporary, no save needed) |
| 35 | - 12, 13, 14, 15, // x12-x15 | |
| 36 | - 0, 1, 2, 3, 4, 5, 6, 7, // x0-x7 (args, but available between calls) | |
| 35 | + 12, 13, 14, 15, // x12-x15 | |
| 36 | + 0, 1, 2, 3, 4, 5, 6, 7, // x0-x7 (args, but available between calls) | |
| 37 | 37 | // Callee-saved (must save/restore if used) |
| 38 | 38 | 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, |
| 39 | 39 | ]; |
@@ -42,8 +42,7 @@ const GP_ALLOC_ORDER: [u8; 22] = [ | ||
| 42 | 42 | /// d29, d30, d31 are reserved exclusively as spill reload scratch. |
| 43 | 43 | const FP_ALLOC_ORDER: [u8; 29] = [ |
| 44 | 44 | // Caller-saved |
| 45 | - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | |
| 46 | - 0, 1, 2, 3, 4, 5, 6, 7, | |
| 45 | + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 0, 1, 2, 3, 4, 5, 6, 7, | |
| 47 | 46 | // Callee-saved |
| 48 | 47 | 8, 9, 10, 11, 12, 13, 14, 15, |
| 49 | 48 | ]; |
@@ -104,7 +103,11 @@ pub fn linear_scan(mf: &mut MachineFunction) -> AllocResult { | ||
| 104 | 103 | |
| 105 | 104 | let reg_opt = if interval.crosses_call { |
| 106 | 105 | // Must use callee-saved. Find one in the free list. |
| 107 | - let callee_range = if is_fp { &FP_CALLEE_SAVED } else { &GP_CALLEE_SAVED }; | |
| 106 | + let callee_range = if is_fp { | |
| 107 | + &FP_CALLEE_SAVED | |
| 108 | + } else { | |
| 109 | + &GP_CALLEE_SAVED | |
| 110 | + }; | |
| 108 | 111 | let idx = free.iter().position(|r| callee_range.contains(r)); |
| 109 | 112 | idx.map(|i| free.remove(i)) |
| 110 | 113 | } else if let Some(hint) = interval.hint { |
@@ -195,20 +198,29 @@ pub fn linear_scan(mf: &mut MachineFunction) -> AllocResult { | ||
| 195 | 198 | _ => 200, |
| 196 | 199 | }); |
| 197 | 200 | |
| 198 | - AllocResult { assignments, spills, callee_saved_used: callee_saved } | |
| 201 | + AllocResult { | |
| 202 | + assignments, | |
| 203 | + spills, | |
| 204 | + callee_saved_used: callee_saved, | |
| 205 | + } | |
| 199 | 206 | } |
| 200 | 207 | |
| 201 | 208 | /// Apply allocation result: rewrite VReg operands to PhysReg, insert spill code. |
| 202 | 209 | /// For spilled operands, borrows a temporarily-free register from the allocation |
| 203 | 210 | /// pool rather than using dedicated scratch registers. This avoids wasting |
| 204 | 211 | /// registers in the pool and follows standard linear scan practice. |
| 205 | -pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness: &super::liveness::LivenessResult) { | |
| 206 | - let vreg_classes: HashMap<VRegId, RegClass> = mf.vregs.iter() | |
| 207 | - .map(|v| (v.id, v.class)) | |
| 208 | - .collect(); | |
| 212 | +pub fn apply_allocation( | |
| 213 | + mf: &mut MachineFunction, | |
| 214 | + result: &AllocResult, | |
| 215 | + liveness: &super::liveness::LivenessResult, | |
| 216 | +) { | |
| 217 | + let vreg_classes: HashMap<VRegId, RegClass> = | |
| 218 | + mf.vregs.iter().map(|v| (v.id, v.class)).collect(); | |
| 209 | 219 | |
| 210 | 220 | // Build interval lookup: for each vreg, its live range. |
| 211 | - let intervals: HashMap<VRegId, (u32, u32)> = liveness.intervals.iter() | |
| 221 | + let intervals: HashMap<VRegId, (u32, u32)> = liveness | |
| 222 | + .intervals | |
| 223 | + .iter() | |
| 212 | 224 | .map(|i| (i.vreg, (i.start, i.end))) |
| 213 | 225 | .collect(); |
| 214 | 226 | |
@@ -241,8 +253,8 @@ pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness | ||
| 241 | 253 | // index, so their order is load-bearing. |
| 242 | 254 | let mut gp_temps: Vec<u8> = Vec::new(); |
| 243 | 255 | let mut fp_temps: Vec<u8> = Vec::new(); |
| 244 | - let mut sorted_assignments: Vec<(VRegId, PhysReg)> = result.assignments | |
| 245 | - .iter().map(|(&v, &p)| (v, p)).collect(); | |
| 256 | + let mut sorted_assignments: Vec<(VRegId, PhysReg)> = | |
| 257 | + result.assignments.iter().map(|(&v, &p)| (v, p)).collect(); | |
| 246 | 258 | sorted_assignments.sort_by_key(|(v, _)| v.0); |
| 247 | 259 | for (vreg, phys) in &sorted_assignments { |
| 248 | 260 | if let Some(&(start, end)) = intervals.get(vreg) { |
@@ -257,8 +269,16 @@ pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness | ||
| 257 | 269 | } |
| 258 | 270 | } |
| 259 | 271 | // Also include the dedicated fallback scratches in case no free regs found. |
| 260 | - for &s in &GP_SPILL_SCRATCH { if !gp_temps.contains(&s) { gp_temps.push(s); } } | |
| 261 | - for &s in &FP_SPILL_SCRATCH { if !fp_temps.contains(&s) { fp_temps.push(s); } } | |
| 272 | + for &s in &GP_SPILL_SCRATCH { | |
| 273 | + if !gp_temps.contains(&s) { | |
| 274 | + gp_temps.push(s); | |
| 275 | + } | |
| 276 | + } | |
| 277 | + for &s in &FP_SPILL_SCRATCH { | |
| 278 | + if !fp_temps.contains(&s) { | |
| 279 | + fp_temps.push(s); | |
| 280 | + } | |
| 281 | + } | |
| 262 | 282 | |
| 263 | 283 | // For spilled vregs used as inputs: borrow a temp register. |
| 264 | 284 | let mut loads = Vec::new(); |
@@ -268,24 +288,45 @@ pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness | ||
| 268 | 288 | if let MachineOperand::VReg(vid) = op { |
| 269 | 289 | if let Some(&offset) = result.spills.get(vid) { |
| 270 | 290 | let class = vreg_classes.get(vid).copied().unwrap_or(RegClass::Gp64); |
| 271 | - let (temp_reg, load_op) = if matches!(class, RegClass::Fp32 | RegClass::Fp64) { | |
| 272 | - let r = fp_temps.get(fp_temp_idx).copied().unwrap_or(FP_SPILL_SCRATCH[0]); | |
| 273 | - fp_temp_idx += 1; | |
| 274 | - let phys = if matches!(class, RegClass::Fp32) { PhysReg::Fp32(r) } else { PhysReg::Fp(r) }; | |
| 275 | - (phys, ArmOpcode::LdrFpImm) | |
| 276 | - } else { | |
| 277 | - let r = gp_temps.get(gp_temp_idx).copied().unwrap_or(GP_SPILL_SCRATCH[0]); | |
| 278 | - gp_temp_idx += 1; | |
| 279 | - let phys = if matches!(class, RegClass::Gp32) { PhysReg::Gp32(r) } else { PhysReg::Gp(r) }; | |
| 280 | - (phys, ArmOpcode::LdrImm) | |
| 281 | - }; | |
| 291 | + let (temp_reg, load_op) = | |
| 292 | + if matches!(class, RegClass::Fp32 | RegClass::Fp64) { | |
| 293 | + let r = fp_temps | |
| 294 | + .get(fp_temp_idx) | |
| 295 | + .copied() | |
| 296 | + .unwrap_or(FP_SPILL_SCRATCH[0]); | |
| 297 | + fp_temp_idx += 1; | |
| 298 | + let phys = if matches!(class, RegClass::Fp32) { | |
| 299 | + PhysReg::Fp32(r) | |
| 300 | + } else { | |
| 301 | + PhysReg::Fp(r) | |
| 302 | + }; | |
| 303 | + (phys, ArmOpcode::LdrFpImm) | |
| 304 | + } else { | |
| 305 | + let r = gp_temps | |
| 306 | + .get(gp_temp_idx) | |
| 307 | + .copied() | |
| 308 | + .unwrap_or(GP_SPILL_SCRATCH[0]); | |
| 309 | + gp_temp_idx += 1; | |
| 310 | + let phys = if matches!(class, RegClass::Gp32) { | |
| 311 | + PhysReg::Gp32(r) | |
| 312 | + } else { | |
| 313 | + PhysReg::Gp(r) | |
| 314 | + }; | |
| 315 | + (phys, ArmOpcode::LdrImm) | |
| 316 | + }; | |
| 282 | 317 | loads.push((i, temp_reg, load_op, offset)); |
| 283 | 318 | } |
| 284 | 319 | } |
| 285 | 320 | } |
| 286 | 321 | |
| 287 | 322 | for (op_idx, scratch, load_op, offset) in &loads { |
| 288 | - if *op_idx == 0 && inst.def.as_ref().map(|d| result.spills.contains_key(d)).unwrap_or(false) { | |
| 323 | + if *op_idx == 0 | |
| 324 | + && inst | |
| 325 | + .def | |
| 326 | + .as_ref() | |
| 327 | + .map(|d| result.spills.contains_key(d)) | |
| 328 | + .unwrap_or(false) | |
| 329 | + { | |
| 289 | 330 | continue; |
| 290 | 331 | } |
| 291 | 332 | new_insts.push(MachineInst { |
@@ -315,11 +356,25 @@ pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness | ||
| 315 | 356 | if let Some(&offset) = result.spills.get(def_vid) { |
| 316 | 357 | let class = vreg_classes.get(def_vid).copied().unwrap_or(RegClass::Gp64); |
| 317 | 358 | let temp_reg = if matches!(class, RegClass::Fp32 | RegClass::Fp64) { |
| 318 | - let r = fp_temps.get(def_temp_idx).copied().unwrap_or(FP_SPILL_SCRATCH[0]); | |
| 319 | - if matches!(class, RegClass::Fp32) { PhysReg::Fp32(r) } else { PhysReg::Fp(r) } | |
| 359 | + let r = fp_temps | |
| 360 | + .get(def_temp_idx) | |
| 361 | + .copied() | |
| 362 | + .unwrap_or(FP_SPILL_SCRATCH[0]); | |
| 363 | + if matches!(class, RegClass::Fp32) { | |
| 364 | + PhysReg::Fp32(r) | |
| 365 | + } else { | |
| 366 | + PhysReg::Fp(r) | |
| 367 | + } | |
| 320 | 368 | } else { |
| 321 | - let r = gp_temps.get(def_temp_idx).copied().unwrap_or(GP_SPILL_SCRATCH[0]); | |
| 322 | - if matches!(class, RegClass::Gp32) { PhysReg::Gp32(r) } else { PhysReg::Gp(r) } | |
| 369 | + let r = gp_temps | |
| 370 | + .get(def_temp_idx) | |
| 371 | + .copied() | |
| 372 | + .unwrap_or(GP_SPILL_SCRATCH[0]); | |
| 373 | + if matches!(class, RegClass::Gp32) { | |
| 374 | + PhysReg::Gp32(r) | |
| 375 | + } else { | |
| 376 | + PhysReg::Gp(r) | |
| 377 | + } | |
| 323 | 378 | }; |
| 324 | 379 | let scratch = temp_reg; |
| 325 | 380 | // Replace def operand with scratch. |
@@ -329,8 +384,12 @@ pub fn apply_allocation(mf: &mut MachineFunction, result: &AllocResult, liveness | ||
| 329 | 384 | } |
| 330 | 385 | } |
| 331 | 386 | Some((scratch, offset, class)) |
| 332 | - } else { None } | |
| 333 | - } else { None }; | |
| 387 | + } else { | |
| 388 | + None | |
| 389 | + } | |
| 390 | + } else { | |
| 391 | + None | |
| 392 | + }; | |
| 334 | 393 | |
| 335 | 394 | rewritten.def = None; |
| 336 | 395 | new_insts.push(rewritten); |
@@ -373,10 +432,14 @@ pub fn insert_callee_saves(mf: &mut MachineFunction, callee_saved: &[PhysReg]) { | ||
| 373 | 432 | |
| 374 | 433 | // Insert saves at the start of the entry block (after prologue setup). |
| 375 | 434 | // Find the insertion point: after the STP + ADD (prologue) instructions. |
| 376 | - let prologue_end = mf.blocks[0].insts.iter().position(|i| { | |
| 377 | - // The prologue is StpPre followed by AddImm. Insert after those. | |
| 378 | - !matches!(i.opcode, ArmOpcode::StpPre | ArmOpcode::AddImm) | |
| 379 | - }).unwrap_or(0); | |
| 435 | + let prologue_end = mf.blocks[0] | |
| 436 | + .insts | |
| 437 | + .iter() | |
| 438 | + .position(|i| { | |
| 439 | + // The prologue is StpPre followed by AddImm. Insert after those. | |
| 440 | + !matches!(i.opcode, ArmOpcode::StpPre | ArmOpcode::AddImm) | |
| 441 | + }) | |
| 442 | + .unwrap_or(0); | |
| 380 | 443 | |
| 381 | 444 | // Emit saves, pairing consecutive same-class registers into STP. |
| 382 | 445 | // save_slots are ordered by increasing register number; slots are |
@@ -421,10 +484,7 @@ pub fn insert_callee_saves(mf: &mut MachineFunction, callee_saved: &[PhysReg]) { | ||
| 421 | 484 | /// STP Xt1, Xt2, [Xn, #off] → Xt1 at off, Xt2 at off+8. |
| 422 | 485 | /// We pair as: STP slots[i+1].reg, slots[i].reg, [FP, #slots[i+1].offset] |
| 423 | 486 | /// because slots[i+1] has the lower (more negative) offset. |
| 424 | -fn emit_callee_store_pairs( | |
| 425 | - save_slots: &[(PhysReg, i32)], | |
| 426 | - restore: bool, | |
| 427 | -) -> Vec<MachineInst> { | |
| 487 | +fn emit_callee_store_pairs(save_slots: &[(PhysReg, i32)], restore: bool) -> Vec<MachineInst> { | |
| 428 | 488 | let mut result = Vec::new(); |
| 429 | 489 | let mut i = 0; |
| 430 | 490 | while i < save_slots.len() { |
@@ -440,7 +500,11 @@ fn emit_callee_store_pairs( | ||
| 440 | 500 | // STP offset must fit in 7-bit signed × 8: range -512..504. |
| 441 | 501 | let in_range = (-512..=504).contains(&off2); |
| 442 | 502 | if same_class && adjacent && in_range { |
| 443 | - let opcode = if restore { ArmOpcode::LdpOffset } else { ArmOpcode::StpOffset }; | |
| 503 | + let opcode = if restore { | |
| 504 | + ArmOpcode::LdpOffset | |
| 505 | + } else { | |
| 506 | + ArmOpcode::StpOffset | |
| 507 | + }; | |
| 444 | 508 | let (low_reg, high_reg) = (reg2, reg1); |
| 445 | 509 | result.push(MachineInst { |
| 446 | 510 | opcode, |
@@ -459,10 +523,18 @@ fn emit_callee_store_pairs( | ||
| 459 | 523 | // Emit individual STR/LDR. |
| 460 | 524 | let (op, is_fp) = match reg1 { |
| 461 | 525 | PhysReg::Fp(_) | PhysReg::Fp32(_) => { |
| 462 | - if restore { (ArmOpcode::LdrFpImm, true) } else { (ArmOpcode::StrFpImm, true) } | |
| 526 | + if restore { | |
| 527 | + (ArmOpcode::LdrFpImm, true) | |
| 528 | + } else { | |
| 529 | + (ArmOpcode::StrFpImm, true) | |
| 530 | + } | |
| 463 | 531 | } |
| 464 | 532 | _ => { |
| 465 | - if restore { (ArmOpcode::LdrImm, false) } else { (ArmOpcode::StrImm, false) } | |
| 533 | + if restore { | |
| 534 | + (ArmOpcode::LdrImm, false) | |
| 535 | + } else { | |
| 536 | + (ArmOpcode::StrImm, false) | |
| 537 | + } | |
| 466 | 538 | } |
| 467 | 539 | }; |
| 468 | 540 | let _ = is_fp; |
@@ -473,7 +545,15 @@ fn emit_callee_store_pairs( | ||
| 473 | 545 | MachineOperand::PhysReg(PhysReg::FP), |
| 474 | 546 | MachineOperand::Imm(off1 as i64), |
| 475 | 547 | ], |
| 476 | - def: if restore { Some(match reg1 { PhysReg::Gp(n) | PhysReg::Gp32(n) => crate::codegen::mir::VRegId(n.into()), PhysReg::Fp(n) | PhysReg::Fp32(n) => crate::codegen::mir::VRegId(n.into()), _ => crate::codegen::mir::VRegId(0) }) } else { None }, | |
| 548 | + def: if restore { | |
| 549 | + Some(match reg1 { | |
| 550 | + PhysReg::Gp(n) | PhysReg::Gp32(n) => crate::codegen::mir::VRegId(n.into()), | |
| 551 | + PhysReg::Fp(n) | PhysReg::Fp32(n) => crate::codegen::mir::VRegId(n.into()), | |
| 552 | + _ => crate::codegen::mir::VRegId(0), | |
| 553 | + }) | |
| 554 | + } else { | |
| 555 | + None | |
| 556 | + }, | |
| 477 | 557 | }); |
| 478 | 558 | i += 1; |
| 479 | 559 | } |
@@ -517,7 +597,10 @@ const FP_SPILL_SCRATCH: [u8; 3] = [29, 30, 31]; | ||
| 517 | 597 | fn spill_scratch(class: RegClass, idx: usize) -> (PhysReg, ArmOpcode) { |
| 518 | 598 | match class { |
| 519 | 599 | RegClass::Fp64 => (PhysReg::Fp(FP_SPILL_SCRATCH[idx % 3]), ArmOpcode::LdrFpImm), |
| 520 | - RegClass::Fp32 => (PhysReg::Fp32(FP_SPILL_SCRATCH[idx % 3]), ArmOpcode::LdrFpImm), | |
| 600 | + RegClass::Fp32 => ( | |
| 601 | + PhysReg::Fp32(FP_SPILL_SCRATCH[idx % 3]), | |
| 602 | + ArmOpcode::LdrFpImm, | |
| 603 | + ), | |
| 521 | 604 | RegClass::Gp32 => (PhysReg::Gp32(GP_SPILL_SCRATCH[idx % 3]), ArmOpcode::LdrImm), |
| 522 | 605 | RegClass::Gp64 => (PhysReg::Gp(GP_SPILL_SCRATCH[idx % 3]), ArmOpcode::LdrImm), |
| 523 | 606 | } |
@@ -526,10 +609,10 @@ fn spill_scratch(class: RegClass, idx: usize) -> (PhysReg, ArmOpcode) { | ||
| 526 | 609 | #[cfg(test)] |
| 527 | 610 | mod tests { |
| 528 | 611 | use super::*; |
| 529 | - use crate::ir::types::*; | |
| 530 | - use crate::ir::inst::*; | |
| 531 | - use crate::ir::builder::FuncBuilder; | |
| 532 | 612 | use crate::codegen::isel::select_function; |
| 613 | + use crate::ir::builder::FuncBuilder; | |
| 614 | + use crate::ir::inst::*; | |
| 615 | + use crate::ir::types::*; | |
| 533 | 616 | |
| 534 | 617 | #[test] |
| 535 | 618 | fn linear_scan_assigns_registers() { |
@@ -544,9 +627,15 @@ mod tests { | ||
| 544 | 627 | let mut mf = select_function(&func); |
| 545 | 628 | let result = linear_scan(&mut mf); |
| 546 | 629 | // Should have assignments for the vregs. |
| 547 | - assert!(!result.assignments.is_empty(), "should have register assignments"); | |
| 630 | + assert!( | |
| 631 | + !result.assignments.is_empty(), | |
| 632 | + "should have register assignments" | |
| 633 | + ); | |
| 548 | 634 | // No spills needed for 3 vregs. |
| 549 | - assert!(result.spills.is_empty(), "should not spill with only 3 vregs"); | |
| 635 | + assert!( | |
| 636 | + result.spills.is_empty(), | |
| 637 | + "should not spill with only 3 vregs" | |
| 638 | + ); | |
| 550 | 639 | } |
| 551 | 640 | |
| 552 | 641 | #[test] |
@@ -595,6 +684,10 @@ mod tests { | ||
| 595 | 684 | }); |
| 596 | 685 | assert_eq!(mf.blocks[0].insts.len(), 2); |
| 597 | 686 | coalesce_moves(&mut mf); |
| 598 | - assert_eq!(mf.blocks[0].insts.len(), 1, "self-move should be eliminated"); | |
| 687 | + assert_eq!( | |
| 688 | + mf.blocks[0].insts.len(), | |
| 689 | + 1, | |
| 690 | + "self-move should be eliminated" | |
| 691 | + ); | |
| 599 | 692 | } |
| 600 | 693 | } |
src/codegen/liveness.rsmodified@@ -3,16 +3,16 @@ | ||
| 3 | 3 | //! Computes live intervals for each virtual register using backward |
| 4 | 4 | //! dataflow. Used by the linear scan register allocator. |
| 5 | 5 | |
| 6 | -use std::collections::{HashMap, BTreeSet}; | |
| 7 | 6 | use super::mir::*; |
| 7 | +use std::collections::{BTreeSet, HashMap}; | |
| 8 | 8 | |
| 9 | 9 | /// A live interval: the range of instruction positions where a vreg is live. |
| 10 | 10 | #[derive(Debug, Clone)] |
| 11 | 11 | pub struct LiveInterval { |
| 12 | 12 | pub vreg: VRegId, |
| 13 | 13 | pub class: RegClass, |
| 14 | - pub start: u32, // first instruction position (definition) | |
| 15 | - pub end: u32, // last instruction position (final use) | |
| 14 | + pub start: u32, // first instruction position (definition) | |
| 15 | + pub end: u32, // last instruction position (final use) | |
| 16 | 16 | pub crosses_call: bool, // true if a BL instruction falls within [start, end] |
| 17 | 17 | /// Preferred physical register (hint). If set, the allocator tries this register first. |
| 18 | 18 | /// Used to avoid unnecessary moves (e.g., arg values prefer xN, return values prefer x0). |
@@ -142,16 +142,26 @@ pub fn compute_liveness(mf: &MachineFunction) -> LivenessResult { | ||
| 142 | 142 | // Vregs live-in to this block: extend their interval to block start. |
| 143 | 143 | if let Some(lin) = live_in.get(&block.id) { |
| 144 | 144 | for &vreg in lin { |
| 145 | - ends.entry(vreg).and_modify(|e| *e = (*e).max(b_end)).or_insert(b_end); | |
| 146 | - starts.entry(vreg).and_modify(|s| *s = (*s).min(b_start)).or_insert(b_start); | |
| 145 | + ends.entry(vreg) | |
| 146 | + .and_modify(|e| *e = (*e).max(b_end)) | |
| 147 | + .or_insert(b_end); | |
| 148 | + starts | |
| 149 | + .entry(vreg) | |
| 150 | + .and_modify(|s| *s = (*s).min(b_start)) | |
| 151 | + .or_insert(b_start); | |
| 147 | 152 | } |
| 148 | 153 | } |
| 149 | 154 | |
| 150 | 155 | // Vregs live-out of this block: extend their interval to block end. |
| 151 | 156 | if let Some(lout) = live_out.get(&block.id) { |
| 152 | 157 | for &vreg in lout { |
| 153 | - ends.entry(vreg).and_modify(|e| *e = (*e).max(b_end)).or_insert(b_end); | |
| 154 | - starts.entry(vreg).and_modify(|s| *s = (*s).min(b_start)).or_insert(b_start); | |
| 158 | + ends.entry(vreg) | |
| 159 | + .and_modify(|e| *e = (*e).max(b_end)) | |
| 160 | + .or_insert(b_end); | |
| 161 | + starts | |
| 162 | + .entry(vreg) | |
| 163 | + .and_modify(|s| *s = (*s).min(b_start)) | |
| 164 | + .or_insert(b_start); | |
| 155 | 165 | } |
| 156 | 166 | } |
| 157 | 167 | |
@@ -159,13 +169,23 @@ pub fn compute_liveness(mf: &MachineFunction) -> LivenessResult { | ||
| 159 | 169 | for (i, inst) in block.insts.iter().enumerate() { |
| 160 | 170 | let p = position_map[&(block.id, i)]; |
| 161 | 171 | if let Some(def) = &inst.def { |
| 162 | - starts.entry(*def).and_modify(|s| *s = (*s).min(p)).or_insert(p); | |
| 163 | - ends.entry(*def).and_modify(|e| *e = (*e).max(p)).or_insert(p); | |
| 172 | + starts | |
| 173 | + .entry(*def) | |
| 174 | + .and_modify(|s| *s = (*s).min(p)) | |
| 175 | + .or_insert(p); | |
| 176 | + ends.entry(*def) | |
| 177 | + .and_modify(|e| *e = (*e).max(p)) | |
| 178 | + .or_insert(p); | |
| 164 | 179 | } |
| 165 | 180 | for op in &inst.operands { |
| 166 | 181 | if let MachineOperand::VReg(vid) = op { |
| 167 | - ends.entry(*vid).and_modify(|e| *e = (*e).max(p)).or_insert(p); | |
| 168 | - starts.entry(*vid).and_modify(|s| *s = (*s).min(p)).or_insert(p); | |
| 182 | + ends.entry(*vid) | |
| 183 | + .and_modify(|e| *e = (*e).max(p)) | |
| 184 | + .or_insert(p); | |
| 185 | + starts | |
| 186 | + .entry(*vid) | |
| 187 | + .and_modify(|s| *s = (*s).min(p)) | |
| 188 | + .or_insert(p); | |
| 169 | 189 | } |
| 170 | 190 | } |
| 171 | 191 | } |
@@ -185,17 +205,24 @@ pub fn compute_liveness(mf: &MachineFunction) -> LivenessResult { | ||
| 185 | 205 | call_positions.sort(); |
| 186 | 206 | |
| 187 | 207 | // Build intervals. |
| 188 | - let vreg_classes: HashMap<VRegId, RegClass> = mf.vregs.iter() | |
| 189 | - .map(|v| (v.id, v.class)) | |
| 190 | - .collect(); | |
| 208 | + let vreg_classes: HashMap<VRegId, RegClass> = | |
| 209 | + mf.vregs.iter().map(|v| (v.id, v.class)).collect(); | |
| 191 | 210 | |
| 192 | - let mut intervals: Vec<LiveInterval> = starts.iter() | |
| 211 | + let mut intervals: Vec<LiveInterval> = starts | |
| 212 | + .iter() | |
| 193 | 213 | .filter_map(|(&vreg, &start)| { |
| 194 | 214 | let end = ends.get(&vreg).copied()?; |
| 195 | 215 | let class = vreg_classes.get(&vreg).copied().unwrap_or(RegClass::Gp64); |
| 196 | 216 | // Check if any call falls within [start, end]. |
| 197 | 217 | let crosses_call = call_positions.iter().any(|&cp| cp > start && cp < end); |
| 198 | - Some(LiveInterval { vreg, class, start, end, crosses_call, hint: None }) | |
| 218 | + Some(LiveInterval { | |
| 219 | + vreg, | |
| 220 | + class, | |
| 221 | + start, | |
| 222 | + end, | |
| 223 | + crosses_call, | |
| 224 | + hint: None, | |
| 225 | + }) | |
| 199 | 226 | }) |
| 200 | 227 | .collect(); |
| 201 | 228 | |
@@ -210,16 +237,19 @@ pub fn compute_liveness(mf: &MachineFunction) -> LivenessResult { | ||
| 210 | 237 | // exposed clearly when mem2reg started perturbing vreg counts. |
| 211 | 238 | intervals.sort_by_key(|i| (i.start, i.vreg.0)); |
| 212 | 239 | |
| 213 | - LivenessResult { intervals, num_positions } | |
| 240 | + LivenessResult { | |
| 241 | + intervals, | |
| 242 | + num_positions, | |
| 243 | + } | |
| 214 | 244 | } |
| 215 | 245 | |
| 216 | 246 | #[cfg(test)] |
| 217 | 247 | mod tests { |
| 218 | 248 | use super::*; |
| 219 | - use crate::ir::types::*; | |
| 220 | - use crate::ir::inst::*; | |
| 221 | - use crate::ir::builder::FuncBuilder; | |
| 222 | 249 | use crate::codegen::isel::select_function; |
| 250 | + use crate::ir::builder::FuncBuilder; | |
| 251 | + use crate::ir::inst::*; | |
| 252 | + use crate::ir::types::*; | |
| 223 | 253 | |
| 224 | 254 | #[test] |
| 225 | 255 | fn liveness_simple_function() { |
@@ -250,8 +280,10 @@ mod tests { | ||
| 250 | 280 | let mf = select_function(&func); |
| 251 | 281 | let result = compute_liveness(&mf); |
| 252 | 282 | // Should have some Fp64 intervals. |
| 253 | - assert!(result.intervals.iter().any(|i| i.class == RegClass::Fp64), | |
| 254 | - "should have Fp64 intervals"); | |
| 283 | + assert!( | |
| 284 | + result.intervals.iter().any(|i| i.class == RegClass::Fp64), | |
| 285 | + "should have Fp64 intervals" | |
| 286 | + ); | |
| 255 | 287 | } |
| 256 | 288 | |
| 257 | 289 | #[test] |
src/codegen/mod.rsmodified@@ -3,11 +3,11 @@ | ||
| 3 | 3 | //! Instruction selection, register allocation, stack frame layout, |
| 4 | 4 | //! and emission of machine instructions for the afs-as assembler. |
| 5 | 5 | |
| 6 | -pub mod mir; | |
| 6 | +pub mod emit; | |
| 7 | 7 | pub mod isel; |
| 8 | -pub mod liveness; | |
| 9 | -pub mod regalloc; | |
| 10 | 8 | pub mod linearscan; |
| 11 | -pub mod emit; | |
| 9 | +pub mod liveness; | |
| 10 | +pub mod mir; | |
| 12 | 11 | pub mod peephole; |
| 12 | +pub mod regalloc; | |
| 13 | 13 | pub mod tailcall; |
src/codegen/peephole.rsmodified@@ -23,7 +23,7 @@ | ||
| 23 | 23 | //! After fusion the FMUL instruction is removed and the FADD/FSUB is |
| 24 | 24 | //! replaced by the three-source FMA instruction. |
| 25 | 25 | |
| 26 | -use super::mir::{MachineFunction, MachineInst, MachineOperand, ArmOpcode, VRegId}; | |
| 26 | +use super::mir::{ArmOpcode, MachineFunction, MachineInst, MachineOperand, VRegId}; | |
| 27 | 27 | use std::collections::HashMap; |
| 28 | 28 | |
| 29 | 29 | /// Run all peephole passes on a machine function. |
@@ -67,7 +67,10 @@ fn fma_fuse_block(mf: &mut MachineFunction, mb_idx: usize) { | ||
| 67 | 67 | |
| 68 | 68 | // Map: vreg defined by a fmul instruction → (block-instruction-index, precision) |
| 69 | 69 | #[derive(Clone, Copy)] |
| 70 | - enum Prec { S, D } | |
| 70 | + enum Prec { | |
| 71 | + S, | |
| 72 | + D, | |
| 73 | + } | |
| 71 | 74 | let mut fmul_defs: HashMap<VRegId, (usize, Prec)> = HashMap::new(); |
| 72 | 75 | |
| 73 | 76 | for (i, inst) in block.insts.iter().enumerate() { |
@@ -108,18 +111,28 @@ fn fma_fuse_block(mf: &mut MachineFunction, mb_idx: usize) { | ||
| 108 | 111 | ArmOpcode::FsubD => (false, true, false), |
| 109 | 112 | _ => continue, |
| 110 | 113 | }; |
| 111 | - if inst.operands.len() < 3 { continue; } | |
| 114 | + if inst.operands.len() < 3 { | |
| 115 | + continue; | |
| 116 | + } | |
| 112 | 117 | |
| 113 | 118 | // operands: [dest, src0, src1] |
| 114 | - let src0 = match &inst.operands[1] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 115 | - let src1 = match &inst.operands[2] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 119 | + let src0 = match &inst.operands[1] { | |
| 120 | + MachineOperand::VReg(v) => *v, | |
| 121 | + _ => continue, | |
| 122 | + }; | |
| 123 | + let src1 = match &inst.operands[2] { | |
| 124 | + MachineOperand::VReg(v) => *v, | |
| 125 | + _ => continue, | |
| 126 | + }; | |
| 116 | 127 | |
| 117 | 128 | // Check if src0 is a single-use fmul result. |
| 118 | - let try_fuse_src0 = fmul_defs.get(&src0) | |
| 129 | + let try_fuse_src0 = fmul_defs | |
| 130 | + .get(&src0) | |
| 119 | 131 | .filter(|_| use_count.get(&src0).copied().unwrap_or(0) == 1); |
| 120 | 132 | // Check if src1 is a single-use fmul result. |
| 121 | 133 | let try_fuse_src1 = if is_add { |
| 122 | - fmul_defs.get(&src1) | |
| 134 | + fmul_defs | |
| 135 | + .get(&src1) | |
| 123 | 136 | .filter(|_| use_count.get(&src1).copied().unwrap_or(0) == 1) |
| 124 | 137 | } else { |
| 125 | 138 | None // fsub(c, fmul(a,b)): src0=c, src1=fmul → handled separately |
@@ -129,50 +142,120 @@ fn fma_fuse_block(mf: &mut MachineFunction, mb_idx: usize) { | ||
| 129 | 142 | // fadd(fmul(a,b), c) → FMADD(a, b, c) |
| 130 | 143 | if let Some(&(mul_idx, _)) = try_fuse_src0 { |
| 131 | 144 | let mul_inst = &block.insts[mul_idx]; |
| 132 | - let n = match &mul_inst.operands[1] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 133 | - let m = match &mul_inst.operands[2] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 134 | - let opcode = if prec_s { ArmOpcode::FmaddS } else { ArmOpcode::FmaddD }; | |
| 135 | - plans.push(FusionPlan { add_idx: i, mul_idx, new_opcode: opcode, n, m, a: src1 }); | |
| 145 | + let n = match &mul_inst.operands[1] { | |
| 146 | + MachineOperand::VReg(v) => *v, | |
| 147 | + _ => continue, | |
| 148 | + }; | |
| 149 | + let m = match &mul_inst.operands[2] { | |
| 150 | + MachineOperand::VReg(v) => *v, | |
| 151 | + _ => continue, | |
| 152 | + }; | |
| 153 | + let opcode = if prec_s { | |
| 154 | + ArmOpcode::FmaddS | |
| 155 | + } else { | |
| 156 | + ArmOpcode::FmaddD | |
| 157 | + }; | |
| 158 | + plans.push(FusionPlan { | |
| 159 | + add_idx: i, | |
| 160 | + mul_idx, | |
| 161 | + new_opcode: opcode, | |
| 162 | + n, | |
| 163 | + m, | |
| 164 | + a: src1, | |
| 165 | + }); | |
| 136 | 166 | } else if let Some(&(mul_idx, _)) = try_fuse_src1 { |
| 137 | 167 | // fadd(c, fmul(a,b)) → FMADD(a, b, c) [commuted] |
| 138 | 168 | let mul_inst = &block.insts[mul_idx]; |
| 139 | - let n = match &mul_inst.operands[1] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 140 | - let m = match &mul_inst.operands[2] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 141 | - let opcode = if prec_s { ArmOpcode::FmaddS } else { ArmOpcode::FmaddD }; | |
| 142 | - plans.push(FusionPlan { add_idx: i, mul_idx, new_opcode: opcode, n, m, a: src0 }); | |
| 169 | + let n = match &mul_inst.operands[1] { | |
| 170 | + MachineOperand::VReg(v) => *v, | |
| 171 | + _ => continue, | |
| 172 | + }; | |
| 173 | + let m = match &mul_inst.operands[2] { | |
| 174 | + MachineOperand::VReg(v) => *v, | |
| 175 | + _ => continue, | |
| 176 | + }; | |
| 177 | + let opcode = if prec_s { | |
| 178 | + ArmOpcode::FmaddS | |
| 179 | + } else { | |
| 180 | + ArmOpcode::FmaddD | |
| 181 | + }; | |
| 182 | + plans.push(FusionPlan { | |
| 183 | + add_idx: i, | |
| 184 | + mul_idx, | |
| 185 | + new_opcode: opcode, | |
| 186 | + n, | |
| 187 | + m, | |
| 188 | + a: src0, | |
| 189 | + }); | |
| 143 | 190 | } |
| 144 | 191 | } else if is_sub { |
| 145 | 192 | // fsub(fmul(a,b), c) → FNMSUB(a, b, c) [result = a*b - c] |
| 146 | 193 | if let Some(&(mul_idx, _)) = try_fuse_src0 { |
| 147 | 194 | let mul_inst = &block.insts[mul_idx]; |
| 148 | - let n = match &mul_inst.operands[1] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 149 | - let m = match &mul_inst.operands[2] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 150 | - let opcode = if prec_s { ArmOpcode::FnmsubS } else { ArmOpcode::FnmsubD }; | |
| 151 | - plans.push(FusionPlan { add_idx: i, mul_idx, new_opcode: opcode, n, m, a: src1 }); | |
| 195 | + let n = match &mul_inst.operands[1] { | |
| 196 | + MachineOperand::VReg(v) => *v, | |
| 197 | + _ => continue, | |
| 198 | + }; | |
| 199 | + let m = match &mul_inst.operands[2] { | |
| 200 | + MachineOperand::VReg(v) => *v, | |
| 201 | + _ => continue, | |
| 202 | + }; | |
| 203 | + let opcode = if prec_s { | |
| 204 | + ArmOpcode::FnmsubS | |
| 205 | + } else { | |
| 206 | + ArmOpcode::FnmsubD | |
| 207 | + }; | |
| 208 | + plans.push(FusionPlan { | |
| 209 | + add_idx: i, | |
| 210 | + mul_idx, | |
| 211 | + new_opcode: opcode, | |
| 212 | + n, | |
| 213 | + m, | |
| 214 | + a: src1, | |
| 215 | + }); | |
| 152 | 216 | } |
| 153 | 217 | // fsub(c, fmul(a,b)) → FMSUB(a, b, c) [result = c - a*b] |
| 154 | 218 | // src0=c, src1=fmul_result |
| 155 | - let try_fuse_sub1 = fmul_defs.get(&src1) | |
| 219 | + let try_fuse_sub1 = fmul_defs | |
| 220 | + .get(&src1) | |
| 156 | 221 | .filter(|_| use_count.get(&src1).copied().unwrap_or(0) == 1); |
| 157 | 222 | if let Some(&(mul_idx, _)) = try_fuse_sub1 { |
| 158 | 223 | let mul_inst = &block.insts[mul_idx]; |
| 159 | - let n = match &mul_inst.operands[1] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 160 | - let m = match &mul_inst.operands[2] { MachineOperand::VReg(v) => *v, _ => continue }; | |
| 161 | - let opcode = if prec_s { ArmOpcode::FmsubS } else { ArmOpcode::FmsubD }; | |
| 162 | - plans.push(FusionPlan { add_idx: i, mul_idx, new_opcode: opcode, n, m, a: src0 }); | |
| 224 | + let n = match &mul_inst.operands[1] { | |
| 225 | + MachineOperand::VReg(v) => *v, | |
| 226 | + _ => continue, | |
| 227 | + }; | |
| 228 | + let m = match &mul_inst.operands[2] { | |
| 229 | + MachineOperand::VReg(v) => *v, | |
| 230 | + _ => continue, | |
| 231 | + }; | |
| 232 | + let opcode = if prec_s { | |
| 233 | + ArmOpcode::FmsubS | |
| 234 | + } else { | |
| 235 | + ArmOpcode::FmsubD | |
| 236 | + }; | |
| 237 | + plans.push(FusionPlan { | |
| 238 | + add_idx: i, | |
| 239 | + mul_idx, | |
| 240 | + new_opcode: opcode, | |
| 241 | + n, | |
| 242 | + m, | |
| 243 | + a: src0, | |
| 244 | + }); | |
| 163 | 245 | } |
| 164 | 246 | } |
| 165 | 247 | } |
| 166 | 248 | |
| 167 | - if plans.is_empty() { return; } | |
| 249 | + if plans.is_empty() { | |
| 250 | + return; | |
| 251 | + } | |
| 168 | 252 | |
| 169 | 253 | // Apply plans in reverse order of add_idx to keep indices stable. |
| 170 | 254 | plans.sort_by(|a, b| b.add_idx.cmp(&a.add_idx)); |
| 171 | 255 | |
| 172 | 256 | // Collect mul_idxs to remove. |
| 173 | - let mut remove_idxs: std::collections::HashSet<usize> = plans.iter() | |
| 174 | - .map(|p| p.mul_idx) | |
| 175 | - .collect(); | |
| 257 | + let mut remove_idxs: std::collections::HashSet<usize> = | |
| 258 | + plans.iter().map(|p| p.mul_idx).collect(); | |
| 176 | 259 | |
| 177 | 260 | // Rewrite the block. |
| 178 | 261 | let block = &mut mf.blocks[mb_idx]; |
@@ -201,8 +284,9 @@ fn fma_fuse_block(mf: &mut MachineFunction, mb_idx: usize) { | ||
| 201 | 284 | #[cfg(test)] |
| 202 | 285 | mod tests { |
| 203 | 286 | use super::*; |
| 204 | - use crate::codegen::mir::{MachineFunction, MachineBlock, MachineInst, | |
| 205 | - MachineOperand, ArmOpcode, MBlockId, RegClass}; | |
| 287 | + use crate::codegen::mir::{ | |
| 288 | + ArmOpcode, MBlockId, MachineBlock, MachineFunction, MachineInst, MachineOperand, RegClass, | |
| 289 | + }; | |
| 206 | 290 | |
| 207 | 291 | fn mf_with_insts(insts: Vec<MachineInst>) -> MachineFunction { |
| 208 | 292 | let mut mf = MachineFunction::new("test".into()); |
@@ -211,12 +295,20 @@ mod tests { | ||
| 211 | 295 | mf.new_vreg(RegClass::Fp32); |
| 212 | 296 | } |
| 213 | 297 | let bid = MBlockId(0); |
| 214 | - mf.blocks = vec![MachineBlock { id: bid, label: "entry".into(), insts }]; | |
| 298 | + mf.blocks = vec![MachineBlock { | |
| 299 | + id: bid, | |
| 300 | + label: "entry".into(), | |
| 301 | + insts, | |
| 302 | + }]; | |
| 215 | 303 | mf |
| 216 | 304 | } |
| 217 | 305 | |
| 218 | - fn vreg(v: u32) -> MachineOperand { MachineOperand::VReg(VRegId(v)) } | |
| 219 | - fn vid(v: u32) -> VRegId { VRegId(v) } | |
| 306 | + fn vreg(v: u32) -> MachineOperand { | |
| 307 | + MachineOperand::VReg(VRegId(v)) | |
| 308 | + } | |
| 309 | + fn vid(v: u32) -> VRegId { | |
| 310 | + VRegId(v) | |
| 311 | + } | |
| 220 | 312 | |
| 221 | 313 | /// fadd(fmul(v1, v2), v3) → fmadd(v1, v2, v3) |
| 222 | 314 | #[test] |
src/codegen/regalloc.rsmodified@@ -10,8 +10,8 @@ | ||
| 10 | 10 | //! |
| 11 | 11 | //! This will be replaced by linear scan allocation in Sprint 21. |
| 12 | 12 | |
| 13 | -use std::collections::HashMap; | |
| 14 | 13 | use super::mir::*; |
| 14 | +use std::collections::HashMap; | |
| 15 | 15 | |
| 16 | 16 | /// Integer scratch registers (caller-saved, safe to clobber). |
| 17 | 17 | const GP_SCRATCH: [u8; 3] = [9, 10, 11]; |
@@ -36,9 +36,8 @@ pub fn regalloc_naive(mf: &mut MachineFunction) { | ||
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | // Build class map for quick lookup. |
| 39 | - let vreg_classes: HashMap<VRegId, RegClass> = mf.vregs.iter() | |
| 40 | - .map(|v| (v.id, v.class)) | |
| 41 | - .collect(); | |
| 39 | + let vreg_classes: HashMap<VRegId, RegClass> = | |
| 40 | + mf.vregs.iter().map(|v| (v.id, v.class)).collect(); | |
| 42 | 41 | |
| 43 | 42 | // Phase 2: rewrite each block's instructions. |
| 44 | 43 | for block_idx in 0..mf.blocks.len() { |
@@ -114,7 +113,10 @@ pub fn regalloc_naive(mf: &mut MachineFunction) { | ||
| 114 | 113 | // Replace the def operand (first operand if it matches def). |
| 115 | 114 | let def_scratch = if let Some(def_vid) = def_vreg { |
| 116 | 115 | if let Some(&offset) = vreg_slots.get(&def_vid) { |
| 117 | - let class = vreg_classes.get(&def_vid).copied().unwrap_or(RegClass::Gp64); | |
| 116 | + let class = vreg_classes | |
| 117 | + .get(&def_vid) | |
| 118 | + .copied() | |
| 119 | + .unwrap_or(RegClass::Gp64); | |
| 118 | 120 | let scratch = match class { |
| 119 | 121 | RegClass::Fp64 => PhysReg::Fp(FP_SCRATCH[0]), |
| 120 | 122 | RegClass::Fp32 => PhysReg::Fp32(FP_SCRATCH[0]), |
@@ -130,8 +132,12 @@ pub fn regalloc_naive(mf: &mut MachineFunction) { | ||
| 130 | 132 | } |
| 131 | 133 | |
| 132 | 134 | Some((scratch, offset, class)) |
| 133 | - } else { None } | |
| 134 | - } else { None }; | |
| 135 | + } else { | |
| 136 | + None | |
| 137 | + } | |
| 138 | + } else { | |
| 139 | + None | |
| 140 | + }; | |
| 135 | 141 | |
| 136 | 142 | // Emit the rewritten instruction. |
| 137 | 143 | rewritten.def = None; // physical regs don't track defs |
@@ -162,10 +168,10 @@ pub fn regalloc_naive(mf: &mut MachineFunction) { | ||
| 162 | 168 | #[cfg(test)] |
| 163 | 169 | mod tests { |
| 164 | 170 | use super::*; |
| 165 | - use crate::ir::types::*; | |
| 166 | - use crate::ir::inst::*; | |
| 167 | - use crate::ir::builder::FuncBuilder; | |
| 168 | 171 | use crate::codegen::isel::select_function; |
| 172 | + use crate::ir::builder::FuncBuilder; | |
| 173 | + use crate::ir::inst::*; | |
| 174 | + use crate::ir::types::*; | |
| 169 | 175 | |
| 170 | 176 | #[test] |
| 171 | 177 | fn regalloc_replaces_vregs() { |
@@ -184,8 +190,11 @@ mod tests { | ||
| 184 | 190 | for block in &mf.blocks { |
| 185 | 191 | for inst in &block.insts { |
| 186 | 192 | for op in &inst.operands { |
| 187 | - assert!(!matches!(op, MachineOperand::VReg(_)), | |
| 188 | - "vreg still present after regalloc: {:?}", inst); | |
| 193 | + assert!( | |
| 194 | + !matches!(op, MachineOperand::VReg(_)), | |
| 195 | + "vreg still present after regalloc: {:?}", | |
| 196 | + inst | |
| 197 | + ); | |
| 189 | 198 | } |
| 190 | 199 | } |
| 191 | 200 | } |
@@ -204,7 +213,10 @@ mod tests { | ||
| 204 | 213 | let mut mf = select_function(&func); |
| 205 | 214 | let before = mf.frame.size; |
| 206 | 215 | regalloc_naive(&mut mf); |
| 207 | - assert!(mf.frame.size >= before, "frame should grow to accommodate spill slots"); | |
| 216 | + assert!( | |
| 217 | + mf.frame.size >= before, | |
| 218 | + "frame should grow to accommodate spill slots" | |
| 219 | + ); | |
| 208 | 220 | } |
| 209 | 221 | |
| 210 | 222 | #[test] |
@@ -223,16 +235,22 @@ mod tests { | ||
| 223 | 235 | // Should use x9, x10, x11 as scratch registers. |
| 224 | 236 | let uses_scratch = mf.blocks.iter().any(|b| { |
| 225 | 237 | b.insts.iter().any(|i| { |
| 226 | - i.operands.iter().any(|op| matches!(op, | |
| 227 | - MachineOperand::PhysReg(PhysReg::Gp(9)) | | |
| 228 | - MachineOperand::PhysReg(PhysReg::Gp(10)) | | |
| 229 | - MachineOperand::PhysReg(PhysReg::Gp(11)) | | |
| 230 | - MachineOperand::PhysReg(PhysReg::Gp32(9)) | | |
| 231 | - MachineOperand::PhysReg(PhysReg::Gp32(10)) | | |
| 232 | - MachineOperand::PhysReg(PhysReg::Gp32(11)) | |
| 233 | - )) | |
| 238 | + i.operands.iter().any(|op| { | |
| 239 | + matches!( | |
| 240 | + op, | |
| 241 | + MachineOperand::PhysReg(PhysReg::Gp(9)) | |
| 242 | + | MachineOperand::PhysReg(PhysReg::Gp(10)) | |
| 243 | + | MachineOperand::PhysReg(PhysReg::Gp(11)) | |
| 244 | + | MachineOperand::PhysReg(PhysReg::Gp32(9)) | |
| 245 | + | MachineOperand::PhysReg(PhysReg::Gp32(10)) | |
| 246 | + | MachineOperand::PhysReg(PhysReg::Gp32(11)) | |
| 247 | + ) | |
| 248 | + }) | |
| 234 | 249 | }) |
| 235 | 250 | }); |
| 236 | - assert!(uses_scratch, "should use scratch registers x9-x11 or w9-w11"); | |
| 251 | + assert!( | |
| 252 | + uses_scratch, | |
| 253 | + "should use scratch registers x9-x11 or w9-w11" | |
| 254 | + ); | |
| 237 | 255 | } |
| 238 | 256 | } |
src/codegen/tailcall.rsmodified@@ -37,8 +37,8 @@ | ||
| 37 | 37 | //! * Gate: we don't fire on non-void calls where a non-trivial result-capture |
| 38 | 38 | //! sequence remains (e.g., `MOV x1, x0`) — those are left alone. |
| 39 | 39 | |
| 40 | -use std::collections::HashSet; | |
| 41 | 40 | use super::mir::{ArmOpcode, MachineFunction, MachineInst, MachineOperand, PhysReg}; |
| 41 | +use std::collections::HashSet; | |
| 42 | 42 | |
| 43 | 43 | /// Run tail call optimization on a single machine function. |
| 44 | 44 | /// |
@@ -47,11 +47,17 @@ use super::mir::{ArmOpcode, MachineFunction, MachineInst, MachineOperand, PhysRe | ||
| 47 | 47 | pub fn tail_call_opt(mf: &mut MachineFunction) { |
| 48 | 48 | for block in &mut mf.blocks { |
| 49 | 49 | let n = block.insts.len(); |
| 50 | - if n < 2 { continue; } | |
| 50 | + if n < 2 { | |
| 51 | + continue; | |
| 52 | + } | |
| 51 | 53 | |
| 52 | 54 | // Epilogue is always `LdpPost; Ret` at the very end. |
| 53 | - if block.insts[n - 1].opcode != ArmOpcode::Ret { continue; } | |
| 54 | - if block.insts[n - 2].opcode != ArmOpcode::LdpPost { continue; } | |
| 55 | + if block.insts[n - 1].opcode != ArmOpcode::Ret { | |
| 56 | + continue; | |
| 57 | + } | |
| 58 | + if block.insts[n - 2].opcode != ArmOpcode::LdpPost { | |
| 59 | + continue; | |
| 60 | + } | |
| 55 | 61 | |
| 56 | 62 | let ldp_idx = n - 2; |
| 57 | 63 | |
@@ -62,9 +68,7 @@ pub fn tail_call_opt(mf: &mut MachineFunction) { | ||
| 62 | 68 | while bl_candidate > 0 { |
| 63 | 69 | bl_candidate -= 1; |
| 64 | 70 | match block.insts[bl_candidate].opcode { |
| 65 | - ArmOpcode::LdpOffset | |
| 66 | - | ArmOpcode::LdrImm | |
| 67 | - | ArmOpcode::LdrFpImm => { | |
| 71 | + ArmOpcode::LdpOffset | ArmOpcode::LdrImm | ArmOpcode::LdrFpImm => { | |
| 68 | 72 | // Callee-save restore — keep scanning backwards. |
| 69 | 73 | } |
| 70 | 74 | ArmOpcode::Bl => { |
@@ -80,8 +84,12 @@ pub fn tail_call_opt(mf: &mut MachineFunction) { | ||
| 80 | 84 | } |
| 81 | 85 | |
| 82 | 86 | // Sentinel or scanned to index 0 without finding Bl. |
| 83 | - if bl_candidate == usize::MAX { continue; } | |
| 84 | - if block.insts[bl_candidate].opcode != ArmOpcode::Bl { continue; } | |
| 87 | + if bl_candidate == usize::MAX { | |
| 88 | + continue; | |
| 89 | + } | |
| 90 | + if block.insts[bl_candidate].opcode != ArmOpcode::Bl { | |
| 91 | + continue; | |
| 92 | + } | |
| 85 | 93 | |
| 86 | 94 | // SAFETY: reject TCO when any argument register (x0–x7) holds a value |
| 87 | 95 | // derived from our frame pointer (e.g. a pointer to a stack-allocated |
@@ -97,7 +105,7 @@ pub fn tail_call_opt(mf: &mut MachineFunction) { | ||
| 97 | 105 | // Extract the call target from the Bl operand. |
| 98 | 106 | let label = match block.insts[bl_candidate].operands.first() { |
| 99 | 107 | Some(MachineOperand::Extern(s)) => s.clone(), |
| 100 | - _ => continue, // indirect call or unexpected operand — skip | |
| 108 | + _ => continue, // indirect call or unexpected operand — skip | |
| 101 | 109 | }; |
| 102 | 110 | |
| 103 | 111 | // Transform: |
@@ -149,7 +157,9 @@ fn has_frame_derived_arg(insts: &[MachineInst]) -> bool { | ||
| 149 | 157 | // sub xN, x29, #imm → xN holds a frame-relative address. |
| 150 | 158 | ArmOpcode::SubImm => { |
| 151 | 159 | if op_is_fp(inst, 1) { |
| 152 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 160 | + if let Some(n) = op_gp(inst, 0) { | |
| 161 | + tainted_regs.insert(n); | |
| 162 | + } | |
| 153 | 163 | } |
| 154 | 164 | } |
| 155 | 165 | // add xN, xM, xP (GEP: propagate taint from either source) |
@@ -157,21 +167,25 @@ fn has_frame_derived_arg(insts: &[MachineInst]) -> bool { | ||
| 157 | 167 | if op_gp(inst, 1).is_some_and(|n| tainted_regs.contains(&n)) |
| 158 | 168 | || op_gp(inst, 2).is_some_and(|n| tainted_regs.contains(&n)) |
| 159 | 169 | { |
| 160 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 170 | + if let Some(n) = op_gp(inst, 0) { | |
| 171 | + tainted_regs.insert(n); | |
| 172 | + } | |
| 161 | 173 | } |
| 162 | 174 | } |
| 163 | 175 | // add xN, xM, #imm (GEP with constant offset / address arithmetic) |
| 164 | 176 | ArmOpcode::AddImm => { |
| 165 | - if op_is_fp(inst, 1) | |
| 166 | - || op_gp(inst, 1).is_some_and(|n| tainted_regs.contains(&n)) | |
| 167 | - { | |
| 168 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 177 | + if op_is_fp(inst, 1) || op_gp(inst, 1).is_some_and(|n| tainted_regs.contains(&n)) { | |
| 178 | + if let Some(n) = op_gp(inst, 0) { | |
| 179 | + tainted_regs.insert(n); | |
| 180 | + } | |
| 169 | 181 | } |
| 170 | 182 | } |
| 171 | 183 | // mov xN, xM (register copy — propagates taint to arg reg) |
| 172 | 184 | ArmOpcode::MovReg => { |
| 173 | 185 | if op_gp(inst, 1).is_some_and(|n| tainted_regs.contains(&n)) { |
| 174 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 186 | + if let Some(n) = op_gp(inst, 0) { | |
| 187 | + tainted_regs.insert(n); | |
| 188 | + } | |
| 175 | 189 | } |
| 176 | 190 | } |
| 177 | 191 | // mul xN, xM, xP (index computation in GEP; conservative) |
@@ -179,15 +193,17 @@ fn has_frame_derived_arg(insts: &[MachineInst]) -> bool { | ||
| 179 | 193 | if op_gp(inst, 1).is_some_and(|n| tainted_regs.contains(&n)) |
| 180 | 194 | || op_gp(inst, 2).is_some_and(|n| tainted_regs.contains(&n)) |
| 181 | 195 | { |
| 182 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 196 | + if let Some(n) = op_gp(inst, 0) { | |
| 197 | + tainted_regs.insert(n); | |
| 198 | + } | |
| 183 | 199 | } |
| 184 | 200 | } |
| 185 | 201 | // str xN, [x29, #off] — if xN is tainted, the slot becomes tainted. |
| 186 | 202 | ArmOpcode::StrImm => { |
| 187 | - if op_gp(inst, 0).is_some_and(|n| tainted_regs.contains(&n)) | |
| 188 | - && op_is_fp(inst, 1) | |
| 189 | - { | |
| 190 | - if let Some(off) = op_fp_offset(inst, 2) { tainted_slots.insert(off); } | |
| 203 | + if op_gp(inst, 0).is_some_and(|n| tainted_regs.contains(&n)) && op_is_fp(inst, 1) { | |
| 204 | + if let Some(off) = op_fp_offset(inst, 2) { | |
| 205 | + tainted_slots.insert(off); | |
| 206 | + } | |
| 191 | 207 | } |
| 192 | 208 | } |
| 193 | 209 | // ldr xN, [x29, #off] — if the slot is tainted, xN becomes tainted. |
@@ -195,7 +211,9 @@ fn has_frame_derived_arg(insts: &[MachineInst]) -> bool { | ||
| 195 | 211 | if op_is_fp(inst, 1) { |
| 196 | 212 | if let Some(off) = op_fp_offset(inst, 2) { |
| 197 | 213 | if tainted_slots.contains(&off) { |
| 198 | - if let Some(n) = op_gp(inst, 0) { tainted_regs.insert(n); } | |
| 214 | + if let Some(n) = op_gp(inst, 0) { | |
| 215 | + tainted_regs.insert(n); | |
| 216 | + } | |
| 199 | 217 | } |
| 200 | 218 | } |
| 201 | 219 | } |
@@ -247,7 +265,11 @@ mod tests { | ||
| 247 | 265 | use crate::codegen::mir::*; |
| 248 | 266 | |
| 249 | 267 | fn make_block(insts: Vec<MachineInst>) -> MachineBlock { |
| 250 | - MachineBlock { label: "test".into(), insts, id: MBlockId(0) } | |
| 268 | + MachineBlock { | |
| 269 | + label: "test".into(), | |
| 270 | + insts, | |
| 271 | + id: MBlockId(0), | |
| 272 | + } | |
| 251 | 273 | } |
| 252 | 274 | |
| 253 | 275 | fn bl(label: &str) -> MachineInst { |
@@ -271,7 +293,11 @@ mod tests { | ||
| 271 | 293 | } |
| 272 | 294 | |
| 273 | 295 | fn ret() -> MachineInst { |
| 274 | - MachineInst { opcode: ArmOpcode::Ret, operands: vec![], def: None } | |
| 296 | + MachineInst { | |
| 297 | + opcode: ArmOpcode::Ret, | |
| 298 | + operands: vec![], | |
| 299 | + def: None, | |
| 300 | + } | |
| 275 | 301 | } |
| 276 | 302 | |
| 277 | 303 | fn ldr_callee_restore() -> MachineInst { |
@@ -300,27 +326,25 @@ mod tests { | ||
| 300 | 326 | #[test] |
| 301 | 327 | fn void_tail_call_no_callee_saves() { |
| 302 | 328 | // Pattern: Bl; LdpPost; Ret → LdpPost; B |
| 303 | - let mut mf = build_mf(vec![ | |
| 304 | - vec![bl("_foo"), ldp_post(), ret()], | |
| 305 | - ]); | |
| 329 | + let mut mf = build_mf(vec![vec![bl("_foo"), ldp_post(), ret()]]); | |
| 306 | 330 | tail_call_opt(&mut mf); |
| 307 | 331 | let insts = &mf.blocks[0].insts; |
| 308 | 332 | // Should now be: LdpPost, B _foo |
| 309 | 333 | assert_eq!(insts.len(), 2); |
| 310 | 334 | assert_eq!(insts[0].opcode, ArmOpcode::LdpPost); |
| 311 | 335 | assert_eq!(insts[1].opcode, ArmOpcode::B); |
| 312 | - assert_eq!( | |
| 313 | - insts[1].operands[0], | |
| 314 | - MachineOperand::Extern("_foo".into()) | |
| 315 | - ); | |
| 336 | + assert_eq!(insts[1].operands[0], MachineOperand::Extern("_foo".into())); | |
| 316 | 337 | } |
| 317 | 338 | |
| 318 | 339 | #[test] |
| 319 | 340 | fn void_tail_call_with_callee_restore() { |
| 320 | 341 | // Pattern: Bl; LdrImm(restore); LdpPost; Ret → LdrImm; LdpPost; B |
| 321 | - let mut mf = build_mf(vec![ | |
| 322 | - vec![bl("_bar"), ldr_callee_restore(), ldp_post(), ret()], | |
| 323 | - ]); | |
| 342 | + let mut mf = build_mf(vec![vec![ | |
| 343 | + bl("_bar"), | |
| 344 | + ldr_callee_restore(), | |
| 345 | + ldp_post(), | |
| 346 | + ret(), | |
| 347 | + ]]); | |
| 324 | 348 | tail_call_opt(&mut mf); |
| 325 | 349 | let insts = &mf.blocks[0].insts; |
| 326 | 350 | assert_eq!(insts.len(), 3); |
@@ -340,16 +364,18 @@ mod tests { | ||
| 340 | 364 | ], |
| 341 | 365 | def: None, |
| 342 | 366 | }; |
| 343 | - let mut mf = build_mf(vec![ | |
| 344 | - vec![bl("_baz"), mov_result, ldp_post(), ret()], | |
| 345 | - ]); | |
| 367 | + let mut mf = build_mf(vec![vec![bl("_baz"), mov_result, ldp_post(), ret()]]); | |
| 346 | 368 | tail_call_opt(&mut mf); |
| 347 | 369 | let insts = &mf.blocks[0].insts; |
| 348 | 370 | // No transformation — Bl should still be present. |
| 349 | - assert!(insts.iter().any(|i| i.opcode == ArmOpcode::Bl), | |
| 350 | - "Bl should NOT be removed when result is captured in non-return register"); | |
| 351 | - assert!(insts.iter().any(|i| i.opcode == ArmOpcode::Ret), | |
| 352 | - "Ret should still be present"); | |
| 371 | + assert!( | |
| 372 | + insts.iter().any(|i| i.opcode == ArmOpcode::Bl), | |
| 373 | + "Bl should NOT be removed when result is captured in non-return register" | |
| 374 | + ); | |
| 375 | + assert!( | |
| 376 | + insts.iter().any(|i| i.opcode == ArmOpcode::Ret), | |
| 377 | + "Ret should still be present" | |
| 378 | + ); | |
| 353 | 379 | } |
| 354 | 380 | |
| 355 | 381 | #[test] |
@@ -360,9 +386,7 @@ mod tests { | ||
| 360 | 386 | operands: vec![MachineOperand::BlockRef(MBlockId(1))], |
| 361 | 387 | def: None, |
| 362 | 388 | }; |
| 363 | - let mut mf = build_mf(vec![ | |
| 364 | - vec![bl("_qux"), ldp_post(), b_inst], | |
| 365 | - ]); | |
| 389 | + let mut mf = build_mf(vec![vec![bl("_qux"), ldp_post(), b_inst]]); | |
| 366 | 390 | tail_call_opt(&mut mf); |
| 367 | 391 | // Should still have Bl (TCO not fired because no Ret). |
| 368 | 392 | assert!(mf.blocks[0].insts.iter().any(|i| i.opcode == ArmOpcode::Bl)); |
src/driver/dep_scan.rsmodified@@ -29,12 +29,15 @@ pub fn scan_file(path: &Path) -> Result<FileDeps, String> { | ||
| 29 | 29 | for line in content.lines() { |
| 30 | 30 | let trimmed = line.trim().to_lowercase(); |
| 31 | 31 | // Skip comments and empty lines. |
| 32 | - if trimmed.starts_with('!') || trimmed.is_empty() { continue; } | |
| 32 | + if trimmed.starts_with('!') || trimmed.is_empty() { | |
| 33 | + continue; | |
| 34 | + } | |
| 33 | 35 | |
| 34 | 36 | // MODULE <name> — but not "module procedure" or "module function" |
| 35 | 37 | if let Some(rest) = trimmed.strip_prefix("module ") { |
| 36 | 38 | let rest = rest.trim(); |
| 37 | - if rest.starts_with("procedure") || rest.starts_with("function") | |
| 39 | + if rest.starts_with("procedure") | |
| 40 | + || rest.starts_with("function") | |
| 38 | 41 | || rest.starts_with("subroutine") |
| 39 | 42 | { |
| 40 | 43 | continue; |
@@ -66,7 +69,10 @@ pub fn scan_file(path: &Path) -> Result<FileDeps, String> { | ||
| 66 | 69 | if rest.starts_with("::") { |
| 67 | 70 | rest = rest[2..].trim(); |
| 68 | 71 | } |
| 69 | - if let Some(name) = rest.split(|c: char| c == ',' || c == ':' || c.is_whitespace()).next() { | |
| 72 | + if let Some(name) = rest | |
| 73 | + .split(|c: char| c == ',' || c == ':' || c.is_whitespace()) | |
| 74 | + .next() | |
| 75 | + { | |
| 70 | 76 | let clean = name.trim(); |
| 71 | 77 | if !clean.is_empty() && clean != "only" { |
| 72 | 78 | // Skip intrinsic modules — they don't need .amod files. |
@@ -84,13 +90,21 @@ pub fn scan_file(path: &Path) -> Result<FileDeps, String> { | ||
| 84 | 90 | defines.sort(); |
| 85 | 91 | defines.dedup(); |
| 86 | 92 | |
| 87 | - Ok(FileDeps { path: path.to_path_buf(), defines, uses }) | |
| 93 | + Ok(FileDeps { | |
| 94 | + path: path.to_path_buf(), | |
| 95 | + defines, | |
| 96 | + uses, | |
| 97 | + }) | |
| 88 | 98 | } |
| 89 | 99 | |
| 90 | 100 | fn is_intrinsic_module(name: &str) -> bool { |
| 91 | - matches!(name, | |
| 92 | - "iso_c_binding" | "iso_fortran_env" | | |
| 93 | - "ieee_arithmetic" | "ieee_exceptions" | "ieee_features" | |
| 101 | + matches!( | |
| 102 | + name, | |
| 103 | + "iso_c_binding" | |
| 104 | + | "iso_fortran_env" | |
| 105 | + | "ieee_arithmetic" | |
| 106 | + | "ieee_exceptions" | |
| 107 | + | "ieee_features" | |
| 94 | 108 | ) |
| 95 | 109 | } |
| 96 | 110 | |
@@ -148,7 +162,14 @@ pub fn resolve_compilation_order(files: &[FileDeps]) -> Result<Vec<usize>, Strin | ||
| 148 | 162 | // Cycle detected — find the modules involved. |
| 149 | 163 | let cycle_files: Vec<&str> = (0..n) |
| 150 | 164 | .filter(|i| in_degree[*i] > 0) |
| 151 | - .map(|i| files[i].path.file_name().unwrap_or_default().to_str().unwrap_or("?")) | |
| 165 | + .map(|i| { | |
| 166 | + files[i] | |
| 167 | + .path | |
| 168 | + .file_name() | |
| 169 | + .unwrap_or_default() | |
| 170 | + .to_str() | |
| 171 | + .unwrap_or("?") | |
| 172 | + }) | |
| 152 | 173 | .collect(); |
| 153 | 174 | return Err(format!( |
| 154 | 175 | "circular module dependency detected among: {}", |
@@ -168,7 +189,11 @@ mod tests { | ||
| 168 | 189 | let dir = std::env::temp_dir().join("dep_scan_test_1"); |
| 169 | 190 | std::fs::create_dir_all(&dir).unwrap(); |
| 170 | 191 | let f = dir.join("mod.f90"); |
| 171 | - std::fs::write(&f, "module mymod\n use other_mod\n implicit none\nend module\n").unwrap(); | |
| 192 | + std::fs::write( | |
| 193 | + &f, | |
| 194 | + "module mymod\n use other_mod\n implicit none\nend module\n", | |
| 195 | + ) | |
| 196 | + .unwrap(); | |
| 172 | 197 | let deps = scan_file(&f).unwrap(); |
| 173 | 198 | assert_eq!(deps.defines, vec!["mymod"]); |
| 174 | 199 | assert_eq!(deps.uses, vec!["other_mod"]); |
@@ -178,9 +203,21 @@ mod tests { | ||
| 178 | 203 | #[test] |
| 179 | 204 | fn topo_sort_chain() { |
| 180 | 205 | let files = vec![ |
| 181 | - FileDeps { path: "c.f90".into(), defines: vec!["c".into()], uses: vec![] }, | |
| 182 | - FileDeps { path: "b.f90".into(), defines: vec!["b".into()], uses: vec!["c".into()] }, | |
| 183 | - FileDeps { path: "a.f90".into(), defines: vec!["a".into()], uses: vec!["b".into()] }, | |
| 206 | + FileDeps { | |
| 207 | + path: "c.f90".into(), | |
| 208 | + defines: vec!["c".into()], | |
| 209 | + uses: vec![], | |
| 210 | + }, | |
| 211 | + FileDeps { | |
| 212 | + path: "b.f90".into(), | |
| 213 | + defines: vec!["b".into()], | |
| 214 | + uses: vec!["c".into()], | |
| 215 | + }, | |
| 216 | + FileDeps { | |
| 217 | + path: "a.f90".into(), | |
| 218 | + defines: vec!["a".into()], | |
| 219 | + uses: vec!["b".into()], | |
| 220 | + }, | |
| 184 | 221 | ]; |
| 185 | 222 | let order = resolve_compilation_order(&files).unwrap(); |
| 186 | 223 | // c must come before b, b before a. |
@@ -194,20 +231,48 @@ mod tests { | ||
| 194 | 231 | #[test] |
| 195 | 232 | fn topo_sort_cycle() { |
| 196 | 233 | let files = vec![ |
| 197 | - FileDeps { path: "a.f90".into(), defines: vec!["a".into()], uses: vec!["b".into()] }, | |
| 198 | - FileDeps { path: "b.f90".into(), defines: vec!["b".into()], uses: vec!["a".into()] }, | |
| 234 | + FileDeps { | |
| 235 | + path: "a.f90".into(), | |
| 236 | + defines: vec!["a".into()], | |
| 237 | + uses: vec!["b".into()], | |
| 238 | + }, | |
| 239 | + FileDeps { | |
| 240 | + path: "b.f90".into(), | |
| 241 | + defines: vec!["b".into()], | |
| 242 | + uses: vec!["a".into()], | |
| 243 | + }, | |
| 199 | 244 | ]; |
| 200 | 245 | let err = resolve_compilation_order(&files).unwrap_err(); |
| 201 | - assert!(err.contains("circular"), "expected cycle error, got: {}", err); | |
| 246 | + assert!( | |
| 247 | + err.contains("circular"), | |
| 248 | + "expected cycle error, got: {}", | |
| 249 | + err | |
| 250 | + ); | |
| 202 | 251 | } |
| 203 | 252 | |
| 204 | 253 | #[test] |
| 205 | 254 | fn topo_sort_diamond() { |
| 206 | 255 | let files = vec![ |
| 207 | - FileDeps { path: "d.f90".into(), defines: vec!["d".into()], uses: vec![] }, | |
| 208 | - FileDeps { path: "b.f90".into(), defines: vec!["b".into()], uses: vec!["d".into()] }, | |
| 209 | - FileDeps { path: "c.f90".into(), defines: vec!["c".into()], uses: vec!["d".into()] }, | |
| 210 | - FileDeps { path: "a.f90".into(), defines: vec!["a".into()], uses: vec!["b".into(), "c".into()] }, | |
| 256 | + FileDeps { | |
| 257 | + path: "d.f90".into(), | |
| 258 | + defines: vec!["d".into()], | |
| 259 | + uses: vec![], | |
| 260 | + }, | |
| 261 | + FileDeps { | |
| 262 | + path: "b.f90".into(), | |
| 263 | + defines: vec!["b".into()], | |
| 264 | + uses: vec!["d".into()], | |
| 265 | + }, | |
| 266 | + FileDeps { | |
| 267 | + path: "c.f90".into(), | |
| 268 | + defines: vec!["c".into()], | |
| 269 | + uses: vec!["d".into()], | |
| 270 | + }, | |
| 271 | + FileDeps { | |
| 272 | + path: "a.f90".into(), | |
| 273 | + defines: vec!["a".into()], | |
| 274 | + uses: vec!["b".into(), "c".into()], | |
| 275 | + }, | |
| 211 | 276 | ]; |
| 212 | 277 | let order = resolve_compilation_order(&files).unwrap(); |
| 213 | 278 | let pos_d = order.iter().position(|&i| i == 0).unwrap(); |
src/driver/diag.rsmodified@@ -82,11 +82,16 @@ pub fn render(file: &str, source: &str, span: Span, level: Level, message: &str, | ||
| 82 | 82 | msg = message, |
| 83 | 83 | ); |
| 84 | 84 | |
| 85 | - if let Some((gutter, line_text, caret_indent, caret_len)) = | |
| 86 | - snippet_for(source, span, span_len) | |
| 85 | + if let Some((gutter, line_text, caret_indent, caret_len)) = snippet_for(source, span, span_len) | |
| 87 | 86 | { |
| 88 | 87 | let blue = if color { "\x1b[34m" } else { "" }; |
| 89 | - eprintln!("{blue}{gutter:>5} |{reset} {line}", gutter = gutter, reset = reset, blue = blue, line = line_text); | |
| 88 | + eprintln!( | |
| 89 | + "{blue}{gutter:>5} |{reset} {line}", | |
| 90 | + gutter = gutter, | |
| 91 | + reset = reset, | |
| 92 | + blue = blue, | |
| 93 | + line = line_text | |
| 94 | + ); | |
| 90 | 95 | let mut caret = String::new(); |
| 91 | 96 | for _ in 0..caret_indent { |
| 92 | 97 | caret.push(' '); |
@@ -94,7 +99,13 @@ pub fn render(file: &str, source: &str, span: Span, level: Level, message: &str, | ||
| 94 | 99 | for _ in 0..caret_len.max(1) { |
| 95 | 100 | caret.push('^'); |
| 96 | 101 | } |
| 97 | - eprintln!("{blue} |{reset} {col_start}{caret}{reset}", blue = blue, reset = reset, col_start = col_start, caret = caret); | |
| 102 | + eprintln!( | |
| 103 | + "{blue} |{reset} {col_start}{caret}{reset}", | |
| 104 | + blue = blue, | |
| 105 | + reset = reset, | |
| 106 | + col_start = col_start, | |
| 107 | + caret = caret | |
| 108 | + ); | |
| 98 | 109 | } |
| 99 | 110 | } |
| 100 | 111 | |
@@ -102,11 +113,7 @@ pub fn render(file: &str, source: &str, span: Span, level: Level, message: &str, | ||
| 102 | 113 | /// Returns `(line_number, line_text, caret_indent, caret_len)`. |
| 103 | 114 | /// caret_indent is in display columns assuming a tab is replaced by |
| 104 | 115 | /// 4 spaces, matching how the line_text is emitted. |
| 105 | -fn snippet_for( | |
| 106 | - source: &str, | |
| 107 | - span: Span, | |
| 108 | - span_len: usize, | |
| 109 | -) -> Option<(u32, String, usize, usize)> { | |
| 116 | +fn snippet_for(source: &str, span: Span, span_len: usize) -> Option<(u32, String, usize, usize)> { | |
| 110 | 117 | if span.start.line == 0 { |
| 111 | 118 | return None; |
| 112 | 119 | } |
src/driver/mod.rsmodified@@ -12,8 +12,8 @@ use std::path::{Path, PathBuf}; | ||
| 12 | 12 | use std::process::Command; |
| 13 | 13 | use std::time::SystemTime; |
| 14 | 14 | |
| 15 | -use crate::codegen::{emit, isel, linearscan, peephole}; | |
| 16 | 15 | use crate::codegen::mir::MachineFunction; |
| 16 | +use crate::codegen::{emit, isel, linearscan, peephole}; | |
| 17 | 17 | use crate::ir::{lower, printer as ir_printer, verify}; |
| 18 | 18 | use crate::lexer::{detect_source_form, tokenize, SourceForm}; |
| 19 | 19 | use crate::parser::Parser; |
@@ -278,7 +278,8 @@ pub fn parse_cli(raw_args: &[String]) -> Result<ParsedCli, String> { | ||
| 278 | 278 | "-D" => { |
| 279 | 279 | i += 1; |
| 280 | 280 | let spec = args.get(i).ok_or("-D requires a macro name")?; |
| 281 | - opts.preprocessor_defines.push(parse_preprocessor_define(spec)?); | |
| 281 | + opts.preprocessor_defines | |
| 282 | + .push(parse_preprocessor_define(spec)?); | |
| 282 | 283 | } |
| 283 | 284 | arg if arg.starts_with("-D") => { |
| 284 | 285 | opts.preprocessor_defines |
@@ -400,9 +401,7 @@ pub fn parse_cli(raw_args: &[String]) -> Result<ParsedCli, String> { | ||
| 400 | 401 | opts.diagnostics_format = match val { |
| 401 | 402 | "text" => DiagnosticsFormat::Text, |
| 402 | 403 | "json" => DiagnosticsFormat::Json, |
| 403 | - other => { | |
| 404 | - return Err(format!("unknown --diagnostics-format value: {}", other)) | |
| 405 | - } | |
| 404 | + other => return Err(format!("unknown --diagnostics-format value: {}", other)), | |
| 406 | 405 | }; |
| 407 | 406 | } |
| 408 | 407 | |
@@ -441,11 +440,17 @@ fn parse_preprocessor_define(spec: &str) -> Result<(String, String), String> { | ||
| 441 | 440 | None => (spec, "1"), |
| 442 | 441 | }; |
| 443 | 442 | if name.is_empty() { |
| 444 | - return Err(format!("invalid macro definition '{}': missing macro name", spec)); | |
| 443 | + return Err(format!( | |
| 444 | + "invalid macro definition '{}': missing macro name", | |
| 445 | + spec | |
| 446 | + )); | |
| 445 | 447 | } |
| 446 | 448 | let mut chars = name.chars(); |
| 447 | 449 | let Some(first) = chars.next() else { |
| 448 | - return Err(format!("invalid macro definition '{}': missing macro name", spec)); | |
| 450 | + return Err(format!( | |
| 451 | + "invalid macro definition '{}': missing macro name", | |
| 452 | + spec | |
| 453 | + )); | |
| 449 | 454 | }; |
| 450 | 455 | if !(first == '_' || first.is_ascii_alphabetic()) { |
| 451 | 456 | return Err(format!( |
@@ -807,7 +812,11 @@ impl PhaseTimer { | ||
| 807 | 812 | eprintln!("─────────────────────────────────"); |
| 808 | 813 | for (name, d) in &self.samples { |
| 809 | 814 | let ms = d.as_secs_f64() * 1000.0; |
| 810 | - let pct = if total_ms > 0.0 { ms / total_ms * 100.0 } else { 0.0 }; | |
| 815 | + let pct = if total_ms > 0.0 { | |
| 816 | + ms / total_ms * 100.0 | |
| 817 | + } else { | |
| 818 | + 0.0 | |
| 819 | + }; | |
| 811 | 820 | eprintln!("{:<16} {:>8.2} {:>4.0}%", name, ms, pct); |
| 812 | 821 | } |
| 813 | 822 | eprintln!("─────────────────────────────────"); |
@@ -1013,8 +1022,7 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1013 | 1022 | for t in &tokens { |
| 1014 | 1023 | buf.push_str(&format!("{:?}\n", t)); |
| 1015 | 1024 | } |
| 1016 | - fs::write(&out, &buf) | |
| 1017 | - .map_err(|e| format!("cannot write '{}': {}", out.display(), e))?; | |
| 1025 | + fs::write(&out, &buf).map_err(|e| format!("cannot write '{}': {}", out.display(), e))?; | |
| 1018 | 1026 | return Ok(()); |
| 1019 | 1027 | } |
| 1020 | 1028 | |
@@ -1040,8 +1048,7 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1040 | 1048 | for u in &units { |
| 1041 | 1049 | buf.push_str(&format!("{:#?}\n", u)); |
| 1042 | 1050 | } |
| 1043 | - fs::write(&out, &buf) | |
| 1044 | - .map_err(|e| format!("cannot write '{}': {}", out.display(), e))?; | |
| 1051 | + fs::write(&out, &buf).map_err(|e| format!("cannot write '{}': {}", out.display(), e))?; | |
| 1045 | 1052 | return Ok(()); |
| 1046 | 1053 | } |
| 1047 | 1054 | |
@@ -1085,8 +1092,7 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1085 | 1092 | }; |
| 1086 | 1093 | // span_len is best-effort: end.col >= start.col on the same |
| 1087 | 1094 | // line gives a nice underline, otherwise default to 1. |
| 1088 | - let span_len = if d.span.end.line == d.span.start.line | |
| 1089 | - && d.span.end.col > d.span.start.col | |
| 1095 | + let span_len = if d.span.end.line == d.span.start.line && d.span.end.col > d.span.start.col | |
| 1090 | 1096 | { |
| 1091 | 1097 | (d.span.end.col - d.span.start.col) as usize |
| 1092 | 1098 | } else { |
@@ -1100,7 +1106,10 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1100 | 1106 | } |
| 1101 | 1107 | } |
| 1102 | 1108 | if had_error { |
| 1103 | - return Err(format!("aborting due to errors in {}", opts.input.display())); | |
| 1109 | + return Err(format!( | |
| 1110 | + "aborting due to errors in {}", | |
| 1111 | + opts.input.display() | |
| 1112 | + )); | |
| 1104 | 1113 | } |
| 1105 | 1114 | phase.end(&mut phases); |
| 1106 | 1115 | if opts.verbose { |
@@ -1114,7 +1123,13 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1114 | 1123 | external_char_len_star.extend(crate::sema::amod::extract_char_len_star_params(ext_mod)); |
| 1115 | 1124 | } |
| 1116 | 1125 | |
| 1117 | - let (mut ir_module, module_globals) = lower::lower_file(&units, &st, &type_layouts, external_globals, external_char_len_star); | |
| 1126 | + let (mut ir_module, module_globals) = lower::lower_file( | |
| 1127 | + &units, | |
| 1128 | + &st, | |
| 1129 | + &type_layouts, | |
| 1130 | + external_globals, | |
| 1131 | + external_char_len_star, | |
| 1132 | + ); | |
| 1118 | 1133 | let ir_errors = verify::verify_module(&ir_module); |
| 1119 | 1134 | if !ir_errors.is_empty() { |
| 1120 | 1135 | let msg = ir_errors |
@@ -1138,11 +1153,11 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1138 | 1153 | { |
| 1139 | 1154 | use crate::opt::pipeline::OptLevel as IrOpt; |
| 1140 | 1155 | let ir_opt = match opts.opt_level { |
| 1141 | - OptLevel::O0 => IrOpt::O0, | |
| 1142 | - OptLevel::O1 => IrOpt::O1, | |
| 1143 | - OptLevel::O2 => IrOpt::O2, | |
| 1144 | - OptLevel::O3 => IrOpt::O3, | |
| 1145 | - OptLevel::Os => IrOpt::Os, | |
| 1156 | + OptLevel::O0 => IrOpt::O0, | |
| 1157 | + OptLevel::O1 => IrOpt::O1, | |
| 1158 | + OptLevel::O2 => IrOpt::O2, | |
| 1159 | + OptLevel::O3 => IrOpt::O3, | |
| 1160 | + OptLevel::Os => IrOpt::Os, | |
| 1146 | 1161 | OptLevel::Ofast => IrOpt::Ofast, |
| 1147 | 1162 | }; |
| 1148 | 1163 | let pm = if ir_module.contains_i128_outside_globals() && opts.opt_level != OptLevel::O0 { |
@@ -1172,8 +1187,7 @@ pub fn compile(opts: &Options) -> Result<(), String> { | ||
| 1172 | 1187 | |
| 1173 | 1188 | if module_has_i128 && !ir_module.i128_backend_o0_supported() { |
| 1174 | 1189 | return Err( |
| 1175 | - "backend does not yet support integer(16) / i128 codegen; use --emit-ir for now" | |
| 1176 | - .into(), | |
| 1190 | + "backend does not yet support integer(16) / i128 codegen; use --emit-ir for now".into(), | |
| 1177 | 1191 | ); |
| 1178 | 1192 | } |
| 1179 | 1193 | |
@@ -1338,10 +1352,8 @@ _main: | ||
| 1338 | 1352 | &ir_module, |
| 1339 | 1353 | &std::collections::HashMap::new(), // char_len_star computed by writer from scope |
| 1340 | 1354 | ); |
| 1341 | - let amod_dir: std::path::PathBuf = opts | |
| 1342 | - .module_output_dir | |
| 1343 | - .clone() | |
| 1344 | - .unwrap_or_else(|| { | |
| 1355 | + let amod_dir: std::path::PathBuf = | |
| 1356 | + opts.module_output_dir.clone().unwrap_or_else(|| { | |
| 1345 | 1357 | opts.output_path() |
| 1346 | 1358 | .parent() |
| 1347 | 1359 | .unwrap_or_else(|| std::path::Path::new(".")) |
@@ -1465,7 +1477,8 @@ pub fn compile_multi(opts: &Options) -> Result<(), String> { | ||
| 1465 | 1477 | all_inputs.extend(opts.extra_inputs.iter().cloned()); |
| 1466 | 1478 | |
| 1467 | 1479 | // Scan dependencies. |
| 1468 | - let file_deps: Vec<dep_scan::FileDeps> = all_inputs.iter() | |
| 1480 | + let file_deps: Vec<dep_scan::FileDeps> = all_inputs | |
| 1481 | + .iter() | |
| 1469 | 1482 | .map(|p| dep_scan::scan_file(p)) |
| 1470 | 1483 | .collect::<Result<Vec<_>, _>>()?; |
| 1471 | 1484 | |
@@ -1474,13 +1487,16 @@ pub fn compile_multi(opts: &Options) -> Result<(), String> { | ||
| 1474 | 1487 | |
| 1475 | 1488 | // Compile each file in order. |
| 1476 | 1489 | let tmp_dir = std::env::temp_dir().join(format!("afs_multi_{}", std::process::id())); |
| 1477 | - fs::create_dir_all(&tmp_dir) | |
| 1478 | - .map_err(|e| format!("cannot create temp dir: {}", e))?; | |
| 1490 | + fs::create_dir_all(&tmp_dir).map_err(|e| format!("cannot create temp dir: {}", e))?; | |
| 1479 | 1491 | |
| 1480 | 1492 | let mut object_files: Vec<PathBuf> = Vec::new(); |
| 1481 | 1493 | for &idx in &order { |
| 1482 | 1494 | let src = &file_deps[idx].path; |
| 1483 | - let stem = src.file_stem().unwrap_or_default().to_str().unwrap_or("out"); | |
| 1495 | + let stem = src | |
| 1496 | + .file_stem() | |
| 1497 | + .unwrap_or_default() | |
| 1498 | + .to_str() | |
| 1499 | + .unwrap_or("out"); | |
| 1484 | 1500 | let obj_path = tmp_dir.join(format!("{}.o", stem)); |
| 1485 | 1501 | |
| 1486 | 1502 | // Build a single-file Options for this source by inheriting |
@@ -1524,7 +1540,10 @@ pub fn compile_multi(opts: &Options) -> Result<(), String> { | ||
| 1524 | 1540 | } |
| 1525 | 1541 | |
| 1526 | 1542 | // Link all object files. |
| 1527 | - let output = opts.output.clone().unwrap_or_else(|| PathBuf::from("a.out")); | |
| 1543 | + let output = opts | |
| 1544 | + .output | |
| 1545 | + .clone() | |
| 1546 | + .unwrap_or_else(|| PathBuf::from("a.out")); | |
| 1528 | 1547 | link_multi(&object_files, &output, opts)?; |
| 1529 | 1548 | |
| 1530 | 1549 | // Cleanup. |
@@ -1592,13 +1611,11 @@ fn find_runtime_lib() -> Result<String, String> { | ||
| 1592 | 1611 | } |
| 1593 | 1612 | } |
| 1594 | 1613 | |
| 1595 | - Err( | |
| 1596 | - "cannot find libarmfortas_rt.a. Searched: \ | |
| 1614 | + Err("cannot find libarmfortas_rt.a. Searched: \ | |
| 1597 | 1615 | $AFS_RUNTIME_PATH, cargo workspace, next to the compiler \ |
| 1598 | 1616 | binary, and /usr/local/lib. Build with \ |
| 1599 | 1617 | 'cargo build -p armfortas-rt' or set AFS_RUNTIME_PATH." |
| 1600 | - .into(), | |
| 1601 | - ) | |
| 1618 | + .into()) | |
| 1602 | 1619 | } |
| 1603 | 1620 | |
| 1604 | 1621 | fn maybe_refresh_runtime_lib(workspace_root: &Path) -> Result<(), String> { |
@@ -1611,7 +1628,9 @@ fn maybe_refresh_runtime_lib(workspace_root: &Path) -> Result<(), String> { | ||
| 1611 | 1628 | return Ok(()); |
| 1612 | 1629 | }; |
| 1613 | 1630 | let debug_archive = workspace_root.join("target/debug/libarmfortas_rt.a"); |
| 1614 | - let archive_mtime = fs::metadata(&debug_archive).ok().and_then(|meta| meta.modified().ok()); | |
| 1631 | + let archive_mtime = fs::metadata(&debug_archive) | |
| 1632 | + .ok() | |
| 1633 | + .and_then(|meta| meta.modified().ok()); | |
| 1615 | 1634 | |
| 1616 | 1635 | if archive_mtime.is_some_and(|mtime| mtime >= source_mtime) { |
| 1617 | 1636 | return Ok(()); |
@@ -1661,7 +1680,8 @@ fn find_workspace_root() -> Option<PathBuf> { | ||
| 1661 | 1680 | |
| 1662 | 1681 | for base in bases { |
| 1663 | 1682 | for ancestor in base.ancestors() { |
| 1664 | - if ancestor.join("Cargo.toml").exists() && ancestor.join("runtime/Cargo.toml").exists() { | |
| 1683 | + if ancestor.join("Cargo.toml").exists() && ancestor.join("runtime/Cargo.toml").exists() | |
| 1684 | + { | |
| 1665 | 1685 | return Some(ancestor.to_path_buf()); |
| 1666 | 1686 | } |
| 1667 | 1687 | } |
@@ -1744,7 +1764,11 @@ mod tests { | ||
| 1744 | 1764 | |
| 1745 | 1765 | compile(&opts).expect("O0 --emit-ir should support integer(16) staging"); |
| 1746 | 1766 | let ir = fs::read_to_string(&output).expect("missing emitted IR"); |
| 1747 | - assert!(ir.contains("i128"), "emitted IR should expose integer(16) as i128:\n{}", ir); | |
| 1767 | + assert!( | |
| 1768 | + ir.contains("i128"), | |
| 1769 | + "emitted IR should expose integer(16) as i128:\n{}", | |
| 1770 | + ir | |
| 1771 | + ); | |
| 1748 | 1772 | let _ = fs::remove_file(output); |
| 1749 | 1773 | } |
| 1750 | 1774 | |
@@ -1768,7 +1792,8 @@ mod tests { | ||
| 1768 | 1792 | ..Options::default() |
| 1769 | 1793 | }; |
| 1770 | 1794 | |
| 1771 | - let err = compile(&opts).expect_err("backend should reject integer(16) until i128 codegen lands"); | |
| 1795 | + let err = | |
| 1796 | + compile(&opts).expect_err("backend should reject integer(16) until i128 codegen lands"); | |
| 1772 | 1797 | assert!( |
| 1773 | 1798 | err.contains("backend does not yet support integer(16) / i128 codegen"), |
| 1774 | 1799 | "unexpected backend rejection:\n{}", |
@@ -1798,7 +1823,11 @@ mod tests { | ||
| 1798 | 1823 | |
| 1799 | 1824 | compile(&opts).expect("simple integer(16) memory traffic should codegen at O0"); |
| 1800 | 1825 | let asm = fs::read_to_string(&output).expect("missing emitted assembly"); |
| 1801 | - assert!(asm.contains("stp x16, x17"), "expected paired i128 store in asm:\n{}", asm); | |
| 1826 | + assert!( | |
| 1827 | + asm.contains("stp x16, x17"), | |
| 1828 | + "expected paired i128 store in asm:\n{}", | |
| 1829 | + asm | |
| 1830 | + ); | |
| 1802 | 1831 | let _ = fs::remove_file(output); |
| 1803 | 1832 | } |
| 1804 | 1833 | |
@@ -1824,7 +1853,11 @@ mod tests { | ||
| 1824 | 1853 | |
| 1825 | 1854 | compile(&opts).expect("simple integer(16) add should codegen at O0"); |
| 1826 | 1855 | let asm = fs::read_to_string(&output).expect("missing emitted assembly"); |
| 1827 | - assert!(asm.contains("adds x16, x16, x8"), "expected i128 add carry chain in asm:\n{}", asm); | |
| 1856 | + assert!( | |
| 1857 | + asm.contains("adds x16, x16, x8"), | |
| 1858 | + "expected i128 add carry chain in asm:\n{}", | |
| 1859 | + asm | |
| 1860 | + ); | |
| 1828 | 1861 | let _ = fs::remove_file(output); |
| 1829 | 1862 | } |
| 1830 | 1863 | |
@@ -1850,8 +1883,16 @@ mod tests { | ||
| 1850 | 1883 | |
| 1851 | 1884 | compile(&opts).expect("internal integer(16) call should codegen at O0"); |
| 1852 | 1885 | let asm = fs::read_to_string(&output).expect("missing emitted assembly"); |
| 1853 | - assert!(asm.contains("bl _add_one"), "expected internal helper call in asm:\n{}", asm); | |
| 1854 | - assert!(asm.contains("stp x0, x1"), "expected pair-register i128 ABI spill in asm:\n{}", asm); | |
| 1886 | + assert!( | |
| 1887 | + asm.contains("bl _add_one"), | |
| 1888 | + "expected internal helper call in asm:\n{}", | |
| 1889 | + asm | |
| 1890 | + ); | |
| 1891 | + assert!( | |
| 1892 | + asm.contains("stp x0, x1"), | |
| 1893 | + "expected pair-register i128 ABI spill in asm:\n{}", | |
| 1894 | + asm | |
| 1895 | + ); | |
| 1855 | 1896 | let _ = fs::remove_file(output); |
| 1856 | 1897 | } |
| 1857 | 1898 | |
@@ -1877,8 +1918,16 @@ mod tests { | ||
| 1877 | 1918 | |
| 1878 | 1919 | compile(&opts).expect("external integer(16) call should codegen at O0"); |
| 1879 | 1920 | let asm = fs::read_to_string(&output).expect("missing emitted assembly"); |
| 1880 | - assert!(asm.contains("bl _add_ext"), "expected external helper call in asm:\n{}", asm); | |
| 1881 | - assert!(asm.contains("stp x0, x1"), "expected pair-register i128 ABI spill in asm:\n{}", asm); | |
| 1921 | + assert!( | |
| 1922 | + asm.contains("bl _add_ext"), | |
| 1923 | + "expected external helper call in asm:\n{}", | |
| 1924 | + asm | |
| 1925 | + ); | |
| 1926 | + assert!( | |
| 1927 | + asm.contains("stp x0, x1"), | |
| 1928 | + "expected pair-register i128 ABI spill in asm:\n{}", | |
| 1929 | + asm | |
| 1930 | + ); | |
| 1882 | 1931 | let _ = fs::remove_file(output); |
| 1883 | 1932 | } |
| 1884 | 1933 | |
@@ -1904,8 +1953,16 @@ mod tests { | ||
| 1904 | 1953 | |
| 1905 | 1954 | compile(&opts).expect("integer(16) multiply should codegen at O1 after const fold"); |
| 1906 | 1955 | let asm = fs::read_to_string(&output).expect("missing emitted assembly"); |
| 1907 | - assert!(asm.contains("movz x16, #42"), "expected folded i128 constant in asm:\n{}", asm); | |
| 1908 | - assert!(!asm.contains("mul "), "expected O1 i128 multiply to fold away before backend:\n{}", asm); | |
| 1956 | + assert!( | |
| 1957 | + asm.contains("movz x16, #42"), | |
| 1958 | + "expected folded i128 constant in asm:\n{}", | |
| 1959 | + asm | |
| 1960 | + ); | |
| 1961 | + assert!( | |
| 1962 | + !asm.contains("mul "), | |
| 1963 | + "expected O1 i128 multiply to fold away before backend:\n{}", | |
| 1964 | + asm | |
| 1965 | + ); | |
| 1909 | 1966 | let _ = fs::remove_file(output); |
| 1910 | 1967 | } |
| 1911 | 1968 | |
src/ir/builder.rsmodified@@ -3,9 +3,9 @@ | ||
| 3 | 3 | //! Used by the AST→IR lowering pass and by tests. Manages ValueId |
| 4 | 4 | //! allocation, block insertion, and instruction emission. |
| 5 | 5 | |
| 6 | -use crate::lexer::{Span, Position}; | |
| 7 | -use super::types::*; | |
| 8 | 6 | use super::inst::*; |
| 7 | +use super::types::*; | |
| 8 | +use crate::lexer::{Position, Span}; | |
| 9 | 9 | |
| 10 | 10 | /// Builder for constructing a function's IR. |
| 11 | 11 | pub struct FuncBuilder<'a> { |
@@ -16,7 +16,10 @@ pub struct FuncBuilder<'a> { | ||
| 16 | 16 | impl<'a> FuncBuilder<'a> { |
| 17 | 17 | pub fn new(func: &'a mut Function) -> Self { |
| 18 | 18 | let entry = func.entry; |
| 19 | - Self { func, current_block: entry } | |
| 19 | + Self { | |
| 20 | + func, | |
| 21 | + current_block: entry, | |
| 22 | + } | |
| 20 | 23 | } |
| 21 | 24 | |
| 22 | 25 | /// Switch to emitting into a different block. |
@@ -38,19 +41,29 @@ impl<'a> FuncBuilder<'a> { | ||
| 38 | 41 | pub fn add_block_param(&mut self, block: BlockId, ty: IrType) -> ValueId { |
| 39 | 42 | let id = self.func.next_value_id(); |
| 40 | 43 | self.func.register_type(id, ty.clone()); |
| 41 | - self.func.block_mut(block).params.push(BlockParam { id, ty }); | |
| 44 | + self.func | |
| 45 | + .block_mut(block) | |
| 46 | + .params | |
| 47 | + .push(BlockParam { id, ty }); | |
| 42 | 48 | id |
| 43 | 49 | } |
| 44 | 50 | |
| 45 | 51 | /// Get the underlying function. |
| 46 | - pub fn func(&self) -> &Function { self.func } | |
| 52 | + pub fn func(&self) -> &Function { | |
| 53 | + self.func | |
| 54 | + } | |
| 47 | 55 | |
| 48 | 56 | // ---- Instruction emission ---- |
| 49 | 57 | |
| 50 | 58 | fn emit(&mut self, kind: InstKind, ty: IrType) -> ValueId { |
| 51 | 59 | let id = self.func.next_value_id(); |
| 52 | 60 | self.func.register_type(id, ty.clone()); |
| 53 | - let inst = Inst { id, kind, ty, span: dummy_span() }; | |
| 61 | + let inst = Inst { | |
| 62 | + id, | |
| 63 | + kind, | |
| 64 | + ty, | |
| 65 | + span: dummy_span(), | |
| 66 | + }; | |
| 54 | 67 | self.func.block_mut(self.current_block).insts.push(inst); |
| 55 | 68 | id |
| 56 | 69 | } |
@@ -110,74 +123,116 @@ impl<'a> FuncBuilder<'a> { | ||
| 110 | 123 | // ---- Integer arithmetic ---- |
| 111 | 124 | |
| 112 | 125 | pub fn iadd(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 113 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 126 | + let ty = self | |
| 127 | + .func | |
| 128 | + .value_type(lhs) | |
| 129 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 114 | 130 | self.emit(InstKind::IAdd(lhs, rhs), ty) |
| 115 | 131 | } |
| 116 | 132 | |
| 117 | 133 | pub fn isub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 118 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 134 | + let ty = self | |
| 135 | + .func | |
| 136 | + .value_type(lhs) | |
| 137 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 119 | 138 | self.emit(InstKind::ISub(lhs, rhs), ty) |
| 120 | 139 | } |
| 121 | 140 | |
| 122 | 141 | pub fn imul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 123 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 142 | + let ty = self | |
| 143 | + .func | |
| 144 | + .value_type(lhs) | |
| 145 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 124 | 146 | self.emit(InstKind::IMul(lhs, rhs), ty) |
| 125 | 147 | } |
| 126 | 148 | |
| 127 | 149 | pub fn idiv(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 128 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 150 | + let ty = self | |
| 151 | + .func | |
| 152 | + .value_type(lhs) | |
| 153 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 129 | 154 | self.emit(InstKind::IDiv(lhs, rhs), ty) |
| 130 | 155 | } |
| 131 | 156 | |
| 132 | 157 | pub fn imod(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 133 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 158 | + let ty = self | |
| 159 | + .func | |
| 160 | + .value_type(lhs) | |
| 161 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 134 | 162 | self.emit(InstKind::IMod(lhs, rhs), ty) |
| 135 | 163 | } |
| 136 | 164 | |
| 137 | 165 | pub fn ineg(&mut self, val: ValueId) -> ValueId { |
| 138 | - let ty = self.func.value_type(val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 166 | + let ty = self | |
| 167 | + .func | |
| 168 | + .value_type(val) | |
| 169 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 139 | 170 | self.emit(InstKind::INeg(val), ty) |
| 140 | 171 | } |
| 141 | 172 | |
| 142 | 173 | // ---- Float arithmetic ---- |
| 143 | 174 | |
| 144 | 175 | pub fn fadd(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 145 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 176 | + let ty = self | |
| 177 | + .func | |
| 178 | + .value_type(lhs) | |
| 179 | + .unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 146 | 180 | self.emit(InstKind::FAdd(lhs, rhs), ty) |
| 147 | 181 | } |
| 148 | 182 | |
| 149 | 183 | pub fn fsub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 150 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 184 | + let ty = self | |
| 185 | + .func | |
| 186 | + .value_type(lhs) | |
| 187 | + .unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 151 | 188 | self.emit(InstKind::FSub(lhs, rhs), ty) |
| 152 | 189 | } |
| 153 | 190 | |
| 154 | 191 | pub fn fmul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 155 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 192 | + let ty = self | |
| 193 | + .func | |
| 194 | + .value_type(lhs) | |
| 195 | + .unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 156 | 196 | self.emit(InstKind::FMul(lhs, rhs), ty) |
| 157 | 197 | } |
| 158 | 198 | |
| 159 | 199 | pub fn fdiv(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 160 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 200 | + let ty = self | |
| 201 | + .func | |
| 202 | + .value_type(lhs) | |
| 203 | + .unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 161 | 204 | self.emit(InstKind::FDiv(lhs, rhs), ty) |
| 162 | 205 | } |
| 163 | 206 | |
| 164 | 207 | pub fn fneg(&mut self, val: ValueId) -> ValueId { |
| 165 | - let ty = self.func.value_type(val).unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 208 | + let ty = self | |
| 209 | + .func | |
| 210 | + .value_type(val) | |
| 211 | + .unwrap_or(IrType::Float(FloatWidth::F32)); | |
| 166 | 212 | self.emit(InstKind::FNeg(val), ty) |
| 167 | 213 | } |
| 168 | 214 | |
| 169 | 215 | pub fn fabs(&mut self, val: ValueId) -> ValueId { |
| 170 | - let ty = self.func.value_type(val).unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 216 | + let ty = self | |
| 217 | + .func | |
| 218 | + .value_type(val) | |
| 219 | + .unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 171 | 220 | self.emit(InstKind::FAbs(val), ty) |
| 172 | 221 | } |
| 173 | 222 | |
| 174 | 223 | pub fn fsqrt(&mut self, val: ValueId) -> ValueId { |
| 175 | - let ty = self.func.value_type(val).unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 224 | + let ty = self | |
| 225 | + .func | |
| 226 | + .value_type(val) | |
| 227 | + .unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 176 | 228 | self.emit(InstKind::FSqrt(val), ty) |
| 177 | 229 | } |
| 178 | 230 | |
| 179 | 231 | pub fn fpow(&mut self, base: ValueId, exp: ValueId) -> ValueId { |
| 180 | - let ty = self.func.value_type(base).unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 232 | + let ty = self | |
| 233 | + .func | |
| 234 | + .value_type(base) | |
| 235 | + .unwrap_or(IrType::Float(FloatWidth::F64)); | |
| 181 | 236 | self.emit(InstKind::FPow(base, exp), ty) |
| 182 | 237 | } |
| 183 | 238 | |
@@ -195,7 +250,10 @@ impl<'a> FuncBuilder<'a> { | ||
| 195 | 250 | |
| 196 | 251 | /// Conditional select: cond ? true_val : false_val |
| 197 | 252 | pub fn select(&mut self, cond: ValueId, true_val: ValueId, false_val: ValueId) -> ValueId { |
| 198 | - let ty = self.func.value_type(true_val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 253 | + let ty = self | |
| 254 | + .func | |
| 255 | + .value_type(true_val) | |
| 256 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 199 | 257 | self.emit(InstKind::Select(cond, true_val, false_val), ty) |
| 200 | 258 | } |
| 201 | 259 | |
@@ -216,47 +274,74 @@ impl<'a> FuncBuilder<'a> { | ||
| 216 | 274 | // ---- Bitwise ---- |
| 217 | 275 | |
| 218 | 276 | pub fn bit_and(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 219 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 277 | + let ty = self | |
| 278 | + .func | |
| 279 | + .value_type(lhs) | |
| 280 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 220 | 281 | self.emit(InstKind::BitAnd(lhs, rhs), ty) |
| 221 | 282 | } |
| 222 | 283 | |
| 223 | 284 | pub fn bit_or(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 224 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 285 | + let ty = self | |
| 286 | + .func | |
| 287 | + .value_type(lhs) | |
| 288 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 225 | 289 | self.emit(InstKind::BitOr(lhs, rhs), ty) |
| 226 | 290 | } |
| 227 | 291 | |
| 228 | 292 | pub fn bit_xor(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 229 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 293 | + let ty = self | |
| 294 | + .func | |
| 295 | + .value_type(lhs) | |
| 296 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 230 | 297 | self.emit(InstKind::BitXor(lhs, rhs), ty) |
| 231 | 298 | } |
| 232 | 299 | |
| 233 | 300 | pub fn shl(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 234 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 301 | + let ty = self | |
| 302 | + .func | |
| 303 | + .value_type(lhs) | |
| 304 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 235 | 305 | self.emit(InstKind::Shl(lhs, rhs), ty) |
| 236 | 306 | } |
| 237 | 307 | |
| 238 | 308 | pub fn bit_not(&mut self, val: ValueId) -> ValueId { |
| 239 | - let ty = self.func.value_type(val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 309 | + let ty = self | |
| 310 | + .func | |
| 311 | + .value_type(val) | |
| 312 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 240 | 313 | self.emit(InstKind::BitNot(val), ty) |
| 241 | 314 | } |
| 242 | 315 | |
| 243 | 316 | pub fn lshr(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { |
| 244 | - let ty = self.func.value_type(lhs).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 317 | + let ty = self | |
| 318 | + .func | |
| 319 | + .value_type(lhs) | |
| 320 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 245 | 321 | self.emit(InstKind::LShr(lhs, rhs), ty) |
| 246 | 322 | } |
| 247 | 323 | |
| 248 | 324 | pub fn clz(&mut self, val: ValueId) -> ValueId { |
| 249 | - let ty = self.func.value_type(val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 325 | + let ty = self | |
| 326 | + .func | |
| 327 | + .value_type(val) | |
| 328 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 250 | 329 | self.emit(InstKind::CountLeadingZeros(val), ty) |
| 251 | 330 | } |
| 252 | 331 | |
| 253 | 332 | pub fn ctz(&mut self, val: ValueId) -> ValueId { |
| 254 | - let ty = self.func.value_type(val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 333 | + let ty = self | |
| 334 | + .func | |
| 335 | + .value_type(val) | |
| 336 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 255 | 337 | self.emit(InstKind::CountTrailingZeros(val), ty) |
| 256 | 338 | } |
| 257 | 339 | |
| 258 | 340 | pub fn popcount(&mut self, val: ValueId) -> ValueId { |
| 259 | - let ty = self.func.value_type(val).unwrap_or(IrType::Int(IntWidth::I32)); | |
| 341 | + let ty = self | |
| 342 | + .func | |
| 343 | + .value_type(val) | |
| 344 | + .unwrap_or(IrType::Int(IntWidth::I32)); | |
| 260 | 345 | self.emit(InstKind::PopCount(val), ty) |
| 261 | 346 | } |
| 262 | 347 | |
@@ -330,7 +415,10 @@ impl<'a> FuncBuilder<'a> { | ||
| 330 | 415 | } |
| 331 | 416 | |
| 332 | 417 | pub fn gep(&mut self, base: ValueId, indices: Vec<ValueId>, result_ty: IrType) -> ValueId { |
| 333 | - self.emit(InstKind::GetElementPtr(base, indices), IrType::Ptr(Box::new(result_ty))) | |
| 418 | + self.emit( | |
| 419 | + InstKind::GetElementPtr(base, indices), | |
| 420 | + IrType::Ptr(Box::new(result_ty)), | |
| 421 | + ) | |
| 334 | 422 | } |
| 335 | 423 | |
| 336 | 424 | // ---- Calls ---- |
@@ -339,7 +427,12 @@ impl<'a> FuncBuilder<'a> { | ||
| 339 | 427 | self.emit(InstKind::Call(func_ref, args), ret_ty) |
| 340 | 428 | } |
| 341 | 429 | |
| 342 | - pub fn runtime_call(&mut self, func: RuntimeFunc, args: Vec<ValueId>, ret_ty: IrType) -> ValueId { | |
| 430 | + pub fn runtime_call( | |
| 431 | + &mut self, | |
| 432 | + func: RuntimeFunc, | |
| 433 | + args: Vec<ValueId>, | |
| 434 | + ret_ty: IrType, | |
| 435 | + ) -> ValueId { | |
| 343 | 436 | self.emit(InstKind::RuntimeCall(func, args), ret_ty) |
| 344 | 437 | } |
| 345 | 438 | |
@@ -365,8 +458,7 @@ impl<'a> FuncBuilder<'a> { | ||
| 365 | 458 | } |
| 366 | 459 | |
| 367 | 460 | pub fn branch(&mut self, dest: BlockId, args: Vec<ValueId>) { |
| 368 | - self.func.block_mut(self.current_block).terminator = | |
| 369 | - Some(Terminator::Branch(dest, args)); | |
| 461 | + self.func.block_mut(self.current_block).terminator = Some(Terminator::Branch(dest, args)); | |
| 370 | 462 | } |
| 371 | 463 | |
| 372 | 464 | pub fn cond_branch( |
@@ -377,13 +469,21 @@ impl<'a> FuncBuilder<'a> { | ||
| 377 | 469 | false_dest: BlockId, |
| 378 | 470 | false_args: Vec<ValueId>, |
| 379 | 471 | ) { |
| 380 | - self.func.block_mut(self.current_block).terminator = | |
| 381 | - Some(Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args }); | |
| 472 | + self.func.block_mut(self.current_block).terminator = Some(Terminator::CondBranch { | |
| 473 | + cond, | |
| 474 | + true_dest, | |
| 475 | + true_args, | |
| 476 | + false_dest, | |
| 477 | + false_args, | |
| 478 | + }); | |
| 382 | 479 | } |
| 383 | 480 | |
| 384 | 481 | pub fn switch(&mut self, selector: ValueId, cases: Vec<(i64, BlockId)>, default: BlockId) { |
| 385 | - self.func.block_mut(self.current_block).terminator = | |
| 386 | - Some(Terminator::Switch { selector, cases, default }); | |
| 482 | + self.func.block_mut(self.current_block).terminator = Some(Terminator::Switch { | |
| 483 | + selector, | |
| 484 | + cases, | |
| 485 | + default, | |
| 486 | + }); | |
| 387 | 487 | } |
| 388 | 488 | |
| 389 | 489 | pub fn unreachable(&mut self) { |
@@ -403,7 +503,11 @@ impl<'a> FuncBuilder<'a> { | ||
| 403 | 503 | } |
| 404 | 504 | |
| 405 | 505 | fn dummy_span() -> Span { |
| 406 | - Span { file_id: 0, start: Position { line: 0, col: 0 }, end: Position { line: 0, col: 0 } } | |
| 506 | + Span { | |
| 507 | + file_id: 0, | |
| 508 | + start: Position { line: 0, col: 0 }, | |
| 509 | + end: Position { line: 0, col: 0 }, | |
| 510 | + } | |
| 407 | 511 | } |
| 408 | 512 | |
| 409 | 513 | #[cfg(test)] |
src/ir/inst.rsmodified@@ -3,7 +3,7 @@ | ||
| 3 | 3 | //! Every instruction produces a value (ValueId) in SSA form. |
| 4 | 4 | //! Basic blocks end with exactly one Terminator. |
| 5 | 5 | |
| 6 | -use super::types::{IrType, IntWidth, FloatWidth}; | |
| 6 | +use super::types::{FloatWidth, IntWidth, IrType}; | |
| 7 | 7 | use crate::lexer::Span; |
| 8 | 8 | use std::collections::HashMap; |
| 9 | 9 | |
@@ -125,7 +125,7 @@ pub enum InstKind { | ||
| 125 | 125 | FloatToInt(ValueId, IntWidth), |
| 126 | 126 | FloatExtend(ValueId, FloatWidth), |
| 127 | 127 | FloatTrunc(ValueId, FloatWidth), |
| 128 | - IntExtend(ValueId, IntWidth, bool), // bool = signed | |
| 128 | + IntExtend(ValueId, IntWidth, bool), // bool = signed | |
| 129 | 129 | IntTrunc(ValueId, IntWidth), |
| 130 | 130 | /// Convert a pointer to an integer (address as i64). |
| 131 | 131 | PtrToInt(ValueId), |
@@ -283,12 +283,18 @@ impl Function { | ||
| 283 | 283 | /// Get a block by ID. Panics if the ID is not present — use |
| 284 | 284 | /// `try_block` for graceful degradation. |
| 285 | 285 | pub fn block(&self, id: BlockId) -> &BasicBlock { |
| 286 | - self.blocks.iter().find(|b| b.id == id).expect("block not found") | |
| 286 | + self.blocks | |
| 287 | + .iter() | |
| 288 | + .find(|b| b.id == id) | |
| 289 | + .expect("block not found") | |
| 287 | 290 | } |
| 288 | 291 | |
| 289 | 292 | /// Get a mutable block by ID. Panics if the ID is not present. |
| 290 | 293 | pub fn block_mut(&mut self, id: BlockId) -> &mut BasicBlock { |
| 291 | - self.blocks.iter_mut().find(|b| b.id == id).expect("block not found") | |
| 294 | + self.blocks | |
| 295 | + .iter_mut() | |
| 296 | + .find(|b| b.id == id) | |
| 297 | + .expect("block not found") | |
| 292 | 298 | } |
| 293 | 299 | |
| 294 | 300 | /// Get a block by ID, returning `None` if the ID is not |
@@ -337,14 +343,20 @@ impl Function { | ||
| 337 | 343 | // pointer-type bugs for entire compilation units. Recompute |
| 338 | 344 | // on-demand so consumers always get a consistent answer. |
| 339 | 345 | for p in &self.params { |
| 340 | - if p.id == id { return Some(p.ty.clone()); } | |
| 346 | + if p.id == id { | |
| 347 | + return Some(p.ty.clone()); | |
| 348 | + } | |
| 341 | 349 | } |
| 342 | 350 | for block in &self.blocks { |
| 343 | 351 | for bp in &block.params { |
| 344 | - if bp.id == id { return Some(bp.ty.clone()); } | |
| 352 | + if bp.id == id { | |
| 353 | + return Some(bp.ty.clone()); | |
| 354 | + } | |
| 345 | 355 | } |
| 346 | 356 | for inst in &block.insts { |
| 347 | - if inst.id == id { return Some(inst.ty.clone()); } | |
| 357 | + if inst.id == id { | |
| 358 | + return Some(inst.ty.clone()); | |
| 359 | + } | |
| 348 | 360 | } |
| 349 | 361 | } |
| 350 | 362 | None |
@@ -354,7 +366,9 @@ impl Function { | ||
| 354 | 366 | pub fn find_defining_inst(&self, id: ValueId) -> Option<&Inst> { |
| 355 | 367 | for block in &self.blocks { |
| 356 | 368 | for inst in &block.insts { |
| 357 | - if inst.id == id { return Some(inst); } | |
| 369 | + if inst.id == id { | |
| 370 | + return Some(inst); | |
| 371 | + } | |
| 358 | 372 | } |
| 359 | 373 | } |
| 360 | 374 | None |
@@ -435,14 +449,28 @@ impl Module { | ||
| 435 | 449 | |
| 436 | 450 | /// True when any live IR surface in the module uses `i128`. |
| 437 | 451 | pub fn contains_i128(&self) -> bool { |
| 438 | - self.globals.iter().any(|global| type_contains_i128(self, &global.ty)) | |
| 439 | - || self.extern_funcs.iter().any(|func| sig_contains_i128(self, &func.sig)) | |
| 452 | + self.globals | |
| 453 | + .iter() | |
| 454 | + .any(|global| type_contains_i128(self, &global.ty)) | |
| 455 | + || self | |
| 456 | + .extern_funcs | |
| 457 | + .iter() | |
| 458 | + .any(|func| sig_contains_i128(self, &func.sig)) | |
| 440 | 459 | || self.functions.iter().any(|func| { |
| 441 | 460 | type_contains_i128(self, &func.return_type) |
| 442 | - || func.params.iter().any(|param| type_contains_i128(self, ¶m.ty)) | |
| 461 | + || func | |
| 462 | + .params | |
| 463 | + .iter() | |
| 464 | + .any(|param| type_contains_i128(self, ¶m.ty)) | |
| 443 | 465 | || func.blocks.iter().any(|block| { |
| 444 | - block.params.iter().any(|param| type_contains_i128(self, ¶m.ty)) | |
| 445 | - || block.insts.iter().any(|inst| type_contains_i128(self, &inst.ty)) | |
| 466 | + block | |
| 467 | + .params | |
| 468 | + .iter() | |
| 469 | + .any(|param| type_contains_i128(self, ¶m.ty)) | |
| 470 | + || block | |
| 471 | + .insts | |
| 472 | + .iter() | |
| 473 | + .any(|inst| type_contains_i128(self, &inst.ty)) | |
| 446 | 474 | }) |
| 447 | 475 | }) |
| 448 | 476 | } |
@@ -454,13 +482,24 @@ impl Module { | ||
| 454 | 482 | /// Parameters, returns, instruction results, block params, and extern |
| 455 | 483 | /// signatures still imply unsupported ABI or codegen work. |
| 456 | 484 | pub fn contains_i128_outside_globals(&self) -> bool { |
| 457 | - self.extern_funcs.iter().any(|func| sig_contains_i128(self, &func.sig)) | |
| 485 | + self.extern_funcs | |
| 486 | + .iter() | |
| 487 | + .any(|func| sig_contains_i128(self, &func.sig)) | |
| 458 | 488 | || self.functions.iter().any(|func| { |
| 459 | 489 | type_contains_i128(self, &func.return_type) |
| 460 | - || func.params.iter().any(|param| type_contains_i128(self, ¶m.ty)) | |
| 490 | + || func | |
| 491 | + .params | |
| 492 | + .iter() | |
| 493 | + .any(|param| type_contains_i128(self, ¶m.ty)) | |
| 461 | 494 | || func.blocks.iter().any(|block| { |
| 462 | - block.params.iter().any(|param| type_contains_i128(self, ¶m.ty)) | |
| 463 | - || block.insts.iter().any(|inst| type_contains_i128(self, &inst.ty)) | |
| 495 | + block | |
| 496 | + .params | |
| 497 | + .iter() | |
| 498 | + .any(|param| type_contains_i128(self, ¶m.ty)) | |
| 499 | + || block | |
| 500 | + .insts | |
| 501 | + .iter() | |
| 502 | + .any(|inst| type_contains_i128(self, &inst.ty)) | |
| 464 | 503 | }) |
| 465 | 504 | }) |
| 466 | 505 | } |
@@ -494,17 +533,14 @@ impl Module { | ||
| 494 | 533 | self.globals |
| 495 | 534 | .iter() |
| 496 | 535 | .all(|global| global_i128_backend_data_supported(self, global)) |
| 497 | - && self | |
| 498 | - .extern_funcs | |
| 499 | - .iter() | |
| 500 | - .all(|func| { | |
| 501 | - abi_type_i128_backend_o0_supported(self, &func.sig.ret, true) | |
| 502 | - && func | |
| 503 | - .sig | |
| 504 | - .params | |
| 505 | - .iter() | |
| 506 | - .all(|param| abi_type_i128_backend_o0_supported(self, param, true)) | |
| 507 | - }) | |
| 536 | + && self.extern_funcs.iter().all(|func| { | |
| 537 | + abi_type_i128_backend_o0_supported(self, &func.sig.ret, true) | |
| 538 | + && func | |
| 539 | + .sig | |
| 540 | + .params | |
| 541 | + .iter() | |
| 542 | + .all(|param| abi_type_i128_backend_o0_supported(self, param, true)) | |
| 543 | + }) | |
| 508 | 544 | && self |
| 509 | 545 | .functions |
| 510 | 546 | .iter() |
@@ -514,17 +550,21 @@ impl Module { | ||
| 514 | 550 | |
| 515 | 551 | fn sig_contains_i128(module: &Module, sig: &super::types::FuncSig) -> bool { |
| 516 | 552 | type_contains_i128(module, &sig.ret) |
| 517 | - || sig.params.iter().any(|param| type_contains_i128(module, param)) | |
| 553 | + || sig | |
| 554 | + .params | |
| 555 | + .iter() | |
| 556 | + .any(|param| type_contains_i128(module, param)) | |
| 518 | 557 | } |
| 519 | 558 | |
| 520 | 559 | fn type_contains_i128(module: &Module, ty: &IrType) -> bool { |
| 521 | 560 | match ty { |
| 522 | 561 | IrType::Int(IntWidth::I128) => true, |
| 523 | 562 | IrType::Ptr(inner) | IrType::Array(inner, _) => type_contains_i128(module, inner), |
| 524 | - IrType::Struct(id) => module | |
| 525 | - .struct_defs | |
| 526 | - .get(*id as usize) | |
| 527 | - .is_some_and(|def| def.fields.iter().any(|(_, field_ty)| type_contains_i128(module, field_ty))), | |
| 563 | + IrType::Struct(id) => module.struct_defs.get(*id as usize).is_some_and(|def| { | |
| 564 | + def.fields | |
| 565 | + .iter() | |
| 566 | + .any(|(_, field_ty)| type_contains_i128(module, field_ty)) | |
| 567 | + }), | |
| 528 | 568 | IrType::FuncPtr(sig) => sig_contains_i128(module, sig), |
| 529 | 569 | _ => false, |
| 530 | 570 | } |
@@ -586,12 +626,14 @@ fn runtime_call_i128_backend_o0_supported( | ||
| 586 | 626 | result_ty: &IrType, |
| 587 | 627 | ) -> bool { |
| 588 | 628 | match rf { |
| 589 | - RuntimeFunc::PrintInt => matches!(result_ty, IrType::Void) | |
| 590 | - && args.len() == 1 | |
| 591 | - && args | |
| 592 | - .iter() | |
| 593 | - .filter_map(|arg| func.value_type(*arg)) | |
| 594 | - .all(|ty| abi_type_i128_backend_o0_supported(module, &ty, true)), | |
| 629 | + RuntimeFunc::PrintInt => { | |
| 630 | + matches!(result_ty, IrType::Void) | |
| 631 | + && args.len() == 1 | |
| 632 | + && args | |
| 633 | + .iter() | |
| 634 | + .filter_map(|arg| func.value_type(*arg)) | |
| 635 | + .all(|ty| abi_type_i128_backend_o0_supported(module, &ty, true)) | |
| 636 | + } | |
| 595 | 637 | _ => false, |
| 596 | 638 | } |
| 597 | 639 | } |
@@ -640,7 +682,10 @@ fn inst_i128_backend_o0_supported(module: &Module, func: &Function, inst: &Inst) | ||
| 640 | 682 | // dereferencing an sret-style hidden result-buffer pointer. |
| 641 | 683 | InstKind::Load(_) if matches!(inst.ty, IrType::Ptr(_)) => true, |
| 642 | 684 | InstKind::IAdd(..) | InstKind::ISub(..) | InstKind::INeg(_) |
| 643 | - if matches!(inst.ty, IrType::Int(IntWidth::I128)) => true, | |
| 685 | + if matches!(inst.ty, IrType::Int(IntWidth::I128)) => | |
| 686 | + { | |
| 687 | + true | |
| 688 | + } | |
| 644 | 689 | InstKind::ICmp(..) if uses_i128 => true, |
| 645 | 690 | InstKind::Select(..) if matches!(inst.ty, IrType::Int(IntWidth::I128)) => true, |
| 646 | 691 | InstKind::Call(callee, args) if inst_ty_has_i128 || uses_i128 => { |
@@ -678,7 +723,11 @@ fn terminator_i128_backend_o0_supported( | ||
| 678 | 723 | .iter() |
| 679 | 724 | .filter_map(|arg| func.value_type(*arg)) |
| 680 | 725 | .all(|ty| ssa_type_i128_backend_o0_supported(module, &ty)), |
| 681 | - Terminator::CondBranch { true_args, false_args, .. } => true_args | |
| 726 | + Terminator::CondBranch { | |
| 727 | + true_args, | |
| 728 | + false_args, | |
| 729 | + .. | |
| 730 | + } => true_args | |
| 682 | 731 | .iter() |
| 683 | 732 | .chain(false_args.iter()) |
| 684 | 733 | .filter_map(|arg| func.value_type(*arg)) |
src/ir/mod.rsmodified@@ -4,10 +4,10 @@ | ||
| 4 | 4 | //! Fortran-aware: understands array descriptors, string descriptors, |
| 5 | 5 | //! and allocatable semantics. |
| 6 | 6 | |
| 7 | -pub mod types; | |
| 8 | -pub mod inst; | |
| 9 | 7 | pub mod builder; |
| 8 | +pub mod inst; | |
| 9 | +pub mod lower; | |
| 10 | 10 | pub mod printer; |
| 11 | +pub mod types; | |
| 11 | 12 | pub mod verify; |
| 12 | 13 | pub mod walk; |
| 13 | -pub mod lower; | |
src/ir/printer.rsmodified@@ -2,8 +2,8 @@ | ||
| 2 | 2 | //! |
| 3 | 3 | //! Used by `-emit-ir` and in test assertions. |
| 4 | 4 | |
| 5 | -use std::fmt::Write; | |
| 6 | 5 | use super::inst::*; |
| 6 | +use std::fmt::Write; | |
| 7 | 7 | |
| 8 | 8 | /// Print a module to a string. |
| 9 | 9 | pub fn print_module(module: &Module) -> String { |
@@ -13,7 +13,9 @@ pub fn print_module(module: &Module) -> String { | ||
| 13 | 13 | for sd in &module.struct_defs { |
| 14 | 14 | write!(out, " struct {} {{ ", sd.name).unwrap(); |
| 15 | 15 | for (i, (name, ty)) in sd.fields.iter().enumerate() { |
| 16 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 16 | + if i > 0 { | |
| 17 | + write!(out, ", ").unwrap(); | |
| 18 | + } | |
| 17 | 19 | write!(out, "{}: {}", name, ty).unwrap(); |
| 18 | 20 | } |
| 19 | 21 | writeln!(out, " }}").unwrap(); |
@@ -26,7 +28,9 @@ pub fn print_module(module: &Module) -> String { | ||
| 26 | 28 | GlobalInit::Zero => write!(out, " = zeroinit").unwrap(), |
| 27 | 29 | GlobalInit::Int(v) => write!(out, " = {}", v).unwrap(), |
| 28 | 30 | GlobalInit::Float(v) => write!(out, " = {}", v).unwrap(), |
| 29 | - GlobalInit::String(s) => write!(out, " = {:?}", String::from_utf8_lossy(s)).unwrap(), | |
| 31 | + GlobalInit::String(s) => { | |
| 32 | + write!(out, " = {:?}", String::from_utf8_lossy(s)).unwrap() | |
| 33 | + } | |
| 30 | 34 | GlobalInit::IntArray(vs) => { |
| 31 | 35 | let s: Vec<String> = vs.iter().map(|v| v.to_string()).collect(); |
| 32 | 36 | write!(out, " = [{}]", s.join(", ")).unwrap(); |
@@ -43,7 +47,9 @@ pub fn print_module(module: &Module) -> String { | ||
| 43 | 47 | for ef in &module.extern_funcs { |
| 44 | 48 | write!(out, " extern fn @{}(", ef.name).unwrap(); |
| 45 | 49 | for (i, p) in ef.sig.params.iter().enumerate() { |
| 46 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 50 | + if i > 0 { | |
| 51 | + write!(out, ", ").unwrap(); | |
| 52 | + } | |
| 47 | 53 | write!(out, "{}", p).unwrap(); |
| 48 | 54 | } |
| 49 | 55 | writeln!(out, ") -> {}", ef.sig.ret).unwrap(); |
@@ -61,12 +67,22 @@ fn print_function_in(module: &Module, func: &Function) -> String { | ||
| 61 | 67 | let mut out = String::new(); |
| 62 | 68 | // Print function attributes. |
| 63 | 69 | let mut attrs = Vec::new(); |
| 64 | - if func.is_pure { attrs.push("pure"); } | |
| 65 | - if func.is_elemental { attrs.push("elemental"); } | |
| 66 | - let attr_str = if attrs.is_empty() { String::new() } else { format!(" [{}]", attrs.join(", ")) }; | |
| 70 | + if func.is_pure { | |
| 71 | + attrs.push("pure"); | |
| 72 | + } | |
| 73 | + if func.is_elemental { | |
| 74 | + attrs.push("elemental"); | |
| 75 | + } | |
| 76 | + let attr_str = if attrs.is_empty() { | |
| 77 | + String::new() | |
| 78 | + } else { | |
| 79 | + format!(" [{}]", attrs.join(", ")) | |
| 80 | + }; | |
| 67 | 81 | write!(out, " func @{}{}(", func.name, attr_str).unwrap(); |
| 68 | 82 | for (i, p) in func.params.iter().enumerate() { |
| 69 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 83 | + if i > 0 { | |
| 84 | + write!(out, ", ").unwrap(); | |
| 85 | + } | |
| 70 | 86 | write!(out, "%{}: {}", p.id.0, p.ty).unwrap(); |
| 71 | 87 | } |
| 72 | 88 | writeln!(out, ") -> {} {{", func.return_type).unwrap(); |
@@ -93,11 +109,17 @@ fn print_block_with_module(block: &BasicBlock, func: &Function, module: &Module) | ||
| 93 | 109 | print_block_with_module_opt(block, func, Some(module)) |
| 94 | 110 | } |
| 95 | 111 | |
| 96 | -fn print_block_with_module_opt(block: &BasicBlock, func: &Function, module: Option<&Module>) -> String { | |
| 112 | +fn print_block_with_module_opt( | |
| 113 | + block: &BasicBlock, | |
| 114 | + func: &Function, | |
| 115 | + module: Option<&Module>, | |
| 116 | +) -> String { | |
| 97 | 117 | let mut out = String::new(); |
| 98 | 118 | write!(out, " {}(", block.name).unwrap(); |
| 99 | 119 | for (i, bp) in block.params.iter().enumerate() { |
| 100 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 120 | + if i > 0 { | |
| 121 | + write!(out, ", ").unwrap(); | |
| 122 | + } | |
| 101 | 123 | write!(out, "%{}: {}", bp.id.0, bp.ty).unwrap(); |
| 102 | 124 | } |
| 103 | 125 | writeln!(out, "):").unwrap(); |
@@ -118,7 +140,9 @@ pub fn print_block(block: &BasicBlock) -> String { | ||
| 118 | 140 | let mut out = String::new(); |
| 119 | 141 | write!(out, " {}(", block.name).unwrap(); |
| 120 | 142 | for (i, bp) in block.params.iter().enumerate() { |
| 121 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 143 | + if i > 0 { | |
| 144 | + write!(out, ", ").unwrap(); | |
| 145 | + } | |
| 122 | 146 | write!(out, "%{}: {}", bp.id.0, bp.ty).unwrap(); |
| 123 | 147 | } |
| 124 | 148 | writeln!(out, "):").unwrap(); |
@@ -143,12 +167,22 @@ fn print_function_with_module_opt(module: Option<&Module>, func: &Function) -> S | ||
| 143 | 167 | let mut out = String::new(); |
| 144 | 168 | // Print function attributes. |
| 145 | 169 | let mut attrs = Vec::new(); |
| 146 | - if func.is_pure { attrs.push("pure"); } | |
| 147 | - if func.is_elemental { attrs.push("elemental"); } | |
| 148 | - let attr_str = if attrs.is_empty() { String::new() } else { format!(" [{}]", attrs.join(", ")) }; | |
| 170 | + if func.is_pure { | |
| 171 | + attrs.push("pure"); | |
| 172 | + } | |
| 173 | + if func.is_elemental { | |
| 174 | + attrs.push("elemental"); | |
| 175 | + } | |
| 176 | + let attr_str = if attrs.is_empty() { | |
| 177 | + String::new() | |
| 178 | + } else { | |
| 179 | + format!(" [{}]", attrs.join(", ")) | |
| 180 | + }; | |
| 149 | 181 | write!(out, " func @{}{}(", func.name, attr_str).unwrap(); |
| 150 | 182 | for (i, p) in func.params.iter().enumerate() { |
| 151 | - if i > 0 { write!(out, ", ").unwrap(); } | |
| 183 | + if i > 0 { | |
| 184 | + write!(out, ", ").unwrap(); | |
| 185 | + } | |
| 152 | 186 | write!(out, "%{}: {}", p.id.0, p.ty).unwrap(); |
| 153 | 187 | } |
| 154 | 188 | writeln!(out, ") -> {} {{", func.return_type).unwrap(); |
@@ -210,7 +244,12 @@ fn print_inst_with_module_opt(inst: &Inst, module: Option<&Module>) -> String { | ||
| 210 | 244 | InstKind::FloatToInt(v, w) => format!("float_to_int %{} : {}", v.0, w), |
| 211 | 245 | InstKind::FloatExtend(v, w) => format!("float_extend %{} : {}", v.0, w), |
| 212 | 246 | InstKind::FloatTrunc(v, w) => format!("float_trunc %{} : {}", v.0, w), |
| 213 | - InstKind::IntExtend(v, w, s) => format!("int_extend %{} : {} {}", v.0, w, if *s { "signed" } else { "unsigned" }), | |
| 247 | + InstKind::IntExtend(v, w, s) => format!( | |
| 248 | + "int_extend %{} : {} {}", | |
| 249 | + v.0, | |
| 250 | + w, | |
| 251 | + if *s { "signed" } else { "unsigned" } | |
| 252 | + ), | |
| 214 | 253 | InstKind::IntTrunc(v, w) => format!("int_trunc %{} : {}", v.0, w), |
| 215 | 254 | InstKind::PtrToInt(v) => format!("ptr_to_int %{}", v.0), |
| 216 | 255 | InstKind::IntToPtr(v, ty) => format!("int_to_ptr %{} : {}", v.0, ty), |
@@ -238,11 +277,17 @@ fn print_inst_with_module_opt(inst: &Inst, module: Option<&Module>) -> String { | ||
| 238 | 277 | } |
| 239 | 278 | InstKind::RuntimeCall(rf, args) => { |
| 240 | 279 | let args_str: Vec<String> = args.iter().map(|a| format!("%{}", a.0)).collect(); |
| 241 | - format!("rt_call @{}({})", runtime_func_name(rf), args_str.join(", ")) | |
| 280 | + format!( | |
| 281 | + "rt_call @{}({})", | |
| 282 | + runtime_func_name(rf), | |
| 283 | + args_str.join(", ") | |
| 284 | + ) | |
| 242 | 285 | } |
| 243 | 286 | |
| 244 | 287 | InstKind::ExtractField(agg, idx) => format!("extract_field %{}, {}", agg.0, idx), |
| 245 | - InstKind::InsertField(agg, idx, val) => format!("insert_field %{}, {}, %{}", agg.0, idx, val.0), | |
| 288 | + InstKind::InsertField(agg, idx, val) => { | |
| 289 | + format!("insert_field %{}, {}, %{}", agg.0, idx, val.0) | |
| 290 | + } | |
| 246 | 291 | }; |
| 247 | 292 | |
| 248 | 293 | if inst.ty == super::types::IrType::Void { |
@@ -266,17 +311,39 @@ pub fn print_terminator_with_names(term: &Terminator, func: &Function) -> String | ||
| 266 | 311 | format!("br {}({})", bname(dest), args_str.join(", ")) |
| 267 | 312 | } |
| 268 | 313 | } |
| 269 | - Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args } => { | |
| 314 | + Terminator::CondBranch { | |
| 315 | + cond, | |
| 316 | + true_dest, | |
| 317 | + true_args, | |
| 318 | + false_dest, | |
| 319 | + false_args, | |
| 320 | + } => { | |
| 270 | 321 | let ta: Vec<String> = true_args.iter().map(|a| format!("%{}", a.0)).collect(); |
| 271 | 322 | let fa: Vec<String> = false_args.iter().map(|a| format!("%{}", a.0)).collect(); |
| 272 | - format!("cond_br %{}, {}({}), {}({})", | |
| 273 | - cond.0, bname(true_dest), ta.join(", "), bname(false_dest), fa.join(", ")) | |
| 323 | + format!( | |
| 324 | + "cond_br %{}, {}({}), {}({})", | |
| 325 | + cond.0, | |
| 326 | + bname(true_dest), | |
| 327 | + ta.join(", "), | |
| 328 | + bname(false_dest), | |
| 329 | + fa.join(", ") | |
| 330 | + ) | |
| 274 | 331 | } |
| 275 | - Terminator::Switch { selector, cases, default } => { | |
| 276 | - let cases_str: Vec<String> = cases.iter() | |
| 332 | + Terminator::Switch { | |
| 333 | + selector, | |
| 334 | + cases, | |
| 335 | + default, | |
| 336 | + } => { | |
| 337 | + let cases_str: Vec<String> = cases | |
| 338 | + .iter() | |
| 277 | 339 | .map(|(v, b)| format!("{} -> {}", v, bname(b))) |
| 278 | 340 | .collect(); |
| 279 | - format!("switch %{}, [{}], default {}", selector.0, cases_str.join(", "), bname(default)) | |
| 341 | + format!( | |
| 342 | + "switch %{}, [{}], default {}", | |
| 343 | + selector.0, | |
| 344 | + cases_str.join(", "), | |
| 345 | + bname(default) | |
| 346 | + ) | |
| 280 | 347 | } |
| 281 | 348 | Terminator::Unreachable => "unreachable".into(), |
| 282 | 349 | } |
@@ -295,17 +362,39 @@ pub fn print_terminator(term: &Terminator) -> String { | ||
| 295 | 362 | format!("br bb{}({})", dest.0, args_str.join(", ")) |
| 296 | 363 | } |
| 297 | 364 | } |
| 298 | - Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args } => { | |
| 365 | + Terminator::CondBranch { | |
| 366 | + cond, | |
| 367 | + true_dest, | |
| 368 | + true_args, | |
| 369 | + false_dest, | |
| 370 | + false_args, | |
| 371 | + } => { | |
| 299 | 372 | let ta: Vec<String> = true_args.iter().map(|a| format!("%{}", a.0)).collect(); |
| 300 | 373 | let fa: Vec<String> = false_args.iter().map(|a| format!("%{}", a.0)).collect(); |
| 301 | - format!("cond_br %{}, bb{}({}), bb{}({})", | |
| 302 | - cond.0, true_dest.0, ta.join(", "), false_dest.0, fa.join(", ")) | |
| 374 | + format!( | |
| 375 | + "cond_br %{}, bb{}({}), bb{}({})", | |
| 376 | + cond.0, | |
| 377 | + true_dest.0, | |
| 378 | + ta.join(", "), | |
| 379 | + false_dest.0, | |
| 380 | + fa.join(", ") | |
| 381 | + ) | |
| 303 | 382 | } |
| 304 | - Terminator::Switch { selector, cases, default } => { | |
| 305 | - let cases_str: Vec<String> = cases.iter() | |
| 383 | + Terminator::Switch { | |
| 384 | + selector, | |
| 385 | + cases, | |
| 386 | + default, | |
| 387 | + } => { | |
| 388 | + let cases_str: Vec<String> = cases | |
| 389 | + .iter() | |
| 306 | 390 | .map(|(v, b)| format!("{} -> bb{}", v, b.0)) |
| 307 | 391 | .collect(); |
| 308 | - format!("switch %{}, [{}], default bb{}", selector.0, cases_str.join(", "), default.0) | |
| 392 | + format!( | |
| 393 | + "switch %{}, [{}], default bb{}", | |
| 394 | + selector.0, | |
| 395 | + cases_str.join(", "), | |
| 396 | + default.0 | |
| 397 | + ) | |
| 309 | 398 | } |
| 310 | 399 | Terminator::Unreachable => "unreachable".into(), |
| 311 | 400 | } |
@@ -342,9 +431,9 @@ fn runtime_func_name(rf: &RuntimeFunc) -> &'static str { | ||
| 342 | 431 | |
| 343 | 432 | #[cfg(test)] |
| 344 | 433 | mod tests { |
| 345 | - use super::*; | |
| 346 | - use super::super::types::*; | |
| 347 | 434 | use super::super::builder::FuncBuilder; |
| 435 | + use super::super::types::*; | |
| 436 | + use super::*; | |
| 348 | 437 | |
| 349 | 438 | #[test] |
| 350 | 439 | fn print_simple_function() { |
@@ -422,7 +511,11 @@ mod tests { | ||
| 422 | 511 | let output = print_function(&func); |
| 423 | 512 | assert!(output.contains("header_1(%")); |
| 424 | 513 | assert!(output.contains(": i32)")); |
| 425 | - assert!(output.contains("br header_1("), "expected 'br header_1(' in:\n{}", output); | |
| 514 | + assert!( | |
| 515 | + output.contains("br header_1("), | |
| 516 | + "expected 'br header_1(' in:\n{}", | |
| 517 | + output | |
| 518 | + ); | |
| 426 | 519 | } |
| 427 | 520 | |
| 428 | 521 | #[test] |
@@ -443,13 +536,25 @@ mod tests { | ||
| 443 | 536 | { |
| 444 | 537 | let mut b = FuncBuilder::new(&mut caller); |
| 445 | 538 | let arg = b.const_i32(7); |
| 446 | - let _ = b.call(FuncRef::Internal(callee_idx), vec![arg], IrType::Int(IntWidth::I32)); | |
| 539 | + let _ = b.call( | |
| 540 | + FuncRef::Internal(callee_idx), | |
| 541 | + vec![arg], | |
| 542 | + IrType::Int(IntWidth::I32), | |
| 543 | + ); | |
| 447 | 544 | b.ret_void(); |
| 448 | 545 | } |
| 449 | 546 | module.add_function(caller); |
| 450 | 547 | |
| 451 | 548 | let output = print_module(&module); |
| 452 | - assert!(output.contains("call @callee("), "expected named internal call in:\n{}", output); | |
| 453 | - assert!(!output.contains("@func_0"), "unexpected fallback name in:\n{}", output); | |
| 549 | + assert!( | |
| 550 | + output.contains("call @callee("), | |
| 551 | + "expected named internal call in:\n{}", | |
| 552 | + output | |
| 553 | + ); | |
| 554 | + assert!( | |
| 555 | + !output.contains("@func_0"), | |
| 556 | + "unexpected fallback name in:\n{}", | |
| 557 | + output | |
| 558 | + ); | |
| 454 | 559 | } |
| 455 | 560 | } |
src/ir/types.rsmodified@@ -55,7 +55,10 @@ pub enum FloatWidth { | ||
| 55 | 55 | |
| 56 | 56 | impl FloatWidth { |
| 57 | 57 | pub fn bits(self) -> u32 { |
| 58 | - match self { Self::F32 => 32, Self::F64 => 64 } | |
| 58 | + match self { | |
| 59 | + Self::F32 => 32, | |
| 60 | + Self::F64 => 64, | |
| 61 | + } | |
| 59 | 62 | } |
| 60 | 63 | |
| 61 | 64 | pub fn bytes(self) -> u32 { |
@@ -90,7 +93,9 @@ impl IrType { | ||
| 90 | 93 | Self::Float(w) => w.bytes() as u64, |
| 91 | 94 | Self::Ptr(_) => 8, // 64-bit pointers |
| 92 | 95 | Self::Array(elem, count) => elem.size_bytes() * count, |
| 93 | - Self::Struct(_) => panic!("Struct size requires struct_defs; use Module::struct_size()"), | |
| 96 | + Self::Struct(_) => { | |
| 97 | + panic!("Struct size requires struct_defs; use Module::struct_size()") | |
| 98 | + } | |
| 94 | 99 | Self::FuncPtr(_) => 8, |
| 95 | 100 | } |
| 96 | 101 | } |
@@ -102,17 +107,27 @@ impl IrType { | ||
| 102 | 107 | |
| 103 | 108 | /// Extract the IntWidth if this is an integer type. |
| 104 | 109 | pub fn int_width(&self) -> Option<IntWidth> { |
| 105 | - if let Self::Int(w) = self { Some(*w) } else { None } | |
| 110 | + if let Self::Int(w) = self { | |
| 111 | + Some(*w) | |
| 112 | + } else { | |
| 113 | + None | |
| 114 | + } | |
| 106 | 115 | } |
| 107 | 116 | |
| 108 | 117 | /// Is this an integer type? |
| 109 | - pub fn is_int(&self) -> bool { matches!(self, Self::Int(_)) } | |
| 118 | + pub fn is_int(&self) -> bool { | |
| 119 | + matches!(self, Self::Int(_)) | |
| 120 | + } | |
| 110 | 121 | |
| 111 | 122 | /// Is this a float type? |
| 112 | - pub fn is_float(&self) -> bool { matches!(self, Self::Float(_)) } | |
| 123 | + pub fn is_float(&self) -> bool { | |
| 124 | + matches!(self, Self::Float(_)) | |
| 125 | + } | |
| 113 | 126 | |
| 114 | 127 | /// Is this a pointer type? |
| 115 | - pub fn is_ptr(&self) -> bool { matches!(self, Self::Ptr(_)) } | |
| 128 | + pub fn is_ptr(&self) -> bool { | |
| 129 | + matches!(self, Self::Ptr(_)) | |
| 130 | + } | |
| 116 | 131 | |
| 117 | 132 | /// Fortran integer(1) → i8, integer(2) → i16, integer(4) → i32, |
| 118 | 133 | /// integer(8) → i64, integer(16) → i128. |
@@ -149,7 +164,9 @@ impl std::fmt::Display for IrType { | ||
| 149 | 164 | Self::FuncPtr(sig) => { |
| 150 | 165 | write!(f, "fn(")?; |
| 151 | 166 | for (i, p) in sig.params.iter().enumerate() { |
| 152 | - if i > 0 { write!(f, ", ")?; } | |
| 167 | + if i > 0 { | |
| 168 | + write!(f, ", ")?; | |
| 169 | + } | |
| 153 | 170 | write!(f, "{}", p)?; |
| 154 | 171 | } |
| 155 | 172 | write!(f, ") -> {}", sig.ret) |
src/ir/verify.rsmodified@@ -4,10 +4,10 @@ | ||
| 4 | 4 | //! Checks: SSA dominance, type consistency, block structure, |
| 5 | 5 | //! terminator completeness, block param/branch arg matching. |
| 6 | 6 | |
| 7 | -use std::collections::{HashMap, HashSet}; | |
| 8 | 7 | use super::inst::*; |
| 9 | -use super::types::{IrType, IntWidth}; | |
| 10 | -use super::walk::{inst_uses, terminator_uses, terminator_targets, compute_dominators}; | |
| 8 | +use super::types::{IntWidth, IrType}; | |
| 9 | +use super::walk::{compute_dominators, inst_uses, terminator_targets, terminator_uses}; | |
| 10 | +use std::collections::{HashMap, HashSet}; | |
| 11 | 11 | |
| 12 | 12 | /// Verification error. |
| 13 | 13 | #[derive(Debug, Clone)] |
@@ -59,7 +59,9 @@ pub fn verify_function(func: &Function) -> Vec<VerifyError> { | ||
| 59 | 59 | }); |
| 60 | 60 | } |
| 61 | 61 | for block in &func.blocks { |
| 62 | - let Some(term) = &block.terminator else { continue; }; | |
| 62 | + let Some(term) = &block.terminator else { | |
| 63 | + continue; | |
| 64 | + }; | |
| 63 | 65 | if terminator_targets(term).contains(&func.entry) { |
| 64 | 66 | errors.push(VerifyError { |
| 65 | 67 | msg: format!( |
@@ -78,8 +80,10 @@ pub fn verify_function(func: &Function) -> Vec<VerifyError> { | ||
| 78 | 80 | for used in inst_uses(&inst.kind) { |
| 79 | 81 | if !defined.contains(&used) { |
| 80 | 82 | errors.push(VerifyError { |
| 81 | - msg: format!("value %{} used in block '{}' but not defined", | |
| 82 | - used.0, block.name), | |
| 83 | + msg: format!( | |
| 84 | + "value %{} used in block '{}' but not defined", | |
| 85 | + used.0, block.name | |
| 86 | + ), | |
| 83 | 87 | }); |
| 84 | 88 | } |
| 85 | 89 | } |
@@ -88,8 +92,10 @@ pub fn verify_function(func: &Function) -> Vec<VerifyError> { | ||
| 88 | 92 | for used in terminator_uses(term) { |
| 89 | 93 | if !defined.contains(&used) { |
| 90 | 94 | errors.push(VerifyError { |
| 91 | - msg: format!("value %{} used in terminator of block '{}' but not defined", | |
| 92 | - used.0, block.name), | |
| 95 | + msg: format!( | |
| 96 | + "value %{} used in terminator of block '{}' but not defined", | |
| 97 | + used.0, block.name | |
| 98 | + ), | |
| 93 | 99 | }); |
| 94 | 100 | } |
| 95 | 101 | } |
@@ -164,8 +170,10 @@ pub fn verify_function(func: &Function) -> Vec<VerifyError> { | ||
| 164 | 170 | for target in terminator_targets(term) { |
| 165 | 171 | if !block_ids.contains(&target) { |
| 166 | 172 | errors.push(VerifyError { |
| 167 | - msg: format!("block '{}' branches to undefined block {}", | |
| 168 | - block.name, target.0), | |
| 173 | + msg: format!( | |
| 174 | + "block '{}' branches to undefined block {}", | |
| 175 | + block.name, target.0 | |
| 176 | + ), | |
| 169 | 177 | }); |
| 170 | 178 | } |
| 171 | 179 | } |
@@ -276,14 +284,22 @@ fn collect_defined_values(func: &Function) -> HashSet<ValueId> { | ||
| 276 | 284 | } |
| 277 | 285 | |
| 278 | 286 | /// Check that branch arguments match block parameters in count and type. |
| 279 | -fn check_branch_args(func: &Function, term: &Terminator, from_block: &str, errors: &mut Vec<VerifyError>) { | |
| 287 | +fn check_branch_args( | |
| 288 | + func: &Function, | |
| 289 | + term: &Terminator, | |
| 290 | + from_block: &str, | |
| 291 | + errors: &mut Vec<VerifyError>, | |
| 292 | +) { | |
| 280 | 293 | let mut check = |dest: BlockId, args: &[ValueId]| { |
| 281 | 294 | let target = func.block(dest); |
| 282 | 295 | if target.params.len() != args.len() { |
| 283 | 296 | errors.push(VerifyError { |
| 284 | 297 | msg: format!( |
| 285 | 298 | "branch from '{}' to '{}': expected {} args, got {}", |
| 286 | - from_block, target.name, target.params.len(), args.len() | |
| 299 | + from_block, | |
| 300 | + target.name, | |
| 301 | + target.params.len(), | |
| 302 | + args.len() | |
| 287 | 303 | ), |
| 288 | 304 | }); |
| 289 | 305 | } else { |
@@ -305,7 +321,13 @@ fn check_branch_args(func: &Function, term: &Terminator, from_block: &str, error | ||
| 305 | 321 | |
| 306 | 322 | match term { |
| 307 | 323 | Terminator::Branch(dest, args) => check(*dest, args), |
| 308 | - Terminator::CondBranch { true_dest, true_args, false_dest, false_args, .. } => { | |
| 324 | + Terminator::CondBranch { | |
| 325 | + true_dest, | |
| 326 | + true_args, | |
| 327 | + false_dest, | |
| 328 | + false_args, | |
| 329 | + .. | |
| 330 | + } => { | |
| 309 | 331 | check(*true_dest, true_args); |
| 310 | 332 | check(*false_dest, false_args); |
| 311 | 333 | } |
@@ -314,7 +336,10 @@ fn check_branch_args(func: &Function, term: &Terminator, from_block: &str, error | ||
| 314 | 336 | let default_block = func.block(*default); |
| 315 | 337 | if !default_block.params.is_empty() { |
| 316 | 338 | errors.push(VerifyError { |
| 317 | - msg: format!("switch default target '{}' has block parameters", default_block.name), | |
| 339 | + msg: format!( | |
| 340 | + "switch default target '{}' has block parameters", | |
| 341 | + default_block.name | |
| 342 | + ), | |
| 318 | 343 | }); |
| 319 | 344 | } |
| 320 | 345 | for (_, dest) in cases { |
@@ -333,9 +358,11 @@ fn check_branch_args(func: &Function, term: &Terminator, from_block: &str, error | ||
| 333 | 358 | /// Check type consistency for instructions. |
| 334 | 359 | fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyError>) { |
| 335 | 360 | match &inst.kind { |
| 336 | - InstKind::IAdd(a, b) | InstKind::ISub(a, b) | | |
| 337 | - InstKind::IMul(a, b) | InstKind::IDiv(a, b) | | |
| 338 | - InstKind::IMod(a, b) => { | |
| 361 | + InstKind::IAdd(a, b) | |
| 362 | + | InstKind::ISub(a, b) | |
| 363 | + | InstKind::IMul(a, b) | |
| 364 | + | InstKind::IDiv(a, b) | |
| 365 | + | InstKind::IMod(a, b) => { | |
| 339 | 366 | let ta = func.value_type(*a); |
| 340 | 367 | let tb = func.value_type(*b); |
| 341 | 368 | // Report missing types so the upstream cache-miss (already |
@@ -354,12 +381,18 @@ fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyE | ||
| 354 | 381 | if let (Some(ta), Some(tb)) = (&ta, &tb) { |
| 355 | 382 | if !ta.is_int() { |
| 356 | 383 | errors.push(VerifyError { |
| 357 | - msg: format!("integer op %{} has non-integer operand %{} : {}", inst.id.0, a.0, ta), | |
| 384 | + msg: format!( | |
| 385 | + "integer op %{} has non-integer operand %{} : {}", | |
| 386 | + inst.id.0, a.0, ta | |
| 387 | + ), | |
| 358 | 388 | }); |
| 359 | 389 | } |
| 360 | 390 | if !tb.is_int() { |
| 361 | 391 | errors.push(VerifyError { |
| 362 | - msg: format!("integer op %{} has non-integer operand %{} : {}", inst.id.0, b.0, tb), | |
| 392 | + msg: format!( | |
| 393 | + "integer op %{} has non-integer operand %{} : {}", | |
| 394 | + inst.id.0, b.0, tb | |
| 395 | + ), | |
| 363 | 396 | }); |
| 364 | 397 | } |
| 365 | 398 | // Audit MAJOR-4: enforce exact width agreement. |
@@ -380,20 +413,28 @@ fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyE | ||
| 380 | 413 | } |
| 381 | 414 | } |
| 382 | 415 | } |
| 383 | - InstKind::FAdd(a, b) | InstKind::FSub(a, b) | | |
| 384 | - InstKind::FMul(a, b) | InstKind::FDiv(a, b) | | |
| 385 | - InstKind::FPow(a, b) => { | |
| 416 | + InstKind::FAdd(a, b) | |
| 417 | + | InstKind::FSub(a, b) | |
| 418 | + | InstKind::FMul(a, b) | |
| 419 | + | InstKind::FDiv(a, b) | |
| 420 | + | InstKind::FPow(a, b) => { | |
| 386 | 421 | let ta = func.value_type(*a); |
| 387 | 422 | let tb = func.value_type(*b); |
| 388 | 423 | if let (Some(ta), Some(tb)) = (&ta, &tb) { |
| 389 | 424 | if !ta.is_float() { |
| 390 | 425 | errors.push(VerifyError { |
| 391 | - msg: format!("float op %{} has non-float operand %{} : {}", inst.id.0, a.0, ta), | |
| 426 | + msg: format!( | |
| 427 | + "float op %{} has non-float operand %{} : {}", | |
| 428 | + inst.id.0, a.0, ta | |
| 429 | + ), | |
| 392 | 430 | }); |
| 393 | 431 | } |
| 394 | 432 | if !tb.is_float() { |
| 395 | 433 | errors.push(VerifyError { |
| 396 | - msg: format!("float op %{} has non-float operand %{} : {}", inst.id.0, b.0, tb), | |
| 434 | + msg: format!( | |
| 435 | + "float op %{} has non-float operand %{} : {}", | |
| 436 | + inst.id.0, b.0, tb | |
| 437 | + ), | |
| 397 | 438 | }); |
| 398 | 439 | } |
| 399 | 440 | // Same width-agreement rule for floats. Mixing |
@@ -423,9 +464,7 @@ fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyE | ||
| 423 | 464 | // A Store(i64_val, ptr<i32>) would silently truncate |
| 424 | 465 | // at codegen because isel picks the str width from |
| 425 | 466 | // the value's reg class, not the pointer's pointee. |
| 426 | - if let (Some(IrType::Ptr(pointee)), Some(vty)) = | |
| 427 | - (&addr_ty, func.value_type(*val)) | |
| 428 | - { | |
| 467 | + if let (Some(IrType::Ptr(pointee)), Some(vty)) = (&addr_ty, func.value_type(*val)) { | |
| 429 | 468 | let inner: &IrType = pointee.as_ref(); |
| 430 | 469 | // Byte-level GEPs into derived-type layouts use |
| 431 | 470 | // `ptr<i8>` as a marker with arbitrary pointee on |
@@ -457,8 +496,12 @@ fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyE | ||
| 457 | 496 | |
| 458 | 497 | // Bitwise binary ops: both operands must be integers of |
| 459 | 498 | // the same width. Audit Med-1. |
| 460 | - InstKind::BitAnd(a, b) | InstKind::BitOr(a, b) | InstKind::BitXor(a, b) | |
| 461 | - | InstKind::Shl(a, b) | InstKind::LShr(a, b) | InstKind::AShr(a, b) => { | |
| 499 | + InstKind::BitAnd(a, b) | |
| 500 | + | InstKind::BitOr(a, b) | |
| 501 | + | InstKind::BitXor(a, b) | |
| 502 | + | InstKind::Shl(a, b) | |
| 503 | + | InstKind::LShr(a, b) | |
| 504 | + | InstKind::AShr(a, b) => { | |
| 462 | 505 | let ta = func.value_type(*a); |
| 463 | 506 | let tb = func.value_type(*b); |
| 464 | 507 | if let (Some(ta), Some(tb)) = (&ta, &tb) { |
@@ -503,9 +546,9 @@ fn check_type_consistency(func: &Function, inst: &Inst, errors: &mut Vec<VerifyE | ||
| 503 | 546 | |
| 504 | 547 | #[cfg(test)] |
| 505 | 548 | mod tests { |
| 506 | - use super::*; | |
| 507 | - use super::super::types::*; | |
| 508 | 549 | use super::super::builder::FuncBuilder; |
| 550 | + use super::super::types::*; | |
| 551 | + use super::*; | |
| 509 | 552 | |
| 510 | 553 | #[test] |
| 511 | 554 | fn valid_simple_function() { |
@@ -551,7 +594,8 @@ mod tests { | ||
| 551 | 594 | let mut func = Function::new("test".into(), vec![], IrType::Void); |
| 552 | 595 | // Manually add a param to the entry block. |
| 553 | 596 | func.blocks[0].params.push(BlockParam { |
| 554 | - id: ValueId(99), ty: IrType::Int(IntWidth::I32), | |
| 597 | + id: ValueId(99), | |
| 598 | + ty: IrType::Int(IntWidth::I32), | |
| 555 | 599 | }); |
| 556 | 600 | func.blocks[0].terminator = Some(Terminator::Return(None)); |
| 557 | 601 | let errs = verify_function(&func); |
@@ -568,7 +612,8 @@ mod tests { | ||
| 568 | 612 | let errs = verify_function(&func); |
| 569 | 613 | assert!( |
| 570 | 614 | errs.iter().any(|e| e.msg.contains("entry block")), |
| 571 | - "expected entry-back-edge error, got: {:?}", errs, | |
| 615 | + "expected entry-back-edge error, got: {:?}", | |
| 616 | + errs, | |
| 572 | 617 | ); |
| 573 | 618 | } |
| 574 | 619 | |
@@ -586,7 +631,9 @@ mod tests { | ||
| 586 | 631 | b.ret_void(); |
| 587 | 632 | } |
| 588 | 633 | let errs = verify_function(&func); |
| 589 | - assert!(errs.iter().any(|e| e.msg.contains("expected 1 args, got 0"))); | |
| 634 | + assert!(errs | |
| 635 | + .iter() | |
| 636 | + .any(|e| e.msg.contains("expected 1 args, got 0"))); | |
| 590 | 637 | } |
| 591 | 638 | |
| 592 | 639 | #[test] |
@@ -619,7 +666,8 @@ mod tests { | ||
| 619 | 666 | } |
| 620 | 667 | let errs = verify_function(&func); |
| 621 | 668 | assert!( |
| 622 | - errs.iter().any(|e| e.msg.contains("doesn't match pointee type")), | |
| 669 | + errs.iter() | |
| 670 | + .any(|e| e.msg.contains("doesn't match pointee type")), | |
| 623 | 671 | "expected pointee type mismatch error, got: {:?}", |
| 624 | 672 | errs, |
| 625 | 673 | ); |
@@ -734,7 +782,7 @@ mod tests { | ||
| 734 | 782 | let mut b = FuncBuilder::new(&mut func); |
| 735 | 783 | let val = b.const_i32(42); |
| 736 | 784 | let not_ptr = b.const_i32(0); // not a pointer |
| 737 | - // Force a store to non-pointer. | |
| 785 | + // Force a store to non-pointer. | |
| 738 | 786 | b.emit_bogus_store(val, not_ptr); |
| 739 | 787 | b.ret_void(); |
| 740 | 788 | } |
@@ -767,25 +815,39 @@ mod tests { | ||
| 767 | 815 | b.ret_void(); |
| 768 | 816 | } |
| 769 | 817 | // Manually inject the value definition in block B with the ID we referenced. |
| 770 | - use crate::lexer::{Span, Position}; | |
| 771 | - let span = Span { file_id: 0, start: Position { line: 0, col: 0 }, end: Position { line: 0, col: 0 } }; | |
| 772 | - func.blocks[2].insts.insert(0, Inst { | |
| 773 | - id: ValueId(100), | |
| 774 | - kind: InstKind::ConstInt(99, IntWidth::I32), | |
| 775 | - ty: IrType::Int(IntWidth::I32), | |
| 776 | - span, | |
| 777 | - }); | |
| 818 | + use crate::lexer::{Position, Span}; | |
| 819 | + let span = Span { | |
| 820 | + file_id: 0, | |
| 821 | + start: Position { line: 0, col: 0 }, | |
| 822 | + end: Position { line: 0, col: 0 }, | |
| 823 | + }; | |
| 824 | + func.blocks[2].insts.insert( | |
| 825 | + 0, | |
| 826 | + Inst { | |
| 827 | + id: ValueId(100), | |
| 828 | + kind: InstKind::ConstInt(99, IntWidth::I32), | |
| 829 | + ty: IrType::Int(IntWidth::I32), | |
| 830 | + span, | |
| 831 | + }, | |
| 832 | + ); | |
| 778 | 833 | let errs = verify_function(&func); |
| 779 | - assert!(errs.iter().any(|e| e.msg.contains("does not dominate")), | |
| 780 | - "expected dominance error, got: {:?}", errs); | |
| 834 | + assert!( | |
| 835 | + errs.iter().any(|e| e.msg.contains("does not dominate")), | |
| 836 | + "expected dominance error, got: {:?}", | |
| 837 | + errs | |
| 838 | + ); | |
| 781 | 839 | } |
| 782 | 840 | |
| 783 | 841 | #[test] |
| 784 | 842 | fn dominance_same_block_order_violation() { |
| 785 | 843 | // Use a value before it's defined in the same block. |
| 786 | 844 | let mut func = Function::new("test".into(), vec![], IrType::Void); |
| 787 | - use crate::lexer::{Span, Position}; | |
| 788 | - let span = Span { file_id: 0, start: Position { line: 0, col: 0 }, end: Position { line: 0, col: 0 } }; | |
| 845 | + use crate::lexer::{Position, Span}; | |
| 846 | + let span = Span { | |
| 847 | + file_id: 0, | |
| 848 | + start: Position { line: 0, col: 0 }, | |
| 849 | + end: Position { line: 0, col: 0 }, | |
| 850 | + }; | |
| 789 | 851 | |
| 790 | 852 | // Manually construct: %1 = iadd %0, %0 then %0 = const_int 42 |
| 791 | 853 | // (use of %0 before its definition) |
@@ -803,8 +865,12 @@ mod tests { | ||
| 803 | 865 | }); |
| 804 | 866 | func.blocks[0].terminator = Some(Terminator::Return(None)); |
| 805 | 867 | let errs = verify_function(&func); |
| 806 | - assert!(errs.iter().any(|e| e.msg.contains("used before its definition")), | |
| 807 | - "expected same-block order error, got: {:?}", errs); | |
| 868 | + assert!( | |
| 869 | + errs.iter() | |
| 870 | + .any(|e| e.msg.contains("used before its definition")), | |
| 871 | + "expected same-block order error, got: {:?}", | |
| 872 | + errs | |
| 873 | + ); | |
| 808 | 874 | } |
| 809 | 875 | |
| 810 | 876 | #[test] |
@@ -837,13 +903,23 @@ mod tests { | ||
| 837 | 903 | fn duplicate_value_id_errors() { |
| 838 | 904 | let mut func = Function::new("test".into(), vec![], IrType::Void); |
| 839 | 905 | // Manually push two instructions with the same ID. |
| 840 | - use crate::lexer::{Span, Position}; | |
| 841 | - let span = Span { file_id: 0, start: Position { line: 0, col: 0 }, end: Position { line: 0, col: 0 } }; | |
| 906 | + use crate::lexer::{Position, Span}; | |
| 907 | + let span = Span { | |
| 908 | + file_id: 0, | |
| 909 | + start: Position { line: 0, col: 0 }, | |
| 910 | + end: Position { line: 0, col: 0 }, | |
| 911 | + }; | |
| 842 | 912 | func.blocks[0].insts.push(Inst { |
| 843 | - id: ValueId(0), kind: InstKind::ConstInt(1, IntWidth::I32), ty: IrType::Int(IntWidth::I32), span, | |
| 913 | + id: ValueId(0), | |
| 914 | + kind: InstKind::ConstInt(1, IntWidth::I32), | |
| 915 | + ty: IrType::Int(IntWidth::I32), | |
| 916 | + span, | |
| 844 | 917 | }); |
| 845 | 918 | func.blocks[0].insts.push(Inst { |
| 846 | - id: ValueId(0), kind: InstKind::ConstInt(2, IntWidth::I32), ty: IrType::Int(IntWidth::I32), span, | |
| 919 | + id: ValueId(0), | |
| 920 | + kind: InstKind::ConstInt(2, IntWidth::I32), | |
| 921 | + ty: IrType::Int(IntWidth::I32), | |
| 922 | + span, | |
| 847 | 923 | }); |
| 848 | 924 | func.blocks[0].terminator = Some(Terminator::Return(None)); |
| 849 | 925 | let errs = verify_function(&func); |
src/ir/walk.rsmodified@@ -23,19 +23,26 @@ use std::collections::{HashMap, HashSet, VecDeque}; | ||
| 23 | 23 | /// All `ValueId`s consumed as operands by an instruction. |
| 24 | 24 | pub fn inst_uses(kind: &InstKind) -> Vec<ValueId> { |
| 25 | 25 | match kind { |
| 26 | - InstKind::ConstInt(..) | InstKind::ConstFloat(..) | | |
| 27 | - InstKind::ConstBool(..) | InstKind::ConstString(..) | | |
| 28 | - InstKind::Undef(..) | InstKind::Alloca(..) | | |
| 29 | - InstKind::GlobalAddr(..) => vec![], | |
| 30 | - | |
| 31 | - InstKind::IAdd(a, b) | InstKind::ISub(a, b) | | |
| 32 | - InstKind::IMul(a, b) | InstKind::IDiv(a, b) | | |
| 33 | - InstKind::IMod(a, b) => vec![*a, *b], | |
| 26 | + InstKind::ConstInt(..) | |
| 27 | + | InstKind::ConstFloat(..) | |
| 28 | + | InstKind::ConstBool(..) | |
| 29 | + | InstKind::ConstString(..) | |
| 30 | + | InstKind::Undef(..) | |
| 31 | + | InstKind::Alloca(..) | |
| 32 | + | InstKind::GlobalAddr(..) => vec![], | |
| 33 | + | |
| 34 | + InstKind::IAdd(a, b) | |
| 35 | + | InstKind::ISub(a, b) | |
| 36 | + | InstKind::IMul(a, b) | |
| 37 | + | InstKind::IDiv(a, b) | |
| 38 | + | InstKind::IMod(a, b) => vec![*a, *b], | |
| 34 | 39 | InstKind::INeg(a) => vec![*a], |
| 35 | 40 | |
| 36 | - InstKind::FAdd(a, b) | InstKind::FSub(a, b) | | |
| 37 | - InstKind::FMul(a, b) | InstKind::FDiv(a, b) | | |
| 38 | - InstKind::FPow(a, b) => vec![*a, *b], | |
| 41 | + InstKind::FAdd(a, b) | |
| 42 | + | InstKind::FSub(a, b) | |
| 43 | + | InstKind::FMul(a, b) | |
| 44 | + | InstKind::FDiv(a, b) | |
| 45 | + | InstKind::FPow(a, b) => vec![*a, *b], | |
| 39 | 46 | InstKind::FNeg(a) | InstKind::FAbs(a) | InstKind::FSqrt(a) => vec![*a], |
| 40 | 47 | |
| 41 | 48 | InstKind::ICmp(_, a, b) | InstKind::FCmp(_, a, b) => vec![*a, *b], |
@@ -45,16 +52,25 @@ pub fn inst_uses(kind: &InstKind) -> Vec<ValueId> { | ||
| 45 | 52 | |
| 46 | 53 | InstKind::Select(c, t, f) => vec![*c, *t, *f], |
| 47 | 54 | |
| 48 | - InstKind::BitAnd(a, b) | InstKind::BitOr(a, b) | | |
| 49 | - InstKind::BitXor(a, b) | InstKind::Shl(a, b) | | |
| 50 | - InstKind::LShr(a, b) | InstKind::AShr(a, b) => vec![*a, *b], | |
| 51 | - InstKind::BitNot(a) | InstKind::CountLeadingZeros(a) | | |
| 52 | - InstKind::CountTrailingZeros(a) | InstKind::PopCount(a) => vec![*a], | |
| 53 | - | |
| 54 | - InstKind::IntToFloat(v, _) | InstKind::FloatToInt(v, _) | | |
| 55 | - InstKind::FloatExtend(v, _) | InstKind::FloatTrunc(v, _) | | |
| 56 | - InstKind::IntExtend(v, _, _) | InstKind::IntTrunc(v, _) | | |
| 57 | - InstKind::PtrToInt(v) | InstKind::IntToPtr(v, _) => vec![*v], | |
| 55 | + InstKind::BitAnd(a, b) | |
| 56 | + | InstKind::BitOr(a, b) | |
| 57 | + | InstKind::BitXor(a, b) | |
| 58 | + | InstKind::Shl(a, b) | |
| 59 | + | InstKind::LShr(a, b) | |
| 60 | + | InstKind::AShr(a, b) => vec![*a, *b], | |
| 61 | + InstKind::BitNot(a) | |
| 62 | + | InstKind::CountLeadingZeros(a) | |
| 63 | + | InstKind::CountTrailingZeros(a) | |
| 64 | + | InstKind::PopCount(a) => vec![*a], | |
| 65 | + | |
| 66 | + InstKind::IntToFloat(v, _) | |
| 67 | + | InstKind::FloatToInt(v, _) | |
| 68 | + | InstKind::FloatExtend(v, _) | |
| 69 | + | InstKind::FloatTrunc(v, _) | |
| 70 | + | InstKind::IntExtend(v, _, _) | |
| 71 | + | InstKind::IntTrunc(v, _) | |
| 72 | + | InstKind::PtrToInt(v) | |
| 73 | + | InstKind::IntToPtr(v, _) => vec![*v], | |
| 58 | 74 | |
| 59 | 75 | InstKind::Load(a) => vec![*a], |
| 60 | 76 | InstKind::Store(v, a) => vec![*v, *a], |
@@ -82,7 +98,12 @@ pub fn terminator_uses(term: &Terminator) -> Vec<ValueId> { | ||
| 82 | 98 | Terminator::Return(None) | Terminator::Unreachable => vec![], |
| 83 | 99 | Terminator::Return(Some(v)) => vec![*v], |
| 84 | 100 | Terminator::Branch(_, args) => args.clone(), |
| 85 | - Terminator::CondBranch { cond, true_args, false_args, .. } => { | |
| 101 | + Terminator::CondBranch { | |
| 102 | + cond, | |
| 103 | + true_args, | |
| 104 | + false_args, | |
| 105 | + .. | |
| 106 | + } => { | |
| 86 | 107 | let mut uses = vec![*cond]; |
| 87 | 108 | uses.extend(true_args); |
| 88 | 109 | uses.extend(false_args); |
@@ -97,7 +118,11 @@ pub fn terminator_targets(term: &Terminator) -> Vec<BlockId> { | ||
| 97 | 118 | match term { |
| 98 | 119 | Terminator::Return(_) | Terminator::Unreachable => vec![], |
| 99 | 120 | Terminator::Branch(d, _) => vec![*d], |
| 100 | - Terminator::CondBranch { true_dest, false_dest, .. } => vec![*true_dest, *false_dest], | |
| 121 | + Terminator::CondBranch { | |
| 122 | + true_dest, | |
| 123 | + false_dest, | |
| 124 | + .. | |
| 125 | + } => vec![*true_dest, *false_dest], | |
| 101 | 126 | Terminator::Switch { cases, default, .. } => { |
| 102 | 127 | let mut t: Vec<BlockId> = cases.iter().map(|(_, b)| *b).collect(); |
| 103 | 128 | t.push(*default); |
@@ -113,72 +138,129 @@ pub fn terminator_targets(term: &Terminator) -> Vec<BlockId> { | ||
| 113 | 138 | /// Apply a closure to every operand slot of an instruction in place. |
| 114 | 139 | pub fn for_each_operand_mut(kind: &mut InstKind, mut r: impl FnMut(&mut ValueId)) { |
| 115 | 140 | match kind { |
| 116 | - InstKind::ConstInt(..) | InstKind::ConstFloat(..) | | |
| 117 | - InstKind::ConstBool(..) | InstKind::ConstString(..) | | |
| 118 | - InstKind::Undef(..) | InstKind::Alloca(..) | | |
| 119 | - InstKind::GlobalAddr(..) => {} | |
| 120 | - | |
| 121 | - InstKind::IAdd(a, b) | InstKind::ISub(a, b) | | |
| 122 | - InstKind::IMul(a, b) | InstKind::IDiv(a, b) | | |
| 123 | - InstKind::IMod(a, b) => { r(a); r(b); } | |
| 141 | + InstKind::ConstInt(..) | |
| 142 | + | InstKind::ConstFloat(..) | |
| 143 | + | InstKind::ConstBool(..) | |
| 144 | + | InstKind::ConstString(..) | |
| 145 | + | InstKind::Undef(..) | |
| 146 | + | InstKind::Alloca(..) | |
| 147 | + | InstKind::GlobalAddr(..) => {} | |
| 148 | + | |
| 149 | + InstKind::IAdd(a, b) | |
| 150 | + | InstKind::ISub(a, b) | |
| 151 | + | InstKind::IMul(a, b) | |
| 152 | + | InstKind::IDiv(a, b) | |
| 153 | + | InstKind::IMod(a, b) => { | |
| 154 | + r(a); | |
| 155 | + r(b); | |
| 156 | + } | |
| 124 | 157 | InstKind::INeg(a) => r(a), |
| 125 | 158 | |
| 126 | - InstKind::FAdd(a, b) | InstKind::FSub(a, b) | | |
| 127 | - InstKind::FMul(a, b) | InstKind::FDiv(a, b) | | |
| 128 | - InstKind::FPow(a, b) => { r(a); r(b); } | |
| 159 | + InstKind::FAdd(a, b) | |
| 160 | + | InstKind::FSub(a, b) | |
| 161 | + | InstKind::FMul(a, b) | |
| 162 | + | InstKind::FDiv(a, b) | |
| 163 | + | InstKind::FPow(a, b) => { | |
| 164 | + r(a); | |
| 165 | + r(b); | |
| 166 | + } | |
| 129 | 167 | InstKind::FNeg(a) | InstKind::FAbs(a) | InstKind::FSqrt(a) => r(a), |
| 130 | 168 | |
| 131 | - InstKind::ICmp(_, a, b) | InstKind::FCmp(_, a, b) => { r(a); r(b); } | |
| 169 | + InstKind::ICmp(_, a, b) | InstKind::FCmp(_, a, b) => { | |
| 170 | + r(a); | |
| 171 | + r(b); | |
| 172 | + } | |
| 132 | 173 | |
| 133 | - InstKind::And(a, b) | InstKind::Or(a, b) => { r(a); r(b); } | |
| 174 | + InstKind::And(a, b) | InstKind::Or(a, b) => { | |
| 175 | + r(a); | |
| 176 | + r(b); | |
| 177 | + } | |
| 134 | 178 | InstKind::Not(a) => r(a), |
| 135 | 179 | |
| 136 | - InstKind::Select(c, t, f) => { r(c); r(t); r(f); } | |
| 137 | - | |
| 138 | - InstKind::BitAnd(a, b) | InstKind::BitOr(a, b) | | |
| 139 | - InstKind::BitXor(a, b) | InstKind::Shl(a, b) | | |
| 140 | - InstKind::LShr(a, b) | InstKind::AShr(a, b) => { r(a); r(b); } | |
| 141 | - InstKind::BitNot(a) | InstKind::CountLeadingZeros(a) | | |
| 142 | - InstKind::CountTrailingZeros(a) | InstKind::PopCount(a) => r(a), | |
| 180 | + InstKind::Select(c, t, f) => { | |
| 181 | + r(c); | |
| 182 | + r(t); | |
| 183 | + r(f); | |
| 184 | + } | |
| 143 | 185 | |
| 144 | - InstKind::IntToFloat(v, _) | InstKind::FloatToInt(v, _) | | |
| 145 | - InstKind::FloatExtend(v, _) | InstKind::FloatTrunc(v, _) | | |
| 146 | - InstKind::IntExtend(v, _, _) | InstKind::IntTrunc(v, _) | | |
| 147 | - InstKind::PtrToInt(v) | InstKind::IntToPtr(v, _) => r(v), | |
| 186 | + InstKind::BitAnd(a, b) | |
| 187 | + | InstKind::BitOr(a, b) | |
| 188 | + | InstKind::BitXor(a, b) | |
| 189 | + | InstKind::Shl(a, b) | |
| 190 | + | InstKind::LShr(a, b) | |
| 191 | + | InstKind::AShr(a, b) => { | |
| 192 | + r(a); | |
| 193 | + r(b); | |
| 194 | + } | |
| 195 | + InstKind::BitNot(a) | |
| 196 | + | InstKind::CountLeadingZeros(a) | |
| 197 | + | InstKind::CountTrailingZeros(a) | |
| 198 | + | InstKind::PopCount(a) => r(a), | |
| 199 | + | |
| 200 | + InstKind::IntToFloat(v, _) | |
| 201 | + | InstKind::FloatToInt(v, _) | |
| 202 | + | InstKind::FloatExtend(v, _) | |
| 203 | + | InstKind::FloatTrunc(v, _) | |
| 204 | + | InstKind::IntExtend(v, _, _) | |
| 205 | + | InstKind::IntTrunc(v, _) | |
| 206 | + | InstKind::PtrToInt(v) | |
| 207 | + | InstKind::IntToPtr(v, _) => r(v), | |
| 148 | 208 | |
| 149 | 209 | InstKind::Load(a) => r(a), |
| 150 | - InstKind::Store(v, a) => { r(v); r(a); } | |
| 210 | + InstKind::Store(v, a) => { | |
| 211 | + r(v); | |
| 212 | + r(a); | |
| 213 | + } | |
| 151 | 214 | InstKind::GetElementPtr(base, idxs) => { |
| 152 | 215 | r(base); |
| 153 | - for i in idxs { r(i); } | |
| 216 | + for i in idxs { | |
| 217 | + r(i); | |
| 218 | + } | |
| 154 | 219 | } |
| 155 | 220 | |
| 156 | 221 | InstKind::Call(FuncRef::Indirect(target), args) => { |
| 157 | 222 | r(target); |
| 158 | - for a in args { r(a); } | |
| 223 | + for a in args { | |
| 224 | + r(a); | |
| 225 | + } | |
| 159 | 226 | } |
| 160 | 227 | InstKind::Call(_, args) | InstKind::RuntimeCall(_, args) => { |
| 161 | - for a in args { r(a); } | |
| 228 | + for a in args { | |
| 229 | + r(a); | |
| 230 | + } | |
| 162 | 231 | } |
| 163 | 232 | |
| 164 | 233 | InstKind::ExtractField(agg, _) => r(agg), |
| 165 | - InstKind::InsertField(agg, _, val) => { r(agg); r(val); } | |
| 234 | + InstKind::InsertField(agg, _, val) => { | |
| 235 | + r(agg); | |
| 236 | + r(val); | |
| 237 | + } | |
| 166 | 238 | } |
| 167 | 239 | } |
| 168 | 240 | |
| 169 | 241 | /// Apply a closure to every operand slot of a terminator in place. |
| 170 | -pub fn for_each_terminator_operand_mut( | |
| 171 | - term: &mut Terminator, | |
| 172 | - mut r: impl FnMut(&mut ValueId), | |
| 173 | -) { | |
| 242 | +pub fn for_each_terminator_operand_mut(term: &mut Terminator, mut r: impl FnMut(&mut ValueId)) { | |
| 174 | 243 | match term { |
| 175 | 244 | Terminator::Return(None) | Terminator::Unreachable => {} |
| 176 | 245 | Terminator::Return(Some(v)) => r(v), |
| 177 | - Terminator::Branch(_, args) => for a in args { r(a); }, | |
| 178 | - Terminator::CondBranch { cond, true_args, false_args, .. } => { | |
| 246 | + Terminator::Branch(_, args) => { | |
| 247 | + for a in args { | |
| 248 | + r(a); | |
| 249 | + } | |
| 250 | + } | |
| 251 | + Terminator::CondBranch { | |
| 252 | + cond, | |
| 253 | + true_args, | |
| 254 | + false_args, | |
| 255 | + .. | |
| 256 | + } => { | |
| 179 | 257 | r(cond); |
| 180 | - for a in true_args { r(a); } | |
| 181 | - for a in false_args { r(a); } | |
| 258 | + for a in true_args { | |
| 259 | + r(a); | |
| 260 | + } | |
| 261 | + for a in false_args { | |
| 262 | + r(a); | |
| 263 | + } | |
| 182 | 264 | } |
| 183 | 265 | Terminator::Switch { selector, .. } => r(selector), |
| 184 | 266 | } |
@@ -188,7 +270,11 @@ pub fn for_each_terminator_operand_mut( | ||
| 188 | 270 | /// Definitions are unaffected — only operand slots in instructions and |
| 189 | 271 | /// terminators are rewritten. |
| 190 | 272 | pub fn substitute_uses(func: &mut Function, old: ValueId, new: ValueId) { |
| 191 | - let r = |v: &mut ValueId| if *v == old { *v = new; }; | |
| 273 | + let r = |v: &mut ValueId| { | |
| 274 | + if *v == old { | |
| 275 | + *v = new; | |
| 276 | + } | |
| 277 | + }; | |
| 192 | 278 | for block in &mut func.blocks { |
| 193 | 279 | for inst in &mut block.insts { |
| 194 | 280 | for_each_operand_mut(&mut inst.kind, r); |
@@ -267,7 +353,9 @@ pub fn compute_dominators(func: &Function) -> HashMap<BlockId, HashSet<BlockId>> | ||
| 267 | 353 | // update them — they participate in no meaningful dominance |
| 268 | 354 | // relationship. |
| 269 | 355 | for block in &func.blocks { |
| 270 | - if block.id == func.entry { continue; } | |
| 356 | + if block.id == func.entry { | |
| 357 | + continue; | |
| 358 | + } | |
| 271 | 359 | if reachable.contains(&block.id) { |
| 272 | 360 | doms.insert(block.id, reachable.clone()); |
| 273 | 361 | } else { |
@@ -280,15 +368,22 @@ pub fn compute_dominators(func: &Function) -> HashMap<BlockId, HashSet<BlockId>> | ||
| 280 | 368 | while changed { |
| 281 | 369 | changed = false; |
| 282 | 370 | for block in &func.blocks { |
| 283 | - if block.id == func.entry { continue; } | |
| 284 | - if !reachable.contains(&block.id) { continue; } | |
| 371 | + if block.id == func.entry { | |
| 372 | + continue; | |
| 373 | + } | |
| 374 | + if !reachable.contains(&block.id) { | |
| 375 | + continue; | |
| 376 | + } | |
| 285 | 377 | let plist = preds.get(&block.id).cloned().unwrap_or_default(); |
| 286 | 378 | // Reachable-only predecessors — an edge from an |
| 287 | 379 | // unreachable block doesn't contribute to dominance. |
| 288 | - let reachable_preds: Vec<BlockId> = plist.into_iter() | |
| 380 | + let reachable_preds: Vec<BlockId> = plist | |
| 381 | + .into_iter() | |
| 289 | 382 | .filter(|p| reachable.contains(p)) |
| 290 | 383 | .collect(); |
| 291 | - if reachable_preds.is_empty() { continue; } | |
| 384 | + if reachable_preds.is_empty() { | |
| 385 | + continue; | |
| 386 | + } | |
| 292 | 387 | let mut new_dom = reachable.clone(); |
| 293 | 388 | for p in &reachable_preds { |
| 294 | 389 | if let Some(pd) = doms.get(p) { |
@@ -381,21 +476,24 @@ pub fn find_natural_loops(func: &Function) -> Vec<NaturalLoop> { | ||
| 381 | 476 | // everything reachable from the entry, which both inflates |
| 382 | 477 | // the body and makes `find_preheader` see no out-of-loop |
| 383 | 478 | // predecessor for the header. |
| 384 | - let mut stack: Vec<BlockId> = latches.iter() | |
| 385 | - .filter(|&&l| l != header) | |
| 386 | - .copied() | |
| 387 | - .collect(); | |
| 479 | + let mut stack: Vec<BlockId> = latches.iter().filter(|&&l| l != header).copied().collect(); | |
| 388 | 480 | while let Some(b) = stack.pop() { |
| 389 | 481 | if let Some(plist) = preds.get(&b) { |
| 390 | 482 | for &p in plist { |
| 391 | - if p == header { continue; } | |
| 483 | + if p == header { | |
| 484 | + continue; | |
| 485 | + } | |
| 392 | 486 | if body.insert(p) { |
| 393 | 487 | stack.push(p); |
| 394 | 488 | } |
| 395 | 489 | } |
| 396 | 490 | } |
| 397 | 491 | } |
| 398 | - loops.push(NaturalLoop { header, body, latches }); | |
| 492 | + loops.push(NaturalLoop { | |
| 493 | + header, | |
| 494 | + body, | |
| 495 | + latches, | |
| 496 | + }); | |
| 399 | 497 | } |
| 400 | 498 | loops |
| 401 | 499 | } |
@@ -414,7 +512,9 @@ pub fn prune_unreachable(func: &mut Function) -> bool { | ||
| 414 | 512 | queue.push_back(func.entry); |
| 415 | 513 | reachable.insert(func.entry); |
| 416 | 514 | while let Some(bid) = queue.pop_front() { |
| 417 | - let Some(block) = func.try_block(bid) else { continue }; | |
| 515 | + let Some(block) = func.try_block(bid) else { | |
| 516 | + continue; | |
| 517 | + }; | |
| 418 | 518 | if let Some(term) = &block.terminator { |
| 419 | 519 | for tgt in terminator_targets(term) { |
| 420 | 520 | if reachable.insert(tgt) { |
@@ -458,28 +558,31 @@ pub fn compute_immediate_dominators(func: &Function) -> HashMap<BlockId, BlockId | ||
| 458 | 558 | let mut idoms: HashMap<BlockId, BlockId> = HashMap::new(); |
| 459 | 559 | |
| 460 | 560 | for block in &func.blocks { |
| 461 | - if block.id == func.entry { continue; } | |
| 462 | - let Some(my_doms) = doms.get(&block.id) else { continue }; | |
| 463 | - if my_doms.is_empty() { continue; } // unreachable | |
| 561 | + if block.id == func.entry { | |
| 562 | + continue; | |
| 563 | + } | |
| 564 | + let Some(my_doms) = doms.get(&block.id) else { | |
| 565 | + continue; | |
| 566 | + }; | |
| 567 | + if my_doms.is_empty() { | |
| 568 | + continue; | |
| 569 | + } // unreachable | |
| 464 | 570 | |
| 465 | 571 | // The immediate dominator is the dominator (other than |
| 466 | 572 | // self) that is dominated by every other dominator (other |
| 467 | 573 | // than self). Equivalently: the dominator that has the |
| 468 | 574 | // largest dominator set — all other dominators of `block` |
| 469 | 575 | // also dominate the idom. |
| 470 | - let candidates: Vec<BlockId> = my_doms.iter() | |
| 471 | - .copied() | |
| 472 | - .filter(|&d| d != block.id) | |
| 473 | - .collect(); | |
| 576 | + let candidates: Vec<BlockId> = my_doms.iter().copied().filter(|&d| d != block.id).collect(); | |
| 474 | 577 | |
| 475 | 578 | let idom = candidates.iter().copied().find(|&cand| { |
| 476 | 579 | // cand is idom iff no other candidate strictly |
| 477 | 580 | // dominates it (only cand itself and cand's own |
| 478 | 581 | // dominators do). |
| 479 | 582 | let cand_doms = doms.get(&cand).cloned().unwrap_or_default(); |
| 480 | - candidates.iter().all(|&other| { | |
| 481 | - other == cand || cand_doms.contains(&other) | |
| 482 | - }) | |
| 583 | + candidates | |
| 584 | + .iter() | |
| 585 | + .all(|&other| other == cand || cand_doms.contains(&other)) | |
| 483 | 586 | }); |
| 484 | 587 | |
| 485 | 588 | if let Some(idom) = idom { |
@@ -496,9 +599,9 @@ pub fn compute_immediate_dominators(func: &Function) -> HashMap<BlockId, BlockId | ||
| 496 | 599 | /// `children[B]` is the set of blocks whose immediate dominator is |
| 497 | 600 | /// `B`. A dominator-tree traversal visits `B` then recurses into |
| 498 | 601 | /// `children[B]` in some order. |
| 499 | -pub fn dominator_tree_children(idoms: &HashMap<BlockId, BlockId>) | |
| 500 | - -> HashMap<BlockId, Vec<BlockId>> | |
| 501 | -{ | |
| 602 | +pub fn dominator_tree_children( | |
| 603 | + idoms: &HashMap<BlockId, BlockId>, | |
| 604 | +) -> HashMap<BlockId, Vec<BlockId>> { | |
| 502 | 605 | let mut children: HashMap<BlockId, Vec<BlockId>> = HashMap::new(); |
| 503 | 606 | for (&child, &parent) in idoms { |
| 504 | 607 | children.entry(parent).or_default().push(child); |
@@ -522,9 +625,7 @@ pub fn dominator_tree_children(idoms: &HashMap<BlockId, BlockId>) | ||
| 522 | 625 | /// for every join point `X` (block with ≥ 2 predecessors), walk |
| 523 | 626 | /// each predecessor `P` upward in the dominator tree, adding `X` |
| 524 | 627 | /// to `DF(runner)` until `runner == idom(X)`. |
| 525 | -pub fn compute_dominance_frontiers(func: &Function) | |
| 526 | - -> HashMap<BlockId, HashSet<BlockId>> | |
| 527 | -{ | |
| 628 | +pub fn compute_dominance_frontiers(func: &Function) -> HashMap<BlockId, HashSet<BlockId>> { | |
| 528 | 629 | let idoms = compute_immediate_dominators(func); |
| 529 | 630 | let preds = predecessors(func); |
| 530 | 631 | |
@@ -536,7 +637,9 @@ pub fn compute_dominance_frontiers(func: &Function) | ||
| 536 | 637 | // unreachable predecessors that downstream consumers (mem2reg's |
| 537 | 638 | // iterated-DF closure in particular) must then defensively |
| 538 | 639 | // ignore. Filtering at the source keeps the DF map clean. |
| 539 | - let reachable: HashSet<BlockId> = idoms.keys().copied() | |
| 640 | + let reachable: HashSet<BlockId> = idoms | |
| 641 | + .keys() | |
| 642 | + .copied() | |
| 540 | 643 | .chain(std::iter::once(func.entry)) |
| 541 | 644 | .collect(); |
| 542 | 645 | |
@@ -552,15 +655,20 @@ pub fn compute_dominance_frontiers(func: &Function) | ||
| 552 | 655 | |
| 553 | 656 | for block in &func.blocks { |
| 554 | 657 | let x = block.id; |
| 555 | - if !reachable.contains(&x) { continue; } | |
| 658 | + if !reachable.contains(&x) { | |
| 659 | + continue; | |
| 660 | + } | |
| 556 | 661 | let plist = preds.get(&x).cloned().unwrap_or_default(); |
| 557 | 662 | // Drop unreachable predecessors before counting toward |
| 558 | 663 | // "join point" status. An unreachable predecessor adds no |
| 559 | 664 | // runtime control flow into x. |
| 560 | - let plist: Vec<BlockId> = plist.into_iter() | |
| 665 | + let plist: Vec<BlockId> = plist | |
| 666 | + .into_iter() | |
| 561 | 667 | .filter(|p| reachable.contains(p)) |
| 562 | 668 | .collect(); |
| 563 | - if plist.len() < 2 { continue; } | |
| 669 | + if plist.len() < 2 { | |
| 670 | + continue; | |
| 671 | + } | |
| 564 | 672 | |
| 565 | 673 | // `x` is a join point. Walk each predecessor upward. |
| 566 | 674 | let idom_x = idoms.get(&x).copied(); |
@@ -609,13 +717,17 @@ pub fn dominator_tree_preorder(func: &Function) -> Vec<BlockId> { | ||
| 609 | 717 | |
| 610 | 718 | #[cfg(test)] |
| 611 | 719 | mod walk_tests { |
| 612 | - use super::*; | |
| 613 | 720 | use super::super::types::IrType; |
| 614 | - use crate::lexer::{Span, Position}; | |
| 721 | + use super::*; | |
| 722 | + use crate::lexer::{Position, Span}; | |
| 615 | 723 | |
| 616 | 724 | fn dummy_span() -> Span { |
| 617 | 725 | let p = Position { line: 1, col: 1 }; |
| 618 | - Span { start: p, end: p, file_id: 0 } | |
| 726 | + Span { | |
| 727 | + start: p, | |
| 728 | + end: p, | |
| 729 | + file_id: 0, | |
| 730 | + } | |
| 619 | 731 | } |
| 620 | 732 | |
| 621 | 733 | /// Build a diamond CFG: |
@@ -760,21 +872,31 @@ mod walk_tests { | ||
| 760 | 872 | let entry = f.entry; |
| 761 | 873 | let c0 = f.next_value_id(); |
| 762 | 874 | f.block_mut(entry).insts.push(Inst { |
| 763 | - id: c0, kind: InstKind::ConstBool(true), | |
| 764 | - ty: IrType::Bool, span: dummy_span(), | |
| 875 | + id: c0, | |
| 876 | + kind: InstKind::ConstBool(true), | |
| 877 | + ty: IrType::Bool, | |
| 878 | + span: dummy_span(), | |
| 765 | 879 | }); |
| 766 | 880 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 767 | - cond: c0, true_dest: a, true_args: vec![], | |
| 768 | - false_dest: b, false_args: vec![], | |
| 881 | + cond: c0, | |
| 882 | + true_dest: a, | |
| 883 | + true_args: vec![], | |
| 884 | + false_dest: b, | |
| 885 | + false_args: vec![], | |
| 769 | 886 | }); |
| 770 | 887 | let c1 = f.next_value_id(); |
| 771 | 888 | f.block_mut(b).insts.push(Inst { |
| 772 | - id: c1, kind: InstKind::ConstBool(true), | |
| 773 | - ty: IrType::Bool, span: dummy_span(), | |
| 889 | + id: c1, | |
| 890 | + kind: InstKind::ConstBool(true), | |
| 891 | + ty: IrType::Bool, | |
| 892 | + span: dummy_span(), | |
| 774 | 893 | }); |
| 775 | 894 | f.block_mut(b).terminator = Some(Terminator::CondBranch { |
| 776 | - cond: c1, true_dest: c, true_args: vec![], | |
| 777 | - false_dest: d, false_args: vec![], | |
| 895 | + cond: c1, | |
| 896 | + true_dest: c, | |
| 897 | + true_args: vec![], | |
| 898 | + false_dest: d, | |
| 899 | + false_args: vec![], | |
| 778 | 900 | }); |
| 779 | 901 | f.block_mut(c).terminator = Some(Terminator::Branch(m1, vec![])); |
| 780 | 902 | f.block_mut(d).terminator = Some(Terminator::Branch(m1, vec![])); |
@@ -815,7 +937,10 @@ mod walk_tests { | ||
| 815 | 937 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 816 | 938 | |
| 817 | 939 | let idoms = compute_immediate_dominators(&f); |
| 818 | - assert!(idoms.is_empty(), "single-block function should have no idoms"); | |
| 940 | + assert!( | |
| 941 | + idoms.is_empty(), | |
| 942 | + "single-block function should have no idoms" | |
| 943 | + ); | |
| 819 | 944 | |
| 820 | 945 | let df = compute_dominance_frontiers(&f); |
| 821 | 946 | // Entry is reachable, so it has an entry in the DF map, but |
@@ -847,13 +972,18 @@ mod walk_tests { | ||
| 847 | 972 | let exit = f.create_block("exit"); |
| 848 | 973 | f.block_mut(f.entry).terminator = Some(Terminator::CondBranch { |
| 849 | 974 | cond, |
| 850 | - true_dest: f.entry, true_args: vec![], | |
| 851 | - false_dest: exit, false_args: vec![], | |
| 975 | + true_dest: f.entry, | |
| 976 | + true_args: vec![], | |
| 977 | + false_dest: exit, | |
| 978 | + false_args: vec![], | |
| 852 | 979 | }); |
| 853 | 980 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 854 | 981 | |
| 855 | 982 | let idoms = compute_immediate_dominators(&f); |
| 856 | - assert!(!idoms.contains_key(&f.entry), "entry has no idom even with a self-edge"); | |
| 983 | + assert!( | |
| 984 | + !idoms.contains_key(&f.entry), | |
| 985 | + "entry has no idom even with a self-edge" | |
| 986 | + ); | |
| 857 | 987 | assert_eq!(idoms[&exit], f.entry); |
| 858 | 988 | |
| 859 | 989 | let df = compute_dominance_frontiers(&f); |
@@ -861,8 +991,11 @@ mod walk_tests { | ||
| 861 | 991 | // the implicit "function entry edge" doesn't count, so entry |
| 862 | 992 | // has 1 reachable pred. Not a join point — entry ∉ any DF. |
| 863 | 993 | for (b, frontier) in &df { |
| 864 | - assert!(!frontier.contains(&f.entry), | |
| 865 | - "entry should not appear in DF[{:?}]", b); | |
| 994 | + assert!( | |
| 995 | + !frontier.contains(&f.entry), | |
| 996 | + "entry should not appear in DF[{:?}]", | |
| 997 | + b | |
| 998 | + ); | |
| 866 | 999 | } |
| 867 | 1000 | } |
| 868 | 1001 | |
@@ -889,14 +1022,19 @@ mod walk_tests { | ||
| 889 | 1022 | }); |
| 890 | 1023 | f.block_mut(b).terminator = Some(Terminator::CondBranch { |
| 891 | 1024 | cond, |
| 892 | - true_dest: b, true_args: vec![], | |
| 893 | - false_dest: exit, false_args: vec![], | |
| 1025 | + true_dest: b, | |
| 1026 | + true_args: vec![], | |
| 1027 | + false_dest: exit, | |
| 1028 | + false_args: vec![], | |
| 894 | 1029 | }); |
| 895 | 1030 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 896 | 1031 | |
| 897 | 1032 | let df = compute_dominance_frontiers(&f); |
| 898 | - assert_eq!(df[&b], HashSet::from([b]), | |
| 899 | - "b's self-edge puts b in its own dominance frontier"); | |
| 1033 | + assert_eq!( | |
| 1034 | + df[&b], | |
| 1035 | + HashSet::from([b]), | |
| 1036 | + "b's self-edge puts b in its own dominance frontier" | |
| 1037 | + ); | |
| 900 | 1038 | let idoms = compute_immediate_dominators(&f); |
| 901 | 1039 | assert_eq!(idoms[&b], f.entry); |
| 902 | 1040 | assert_eq!(idoms[&exit], b); |
@@ -921,13 +1059,17 @@ mod walk_tests { | ||
| 921 | 1059 | // entry → cond ? a : b |
| 922 | 1060 | let c0 = f.next_value_id(); |
| 923 | 1061 | f.block_mut(f.entry).insts.push(Inst { |
| 924 | - id: c0, kind: InstKind::ConstBool(true), | |
| 925 | - ty: IrType::Bool, span: dummy_span(), | |
| 1062 | + id: c0, | |
| 1063 | + kind: InstKind::ConstBool(true), | |
| 1064 | + ty: IrType::Bool, | |
| 1065 | + span: dummy_span(), | |
| 926 | 1066 | }); |
| 927 | 1067 | f.block_mut(f.entry).terminator = Some(Terminator::CondBranch { |
| 928 | 1068 | cond: c0, |
| 929 | - true_dest: a, true_args: vec![], | |
| 930 | - false_dest: b, false_args: vec![], | |
| 1069 | + true_dest: a, | |
| 1070 | + true_args: vec![], | |
| 1071 | + false_dest: b, | |
| 1072 | + false_args: vec![], | |
| 931 | 1073 | }); |
| 932 | 1074 | |
| 933 | 1075 | // a → b |
@@ -938,13 +1080,17 @@ mod walk_tests { | ||
| 938 | 1080 | // bypasses it). |
| 939 | 1081 | let c1 = f.next_value_id(); |
| 940 | 1082 | f.block_mut(b).insts.push(Inst { |
| 941 | - id: c1, kind: InstKind::ConstBool(true), | |
| 942 | - ty: IrType::Bool, span: dummy_span(), | |
| 1083 | + id: c1, | |
| 1084 | + kind: InstKind::ConstBool(true), | |
| 1085 | + ty: IrType::Bool, | |
| 1086 | + span: dummy_span(), | |
| 943 | 1087 | }); |
| 944 | 1088 | f.block_mut(b).terminator = Some(Terminator::CondBranch { |
| 945 | 1089 | cond: c1, |
| 946 | - true_dest: a, true_args: vec![], | |
| 947 | - false_dest: exit, false_args: vec![], | |
| 1090 | + true_dest: a, | |
| 1091 | + true_args: vec![], | |
| 1092 | + false_dest: exit, | |
| 1093 | + false_args: vec![], | |
| 948 | 1094 | }); |
| 949 | 1095 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 950 | 1096 | |
src/lexer/mod.rsmodified@@ -59,36 +59,36 @@ pub enum TokenKind { | ||
| 59 | 59 | Identifier, |
| 60 | 60 | |
| 61 | 61 | // ---- Operators ---- |
| 62 | - Plus, // + | |
| 63 | - Minus, // - | |
| 64 | - Star, // * | |
| 65 | - Slash, // / | |
| 66 | - Power, // ** | |
| 67 | - Concat, // // | |
| 68 | - Eq, // == | |
| 69 | - Ne, // /= | |
| 70 | - Lt, // < | |
| 71 | - Gt, // > | |
| 72 | - Le, // <= | |
| 73 | - Ge, // >= | |
| 62 | + Plus, // + | |
| 63 | + Minus, // - | |
| 64 | + Star, // * | |
| 65 | + Slash, // / | |
| 66 | + Power, // ** | |
| 67 | + Concat, // // | |
| 68 | + Eq, // == | |
| 69 | + Ne, // /= | |
| 70 | + Lt, // < | |
| 71 | + Gt, // > | |
| 72 | + Le, // <= | |
| 73 | + Ge, // >= | |
| 74 | 74 | /// .eq. .ne. .lt. .gt. .le. .ge. .and. .or. .not. .eqv. .neqv. |
| 75 | 75 | DotOp(String), |
| 76 | 76 | /// User-defined operator: .myop. |
| 77 | 77 | DefinedOp(String), |
| 78 | 78 | |
| 79 | 79 | // ---- Punctuation ---- |
| 80 | - LParen, // ( | |
| 81 | - RParen, // ) | |
| 82 | - LBracket, // [ | |
| 83 | - RBracket, // ] | |
| 84 | - Comma, // , | |
| 85 | - Colon, // : | |
| 86 | - ColonColon, // :: | |
| 87 | - Semicolon, // ; | |
| 88 | - Percent, // % | |
| 89 | - Arrow, // => | |
| 90 | - Assign, // = | |
| 91 | - Ampersand, // & (when not continuation) | |
| 80 | + LParen, // ( | |
| 81 | + RParen, // ) | |
| 82 | + LBracket, // [ | |
| 83 | + RBracket, // ] | |
| 84 | + Comma, // , | |
| 85 | + Colon, // : | |
| 86 | + ColonColon, // :: | |
| 87 | + Semicolon, // ; | |
| 88 | + Percent, // % | |
| 89 | + Arrow, // => | |
| 90 | + Assign, // = | |
| 91 | + Ampersand, // & (when not continuation) | |
| 92 | 92 | |
| 93 | 93 | // ---- Special ---- |
| 94 | 94 | Newline, |
@@ -144,34 +144,123 @@ impl fmt::Display for TokenKind { | ||
| 144 | 144 | /// The lexer provides `is_keyword()` as a helper. |
| 145 | 145 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 146 | 146 | pub enum Keyword { |
| 147 | - Program, EndProgram, Module, EndModule, Submodule, EndSubmodule, | |
| 148 | - Subroutine, EndSubroutine, Function, EndFunction, BlockData, EndBlockData, | |
| 149 | - Contains, Use, Only, Import, | |
| 150 | - Implicit, None, | |
| 151 | - Integer, Real, DoublePrecision, DoubleComplex, Complex, Character, Logical, Type, Class, | |
| 152 | - Dimension, Allocatable, Pointer, Target, Intent, In, Out, InOut, | |
| 153 | - Optional, Save, Parameter, Value, Volatile, Asynchronous, Protected, | |
| 154 | - Contiguous, External, Intrinsic, Bind, | |
| 155 | - Public, Private, | |
| 156 | - If, Then, Else, ElseIf, EndIf, | |
| 157 | - Do, EndDo, While, Concurrent, | |
| 158 | - Select, Case, EndSelect, Default, | |
| 159 | - Where, EndWhere, Elsewhere, | |
| 160 | - Forall, EndForall, | |
| 161 | - Block, EndBlock, | |
| 162 | - Associate, EndAssociate, | |
| 163 | - Critical, EndCritical, | |
| 147 | + Program, | |
| 148 | + EndProgram, | |
| 149 | + Module, | |
| 150 | + EndModule, | |
| 151 | + Submodule, | |
| 152 | + EndSubmodule, | |
| 153 | + Subroutine, | |
| 154 | + EndSubroutine, | |
| 155 | + Function, | |
| 156 | + EndFunction, | |
| 157 | + BlockData, | |
| 158 | + EndBlockData, | |
| 159 | + Contains, | |
| 160 | + Use, | |
| 161 | + Only, | |
| 162 | + Import, | |
| 163 | + Implicit, | |
| 164 | + None, | |
| 165 | + Integer, | |
| 166 | + Real, | |
| 167 | + DoublePrecision, | |
| 168 | + DoubleComplex, | |
| 169 | + Complex, | |
| 170 | + Character, | |
| 171 | + Logical, | |
| 172 | + Type, | |
| 173 | + Class, | |
| 174 | + Dimension, | |
| 175 | + Allocatable, | |
| 176 | + Pointer, | |
| 177 | + Target, | |
| 178 | + Intent, | |
| 179 | + In, | |
| 180 | + Out, | |
| 181 | + InOut, | |
| 182 | + Optional, | |
| 183 | + Save, | |
| 184 | + Parameter, | |
| 185 | + Value, | |
| 186 | + Volatile, | |
| 187 | + Asynchronous, | |
| 188 | + Protected, | |
| 189 | + Contiguous, | |
| 190 | + External, | |
| 191 | + Intrinsic, | |
| 192 | + Bind, | |
| 193 | + Public, | |
| 194 | + Private, | |
| 195 | + If, | |
| 196 | + Then, | |
| 197 | + Else, | |
| 198 | + ElseIf, | |
| 199 | + EndIf, | |
| 200 | + Do, | |
| 201 | + EndDo, | |
| 202 | + While, | |
| 203 | + Concurrent, | |
| 204 | + Select, | |
| 205 | + Case, | |
| 206 | + EndSelect, | |
| 207 | + Default, | |
| 208 | + Where, | |
| 209 | + EndWhere, | |
| 210 | + Elsewhere, | |
| 211 | + Forall, | |
| 212 | + EndForall, | |
| 213 | + Block, | |
| 214 | + EndBlock, | |
| 215 | + Associate, | |
| 216 | + EndAssociate, | |
| 217 | + Critical, | |
| 218 | + EndCritical, | |
| 164 | 219 | Continue, |
| 165 | - Exit, Cycle, Stop, ErrorStop, Return, GoTo, | |
| 166 | - Call, Print, Write, Read, Open, Close, Inquire, | |
| 167 | - Rewind, Backspace, Endfile, Flush, Wait, | |
| 168 | - Allocate, Deallocate, Nullify, | |
| 169 | - Data, Common, Equivalence, Namelist, Sequence, | |
| 220 | + Exit, | |
| 221 | + Cycle, | |
| 222 | + Stop, | |
| 223 | + ErrorStop, | |
| 224 | + Return, | |
| 225 | + GoTo, | |
| 226 | + Call, | |
| 227 | + Print, | |
| 228 | + Write, | |
| 229 | + Read, | |
| 230 | + Open, | |
| 231 | + Close, | |
| 232 | + Inquire, | |
| 233 | + Rewind, | |
| 234 | + Backspace, | |
| 235 | + Endfile, | |
| 236 | + Flush, | |
| 237 | + Wait, | |
| 238 | + Allocate, | |
| 239 | + Deallocate, | |
| 240 | + Nullify, | |
| 241 | + Data, | |
| 242 | + Common, | |
| 243 | + Equivalence, | |
| 244 | + Namelist, | |
| 245 | + Sequence, | |
| 170 | 246 | Format, |
| 171 | - Pure, Impure, Elemental, Recursive, NonRecursive, | |
| 172 | - Abstract, Interface, EndInterface, Procedure, Generic, Operator, Assignment, | |
| 173 | - Entry, Result, | |
| 174 | - Enum, Enumerator, EndEnum, | |
| 247 | + Pure, | |
| 248 | + Impure, | |
| 249 | + Elemental, | |
| 250 | + Recursive, | |
| 251 | + NonRecursive, | |
| 252 | + Abstract, | |
| 253 | + Interface, | |
| 254 | + EndInterface, | |
| 255 | + Procedure, | |
| 256 | + Generic, | |
| 257 | + Operator, | |
| 258 | + Assignment, | |
| 259 | + Entry, | |
| 260 | + Result, | |
| 261 | + Enum, | |
| 262 | + Enumerator, | |
| 263 | + EndEnum, | |
| 175 | 264 | End, |
| 176 | 265 | } |
| 177 | 266 | |
@@ -303,10 +392,21 @@ pub fn is_keyword(name: &str) -> Option<Keyword> { | ||
| 303 | 392 | // ---- Known dot-operators ---- |
| 304 | 393 | |
| 305 | 394 | fn is_known_dot_op(name: &str) -> bool { |
| 306 | - matches!(name.to_lowercase().as_str(), | |
| 307 | - "and" | "or" | "not" | "eqv" | "neqv" | | |
| 308 | - "eq" | "ne" | "lt" | "gt" | "le" | "ge" | | |
| 309 | - "true" | "false" | |
| 395 | + matches!( | |
| 396 | + name.to_lowercase().as_str(), | |
| 397 | + "and" | |
| 398 | + | "or" | |
| 399 | + | "not" | |
| 400 | + | "eqv" | |
| 401 | + | "neqv" | |
| 402 | + | "eq" | |
| 403 | + | "ne" | |
| 404 | + | "lt" | |
| 405 | + | "gt" | |
| 406 | + | "le" | |
| 407 | + | "ge" | |
| 408 | + | "true" | |
| 409 | + | "false" | |
| 310 | 410 | ) |
| 311 | 411 | } |
| 312 | 412 | |
@@ -320,7 +420,11 @@ pub struct LexError { | ||
| 320 | 420 | |
| 321 | 421 | impl fmt::Display for LexError { |
| 322 | 422 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 323 | - write!(f, "{}:{}: error: {}", self.span.start.line, self.span.start.col, self.msg) | |
| 423 | + write!( | |
| 424 | + f, | |
| 425 | + "{}:{}: error: {}", | |
| 426 | + self.span.start.line, self.span.start.col, self.msg | |
| 427 | + ) | |
| 324 | 428 | } |
| 325 | 429 | } |
| 326 | 430 | |
@@ -371,7 +475,13 @@ pub struct Lexer<'a> { | ||
| 371 | 475 | |
| 372 | 476 | impl<'a> Lexer<'a> { |
| 373 | 477 | pub fn new(src: &'a str, file_id: u32) -> Self { |
| 374 | - Self { src: src.as_bytes(), pos: 0, line: 1, col: 1, file_id } | |
| 478 | + Self { | |
| 479 | + src: src.as_bytes(), | |
| 480 | + pos: 0, | |
| 481 | + line: 1, | |
| 482 | + col: 1, | |
| 483 | + file_id, | |
| 484 | + } | |
| 375 | 485 | } |
| 376 | 486 | |
| 377 | 487 | /// Tokenize the entire source into a Vec. |
@@ -382,29 +492,49 @@ impl<'a> Lexer<'a> { | ||
| 382 | 492 | let tok = lexer.next_token()?; |
| 383 | 493 | let is_eof = tok.kind == TokenKind::Eof; |
| 384 | 494 | tokens.push(tok); |
| 385 | - if is_eof { break; } | |
| 495 | + if is_eof { | |
| 496 | + break; | |
| 497 | + } | |
| 386 | 498 | } |
| 387 | 499 | Ok(tokens) |
| 388 | 500 | } |
| 389 | 501 | |
| 390 | 502 | fn pos(&self) -> Position { |
| 391 | - Position { line: self.line, col: self.col } | |
| 503 | + Position { | |
| 504 | + line: self.line, | |
| 505 | + col: self.col, | |
| 506 | + } | |
| 392 | 507 | } |
| 393 | 508 | |
| 394 | 509 | fn span_from(&self, start: Position) -> Span { |
| 395 | - Span { file_id: self.file_id, start, end: self.pos() } | |
| 510 | + Span { | |
| 511 | + file_id: self.file_id, | |
| 512 | + start, | |
| 513 | + end: self.pos(), | |
| 514 | + } | |
| 396 | 515 | } |
| 397 | 516 | |
| 398 | 517 | fn err(&self, start: Position, msg: String) -> LexError { |
| 399 | - LexError { span: self.span_from(start), msg } | |
| 518 | + LexError { | |
| 519 | + span: self.span_from(start), | |
| 520 | + msg, | |
| 521 | + } | |
| 400 | 522 | } |
| 401 | 523 | |
| 402 | 524 | fn peek(&self) -> u8 { |
| 403 | - if self.pos < self.src.len() { self.src[self.pos] } else { 0 } | |
| 525 | + if self.pos < self.src.len() { | |
| 526 | + self.src[self.pos] | |
| 527 | + } else { | |
| 528 | + 0 | |
| 529 | + } | |
| 404 | 530 | } |
| 405 | 531 | |
| 406 | 532 | fn peek2(&self) -> u8 { |
| 407 | - if self.pos + 1 < self.src.len() { self.src[self.pos + 1] } else { 0 } | |
| 533 | + if self.pos + 1 < self.src.len() { | |
| 534 | + self.src[self.pos + 1] | |
| 535 | + } else { | |
| 536 | + 0 | |
| 537 | + } | |
| 408 | 538 | } |
| 409 | 539 | |
| 410 | 540 | fn advance(&mut self) -> u8 { |
@@ -432,7 +562,9 @@ impl<'a> Lexer<'a> { | ||
| 432 | 562 | /// Skip a continuation: & at end of line, optional comment, newline, optional leading &. |
| 433 | 563 | /// Returns true if a continuation was consumed. |
| 434 | 564 | fn try_continuation(&mut self) -> bool { |
| 435 | - if self.peek() != b'&' { return false; } | |
| 565 | + if self.peek() != b'&' { | |
| 566 | + return false; | |
| 567 | + } | |
| 436 | 568 | |
| 437 | 569 | // Save position in case this isn't a continuation. |
| 438 | 570 | let save_pos = self.pos; |
@@ -487,7 +619,9 @@ impl<'a> Lexer<'a> { | ||
| 487 | 619 | while !self.at_end() && self.peek() != b'\n' { |
| 488 | 620 | self.advance(); |
| 489 | 621 | } |
| 490 | - if !self.at_end() { self.advance(); } | |
| 622 | + if !self.at_end() { | |
| 623 | + self.advance(); | |
| 624 | + } | |
| 491 | 625 | continue; |
| 492 | 626 | } |
| 493 | 627 | break; |
@@ -600,17 +734,17 @@ impl<'a> Lexer<'a> { | ||
| 600 | 734 | let save_col = self.col; |
| 601 | 735 | |
| 602 | 736 | self.advance(); // & |
| 603 | - // String-continuation check. A lone `&` ending the | |
| 604 | - // current physical line (optionally with trailing | |
| 605 | - // whitespace and/or a `!comment`) continues the | |
| 606 | - // string on the next line. Anything else — notably a | |
| 607 | - // closing quote before the newline — means the `&` | |
| 608 | - // is a literal character of the string. | |
| 609 | - // | |
| 610 | - // Scan from the current pos without committing: find | |
| 611 | - // the earliest of quote, `!`, or newline. Only newline | |
| 612 | - // (possibly preceded by whitespace or a comment) keeps | |
| 613 | - // is_cont true. | |
| 737 | + // String-continuation check. A lone `&` ending the | |
| 738 | + // current physical line (optionally with trailing | |
| 739 | + // whitespace and/or a `!comment`) continues the | |
| 740 | + // string on the next line. Anything else — notably a | |
| 741 | + // closing quote before the newline — means the `&` | |
| 742 | + // is a literal character of the string. | |
| 743 | + // | |
| 744 | + // Scan from the current pos without committing: find | |
| 745 | + // the earliest of quote, `!`, or newline. Only newline | |
| 746 | + // (possibly preceded by whitespace or a comment) keeps | |
| 747 | + // is_cont true. | |
| 614 | 748 | let mut is_cont = false; |
| 615 | 749 | let mut scan = self.pos; |
| 616 | 750 | let src_bytes = self.src; |
@@ -669,7 +803,9 @@ impl<'a> Lexer<'a> { | ||
| 669 | 803 | if is_cont && !self.at_end() { |
| 670 | 804 | self.advance(); // newline |
| 671 | 805 | self.skip_spaces(); |
| 672 | - if self.peek() == b'&' { self.advance(); } | |
| 806 | + if self.peek() == b'&' { | |
| 807 | + self.advance(); | |
| 808 | + } | |
| 673 | 809 | continue; |
| 674 | 810 | } |
| 675 | 811 | // Not a continuation — restore and treat & as literal character. |
@@ -731,7 +867,11 @@ impl<'a> Lexer<'a> { | ||
| 731 | 867 | // Could be 1.0e5 (exponent) or 1.eq.2 (dot-operator). |
| 732 | 868 | // Lookahead past the e/d: if next is digit or +/-, it's an exponent. |
| 733 | 869 | // If it's another letter (like 'q' in 'eq'), it's a dot-operator. |
| 734 | - let after_ed = if self.pos + 2 < self.src.len() { self.src[self.pos + 2] } else { 0 }; | |
| 870 | + let after_ed = if self.pos + 2 < self.src.len() { | |
| 871 | + self.src[self.pos + 2] | |
| 872 | + } else { | |
| 873 | + 0 | |
| 874 | + }; | |
| 735 | 875 | matches!(after_ed, b'0'..=b'9' | b'+' | b'-') |
| 736 | 876 | } else if !next.is_ascii_alphabetic() { |
| 737 | 877 | // 5. followed by space/operator/newline/EOF — trailing dot real. |
@@ -781,7 +921,11 @@ impl<'a> Lexer<'a> { | ||
| 781 | 921 | } |
| 782 | 922 | |
| 783 | 923 | Ok(Token { |
| 784 | - kind: if is_real { TokenKind::RealLiteral } else { TokenKind::IntegerLiteral }, | |
| 924 | + kind: if is_real { | |
| 925 | + TokenKind::RealLiteral | |
| 926 | + } else { | |
| 927 | + TokenKind::IntegerLiteral | |
| 928 | + }, | |
| 785 | 929 | text, |
| 786 | 930 | span: self.span_from(start), |
| 787 | 931 | }) |
@@ -830,7 +974,11 @@ impl<'a> Lexer<'a> { | ||
| 830 | 974 | return Ok(Token { |
| 831 | 975 | kind: TokenKind::Identifier, |
| 832 | 976 | text: "..".into(), |
| 833 | - span: Span { start, end: self.pos(), file_id: self.file_id }, | |
| 977 | + span: Span { | |
| 978 | + start, | |
| 979 | + end: self.pos(), | |
| 980 | + file_id: self.file_id, | |
| 981 | + }, | |
| 834 | 982 | }); |
| 835 | 983 | } |
| 836 | 984 | return Err(self.err(start, "unexpected '.'".into())); |
@@ -904,24 +1052,48 @@ impl<'a> Lexer<'a> { | ||
| 904 | 1052 | let (kind, text) = match ch { |
| 905 | 1053 | b'+' => (TokenKind::Plus, "+"), |
| 906 | 1054 | b'-' => (TokenKind::Minus, "-"), |
| 907 | - b'*' if next == b'*' => { self.advance(); (TokenKind::Power, "**") } | |
| 1055 | + b'*' if next == b'*' => { | |
| 1056 | + self.advance(); | |
| 1057 | + (TokenKind::Power, "**") | |
| 1058 | + } | |
| 908 | 1059 | b'*' => (TokenKind::Star, "*"), |
| 909 | - b'/' if next == b'/' => { self.advance(); (TokenKind::Concat, "//") } | |
| 910 | - b'/' if next == b'=' => { self.advance(); (TokenKind::Ne, "/=") } | |
| 1060 | + b'/' if next == b'/' => { | |
| 1061 | + self.advance(); | |
| 1062 | + (TokenKind::Concat, "//") | |
| 1063 | + } | |
| 1064 | + b'/' if next == b'=' => { | |
| 1065 | + self.advance(); | |
| 1066 | + (TokenKind::Ne, "/=") | |
| 1067 | + } | |
| 911 | 1068 | b'/' => (TokenKind::Slash, "/"), |
| 912 | - b'=' if next == b'=' => { self.advance(); (TokenKind::Eq, "==") } | |
| 913 | - b'=' if next == b'>' => { self.advance(); (TokenKind::Arrow, "=>") } | |
| 1069 | + b'=' if next == b'=' => { | |
| 1070 | + self.advance(); | |
| 1071 | + (TokenKind::Eq, "==") | |
| 1072 | + } | |
| 1073 | + b'=' if next == b'>' => { | |
| 1074 | + self.advance(); | |
| 1075 | + (TokenKind::Arrow, "=>") | |
| 1076 | + } | |
| 914 | 1077 | b'=' => (TokenKind::Assign, "="), |
| 915 | - b'<' if next == b'=' => { self.advance(); (TokenKind::Le, "<=") } | |
| 1078 | + b'<' if next == b'=' => { | |
| 1079 | + self.advance(); | |
| 1080 | + (TokenKind::Le, "<=") | |
| 1081 | + } | |
| 916 | 1082 | b'<' => (TokenKind::Lt, "<"), |
| 917 | - b'>' if next == b'=' => { self.advance(); (TokenKind::Ge, ">=") } | |
| 1083 | + b'>' if next == b'=' => { | |
| 1084 | + self.advance(); | |
| 1085 | + (TokenKind::Ge, ">=") | |
| 1086 | + } | |
| 918 | 1087 | b'>' => (TokenKind::Gt, ">"), |
| 919 | 1088 | b'(' => (TokenKind::LParen, "("), |
| 920 | 1089 | b')' => (TokenKind::RParen, ")"), |
| 921 | 1090 | b'[' => (TokenKind::LBracket, "["), |
| 922 | 1091 | b']' => (TokenKind::RBracket, "]"), |
| 923 | 1092 | b',' => (TokenKind::Comma, ","), |
| 924 | - b':' if next == b':' => { self.advance(); (TokenKind::ColonColon, "::") } | |
| 1093 | + b':' if next == b':' => { | |
| 1094 | + self.advance(); | |
| 1095 | + (TokenKind::ColonColon, "::") | |
| 1096 | + } | |
| 925 | 1097 | b':' => (TokenKind::Colon, ":"), |
| 926 | 1098 | b';' => (TokenKind::Semicolon, ";"), |
| 927 | 1099 | b'%' => (TokenKind::Percent, "%"), |
@@ -948,11 +1120,19 @@ mod tests { | ||
| 948 | 1120 | } |
| 949 | 1121 | |
| 950 | 1122 | fn kinds(src: &str) -> Vec<TokenKind> { |
| 951 | - toks(src).into_iter().map(|t| t.kind).filter(|k| !matches!(k, TokenKind::Eof)).collect() | |
| 1123 | + toks(src) | |
| 1124 | + .into_iter() | |
| 1125 | + .map(|t| t.kind) | |
| 1126 | + .filter(|k| !matches!(k, TokenKind::Eof)) | |
| 1127 | + .collect() | |
| 952 | 1128 | } |
| 953 | 1129 | |
| 954 | 1130 | fn texts(src: &str) -> Vec<String> { |
| 955 | - toks(src).into_iter().map(|t| t.text).filter(|t| !t.is_empty()).collect() | |
| 1131 | + toks(src) | |
| 1132 | + .into_iter() | |
| 1133 | + .map(|t| t.text) | |
| 1134 | + .filter(|t| !t.is_empty()) | |
| 1135 | + .collect() | |
| 956 | 1136 | } |
| 957 | 1137 | |
| 958 | 1138 | // ---- Identifiers ---- |
@@ -972,10 +1152,15 @@ mod tests { | ||
| 972 | 1152 | fn keyword_is_identifier() { |
| 973 | 1153 | // Keywords are not reserved — lexed as identifiers. |
| 974 | 1154 | let toks = kinds("integer real do if"); |
| 975 | - assert_eq!(toks, vec![ | |
| 976 | - TokenKind::Identifier, TokenKind::Identifier, | |
| 977 | - TokenKind::Identifier, TokenKind::Identifier, | |
| 978 | - ]); | |
| 1155 | + assert_eq!( | |
| 1156 | + toks, | |
| 1157 | + vec![ | |
| 1158 | + TokenKind::Identifier, | |
| 1159 | + TokenKind::Identifier, | |
| 1160 | + TokenKind::Identifier, | |
| 1161 | + TokenKind::Identifier, | |
| 1162 | + ] | |
| 1163 | + ); | |
| 979 | 1164 | } |
| 980 | 1165 | |
| 981 | 1166 | #[test] |
@@ -1158,9 +1343,15 @@ mod tests { | ||
| 1158 | 1343 | |
| 1159 | 1344 | #[test] |
| 1160 | 1345 | fn arithmetic_operators() { |
| 1161 | - assert_eq!(kinds("+ - * /"), vec![ | |
| 1162 | - TokenKind::Plus, TokenKind::Minus, TokenKind::Star, TokenKind::Slash, | |
| 1163 | - ]); | |
| 1346 | + assert_eq!( | |
| 1347 | + kinds("+ - * /"), | |
| 1348 | + vec![ | |
| 1349 | + TokenKind::Plus, | |
| 1350 | + TokenKind::Minus, | |
| 1351 | + TokenKind::Star, | |
| 1352 | + TokenKind::Slash, | |
| 1353 | + ] | |
| 1354 | + ); | |
| 1164 | 1355 | } |
| 1165 | 1356 | |
| 1166 | 1357 | #[test] |
@@ -1175,34 +1366,55 @@ mod tests { | ||
| 1175 | 1366 | |
| 1176 | 1367 | #[test] |
| 1177 | 1368 | fn comparison_operators() { |
| 1178 | - assert_eq!(kinds("== /= < > <= >="), vec![ | |
| 1179 | - TokenKind::Eq, TokenKind::Ne, TokenKind::Lt, TokenKind::Gt, | |
| 1180 | - TokenKind::Le, TokenKind::Ge, | |
| 1181 | - ]); | |
| 1369 | + assert_eq!( | |
| 1370 | + kinds("== /= < > <= >="), | |
| 1371 | + vec![ | |
| 1372 | + TokenKind::Eq, | |
| 1373 | + TokenKind::Ne, | |
| 1374 | + TokenKind::Lt, | |
| 1375 | + TokenKind::Gt, | |
| 1376 | + TokenKind::Le, | |
| 1377 | + TokenKind::Ge, | |
| 1378 | + ] | |
| 1379 | + ); | |
| 1182 | 1380 | } |
| 1183 | 1381 | |
| 1184 | 1382 | #[test] |
| 1185 | 1383 | fn dot_comparison_operators() { |
| 1186 | - assert_eq!(kinds(".eq. .ne. .lt. .gt. .le. .ge."), vec![ | |
| 1187 | - TokenKind::DotOp("eq".into()), TokenKind::DotOp("ne".into()), | |
| 1188 | - TokenKind::DotOp("lt".into()), TokenKind::DotOp("gt".into()), | |
| 1189 | - TokenKind::DotOp("le".into()), TokenKind::DotOp("ge".into()), | |
| 1190 | - ]); | |
| 1384 | + assert_eq!( | |
| 1385 | + kinds(".eq. .ne. .lt. .gt. .le. .ge."), | |
| 1386 | + vec![ | |
| 1387 | + TokenKind::DotOp("eq".into()), | |
| 1388 | + TokenKind::DotOp("ne".into()), | |
| 1389 | + TokenKind::DotOp("lt".into()), | |
| 1390 | + TokenKind::DotOp("gt".into()), | |
| 1391 | + TokenKind::DotOp("le".into()), | |
| 1392 | + TokenKind::DotOp("ge".into()), | |
| 1393 | + ] | |
| 1394 | + ); | |
| 1191 | 1395 | } |
| 1192 | 1396 | |
| 1193 | 1397 | #[test] |
| 1194 | 1398 | fn dot_logical_operators() { |
| 1195 | - assert_eq!(kinds(".and. .or. .not."), vec![ | |
| 1196 | - TokenKind::DotOp("and".into()), TokenKind::DotOp("or".into()), | |
| 1197 | - TokenKind::DotOp("not".into()), | |
| 1198 | - ]); | |
| 1399 | + assert_eq!( | |
| 1400 | + kinds(".and. .or. .not."), | |
| 1401 | + vec![ | |
| 1402 | + TokenKind::DotOp("and".into()), | |
| 1403 | + TokenKind::DotOp("or".into()), | |
| 1404 | + TokenKind::DotOp("not".into()), | |
| 1405 | + ] | |
| 1406 | + ); | |
| 1199 | 1407 | } |
| 1200 | 1408 | |
| 1201 | 1409 | #[test] |
| 1202 | 1410 | fn dot_eqv_neqv() { |
| 1203 | - assert_eq!(kinds(".eqv. .neqv."), vec![ | |
| 1204 | - TokenKind::DotOp("eqv".into()), TokenKind::DotOp("neqv".into()), | |
| 1205 | - ]); | |
| 1411 | + assert_eq!( | |
| 1412 | + kinds(".eqv. .neqv."), | |
| 1413 | + vec![ | |
| 1414 | + TokenKind::DotOp("eqv".into()), | |
| 1415 | + TokenKind::DotOp("neqv".into()), | |
| 1416 | + ] | |
| 1417 | + ); | |
| 1206 | 1418 | } |
| 1207 | 1419 | |
| 1208 | 1420 | #[test] |
@@ -1214,11 +1426,19 @@ mod tests { | ||
| 1214 | 1426 | |
| 1215 | 1427 | #[test] |
| 1216 | 1428 | fn punctuation() { |
| 1217 | - assert_eq!(kinds("( ) [ ] , : ; %"), vec![ | |
| 1218 | - TokenKind::LParen, TokenKind::RParen, | |
| 1219 | - TokenKind::LBracket, TokenKind::RBracket, | |
| 1220 | - TokenKind::Comma, TokenKind::Colon, TokenKind::Semicolon, TokenKind::Percent, | |
| 1221 | - ]); | |
| 1429 | + assert_eq!( | |
| 1430 | + kinds("( ) [ ] , : ; %"), | |
| 1431 | + vec![ | |
| 1432 | + TokenKind::LParen, | |
| 1433 | + TokenKind::RParen, | |
| 1434 | + TokenKind::LBracket, | |
| 1435 | + TokenKind::RBracket, | |
| 1436 | + TokenKind::Comma, | |
| 1437 | + TokenKind::Colon, | |
| 1438 | + TokenKind::Semicolon, | |
| 1439 | + TokenKind::Percent, | |
| 1440 | + ] | |
| 1441 | + ); | |
| 1222 | 1442 | } |
| 1223 | 1443 | |
| 1224 | 1444 | #[test] |
@@ -1249,16 +1469,26 @@ mod tests { | ||
| 1249 | 1469 | |
| 1250 | 1470 | #[test] |
| 1251 | 1471 | fn newline_is_statement_terminator() { |
| 1252 | - assert_eq!(kinds("x\ny"), vec![ | |
| 1253 | - TokenKind::Identifier, TokenKind::Newline, TokenKind::Identifier, | |
| 1254 | - ]); | |
| 1472 | + assert_eq!( | |
| 1473 | + kinds("x\ny"), | |
| 1474 | + vec![ | |
| 1475 | + TokenKind::Identifier, | |
| 1476 | + TokenKind::Newline, | |
| 1477 | + TokenKind::Identifier, | |
| 1478 | + ] | |
| 1479 | + ); | |
| 1255 | 1480 | } |
| 1256 | 1481 | |
| 1257 | 1482 | #[test] |
| 1258 | 1483 | fn semicolon_is_statement_separator() { |
| 1259 | - assert_eq!(kinds("x; y"), vec![ | |
| 1260 | - TokenKind::Identifier, TokenKind::Semicolon, TokenKind::Identifier, | |
| 1261 | - ]); | |
| 1484 | + assert_eq!( | |
| 1485 | + kinds("x; y"), | |
| 1486 | + vec![ | |
| 1487 | + TokenKind::Identifier, | |
| 1488 | + TokenKind::Semicolon, | |
| 1489 | + TokenKind::Identifier, | |
| 1490 | + ] | |
| 1491 | + ); | |
| 1262 | 1492 | } |
| 1263 | 1493 | |
| 1264 | 1494 | // ---- Continuation lines ---- |
@@ -1266,19 +1496,40 @@ mod tests { | ||
| 1266 | 1496 | #[test] |
| 1267 | 1497 | fn continuation_joins_tokens() { |
| 1268 | 1498 | let k = kinds("x + &\n y"); |
| 1269 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 1499 | + assert_eq!( | |
| 1500 | + k, | |
| 1501 | + vec![ | |
| 1502 | + TokenKind::Identifier, | |
| 1503 | + TokenKind::Plus, | |
| 1504 | + TokenKind::Identifier | |
| 1505 | + ] | |
| 1506 | + ); | |
| 1270 | 1507 | } |
| 1271 | 1508 | |
| 1272 | 1509 | #[test] |
| 1273 | 1510 | fn continuation_with_leading_ampersand() { |
| 1274 | 1511 | let k = kinds("x + &\n &y"); |
| 1275 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 1512 | + assert_eq!( | |
| 1513 | + k, | |
| 1514 | + vec![ | |
| 1515 | + TokenKind::Identifier, | |
| 1516 | + TokenKind::Plus, | |
| 1517 | + TokenKind::Identifier | |
| 1518 | + ] | |
| 1519 | + ); | |
| 1276 | 1520 | } |
| 1277 | 1521 | |
| 1278 | 1522 | #[test] |
| 1279 | 1523 | fn continuation_with_comment() { |
| 1280 | 1524 | let k = kinds("x + & ! comment\n y"); |
| 1281 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 1525 | + assert_eq!( | |
| 1526 | + k, | |
| 1527 | + vec![ | |
| 1528 | + TokenKind::Identifier, | |
| 1529 | + TokenKind::Plus, | |
| 1530 | + TokenKind::Identifier | |
| 1531 | + ] | |
| 1532 | + ); | |
| 1282 | 1533 | } |
| 1283 | 1534 | |
| 1284 | 1535 | // ---- Source locations ---- |
@@ -1299,34 +1550,64 @@ mod tests { | ||
| 1299 | 1550 | #[test] |
| 1300 | 1551 | fn complex_declaration() { |
| 1301 | 1552 | let k = kinds("integer, allocatable :: x(:,:)"); |
| 1302 | - assert_eq!(k, vec![ | |
| 1303 | - TokenKind::Identifier, TokenKind::Comma, TokenKind::Identifier, | |
| 1304 | - TokenKind::ColonColon, | |
| 1305 | - TokenKind::Identifier, TokenKind::LParen, TokenKind::Colon, | |
| 1306 | - TokenKind::Comma, TokenKind::Colon, TokenKind::RParen, | |
| 1307 | - ]); | |
| 1553 | + assert_eq!( | |
| 1554 | + k, | |
| 1555 | + vec![ | |
| 1556 | + TokenKind::Identifier, | |
| 1557 | + TokenKind::Comma, | |
| 1558 | + TokenKind::Identifier, | |
| 1559 | + TokenKind::ColonColon, | |
| 1560 | + TokenKind::Identifier, | |
| 1561 | + TokenKind::LParen, | |
| 1562 | + TokenKind::Colon, | |
| 1563 | + TokenKind::Comma, | |
| 1564 | + TokenKind::Colon, | |
| 1565 | + TokenKind::RParen, | |
| 1566 | + ] | |
| 1567 | + ); | |
| 1308 | 1568 | } |
| 1309 | 1569 | |
| 1310 | 1570 | #[test] |
| 1311 | 1571 | fn pointer_assignment() { |
| 1312 | 1572 | let k = kinds("ptr => target"); |
| 1313 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Arrow, TokenKind::Identifier]); | |
| 1573 | + assert_eq!( | |
| 1574 | + k, | |
| 1575 | + vec![ | |
| 1576 | + TokenKind::Identifier, | |
| 1577 | + TokenKind::Arrow, | |
| 1578 | + TokenKind::Identifier | |
| 1579 | + ] | |
| 1580 | + ); | |
| 1314 | 1581 | } |
| 1315 | 1582 | |
| 1316 | 1583 | #[test] |
| 1317 | 1584 | fn component_access() { |
| 1318 | 1585 | let k = kinds("obj%member"); |
| 1319 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Percent, TokenKind::Identifier]); | |
| 1586 | + assert_eq!( | |
| 1587 | + k, | |
| 1588 | + vec![ | |
| 1589 | + TokenKind::Identifier, | |
| 1590 | + TokenKind::Percent, | |
| 1591 | + TokenKind::Identifier | |
| 1592 | + ] | |
| 1593 | + ); | |
| 1320 | 1594 | } |
| 1321 | 1595 | |
| 1322 | 1596 | #[test] |
| 1323 | 1597 | fn array_constructor_bracket() { |
| 1324 | 1598 | let k = kinds("[1, 2, 3]"); |
| 1325 | - assert_eq!(k, vec![ | |
| 1326 | - TokenKind::LBracket, TokenKind::IntegerLiteral, TokenKind::Comma, | |
| 1327 | - TokenKind::IntegerLiteral, TokenKind::Comma, TokenKind::IntegerLiteral, | |
| 1328 | - TokenKind::RBracket, | |
| 1329 | - ]); | |
| 1599 | + assert_eq!( | |
| 1600 | + k, | |
| 1601 | + vec![ | |
| 1602 | + TokenKind::LBracket, | |
| 1603 | + TokenKind::IntegerLiteral, | |
| 1604 | + TokenKind::Comma, | |
| 1605 | + TokenKind::IntegerLiteral, | |
| 1606 | + TokenKind::Comma, | |
| 1607 | + TokenKind::IntegerLiteral, | |
| 1608 | + TokenKind::RBracket, | |
| 1609 | + ] | |
| 1610 | + ); | |
| 1330 | 1611 | } |
| 1331 | 1612 | |
| 1332 | 1613 | // ---- Ambiguity cases from spec ---- |
@@ -1341,9 +1622,14 @@ mod tests { | ||
| 1341 | 1622 | #[test] |
| 1342 | 1623 | fn integer_dot_and_with_spaces() { |
| 1343 | 1624 | let k = kinds("a .and. b"); |
| 1344 | - assert_eq!(k, vec![ | |
| 1345 | - TokenKind::Identifier, TokenKind::DotOp("and".into()), TokenKind::Identifier, | |
| 1346 | - ]); | |
| 1625 | + assert_eq!( | |
| 1626 | + k, | |
| 1627 | + vec![ | |
| 1628 | + TokenKind::Identifier, | |
| 1629 | + TokenKind::DotOp("and".into()), | |
| 1630 | + TokenKind::Identifier, | |
| 1631 | + ] | |
| 1632 | + ); | |
| 1347 | 1633 | } |
| 1348 | 1634 | |
| 1349 | 1635 | #[test] |
@@ -1351,25 +1637,40 @@ mod tests { | ||
| 1351 | 1637 | // Critical ambiguity: 1.eq.2 must NOT be parsed as a real with exponent. |
| 1352 | 1638 | // It's: integer(1), .eq., integer(2) |
| 1353 | 1639 | let k = kinds("1.eq.2"); |
| 1354 | - assert_eq!(k, vec![ | |
| 1355 | - TokenKind::IntegerLiteral, TokenKind::DotOp("eq".into()), TokenKind::IntegerLiteral, | |
| 1356 | - ]); | |
| 1640 | + assert_eq!( | |
| 1641 | + k, | |
| 1642 | + vec![ | |
| 1643 | + TokenKind::IntegerLiteral, | |
| 1644 | + TokenKind::DotOp("eq".into()), | |
| 1645 | + TokenKind::IntegerLiteral, | |
| 1646 | + ] | |
| 1647 | + ); | |
| 1357 | 1648 | } |
| 1358 | 1649 | |
| 1359 | 1650 | #[test] |
| 1360 | 1651 | fn integer_dot_and_no_spaces() { |
| 1361 | 1652 | let k = kinds("1.and.2"); |
| 1362 | - assert_eq!(k, vec![ | |
| 1363 | - TokenKind::IntegerLiteral, TokenKind::DotOp("and".into()), TokenKind::IntegerLiteral, | |
| 1364 | - ]); | |
| 1653 | + assert_eq!( | |
| 1654 | + k, | |
| 1655 | + vec![ | |
| 1656 | + TokenKind::IntegerLiteral, | |
| 1657 | + TokenKind::DotOp("and".into()), | |
| 1658 | + TokenKind::IntegerLiteral, | |
| 1659 | + ] | |
| 1660 | + ); | |
| 1365 | 1661 | } |
| 1366 | 1662 | |
| 1367 | 1663 | #[test] |
| 1368 | 1664 | fn integer_dot_ne_no_spaces() { |
| 1369 | 1665 | let k = kinds("x.ne.y"); |
| 1370 | - assert_eq!(k, vec![ | |
| 1371 | - TokenKind::Identifier, TokenKind::DotOp("ne".into()), TokenKind::Identifier, | |
| 1372 | - ]); | |
| 1666 | + assert_eq!( | |
| 1667 | + k, | |
| 1668 | + vec![ | |
| 1669 | + TokenKind::Identifier, | |
| 1670 | + TokenKind::DotOp("ne".into()), | |
| 1671 | + TokenKind::Identifier, | |
| 1672 | + ] | |
| 1673 | + ); | |
| 1373 | 1674 | } |
| 1374 | 1675 | |
| 1375 | 1676 | #[test] |
@@ -1396,9 +1697,14 @@ mod tests { | ||
| 1396 | 1697 | fn five_dot_eq_three() { |
| 1397 | 1698 | // 5.eq.3 — integer, .eq., integer (not a real) |
| 1398 | 1699 | let k = kinds("5.eq.3"); |
| 1399 | - assert_eq!(k, vec![ | |
| 1400 | - TokenKind::IntegerLiteral, TokenKind::DotOp("eq".into()), TokenKind::IntegerLiteral, | |
| 1401 | - ]); | |
| 1700 | + assert_eq!( | |
| 1701 | + k, | |
| 1702 | + vec![ | |
| 1703 | + TokenKind::IntegerLiteral, | |
| 1704 | + TokenKind::DotOp("eq".into()), | |
| 1705 | + TokenKind::IntegerLiteral, | |
| 1706 | + ] | |
| 1707 | + ); | |
| 1402 | 1708 | } |
| 1403 | 1709 | |
| 1404 | 1710 | // ---- Error cases ---- |
@@ -1428,9 +1734,20 @@ program hello | ||
| 1428 | 1734 | end program hello |
| 1429 | 1735 | "; |
| 1430 | 1736 | let tokens = Lexer::tokenize(src, 0).unwrap(); |
| 1431 | - let ident_count = tokens.iter().filter(|t| t.kind == TokenKind::Identifier).count(); | |
| 1432 | - assert!(ident_count >= 8, "expected 8+ identifiers, got {}", ident_count); | |
| 1433 | - let last_non_eof = tokens.iter().rev().find(|t| t.kind != TokenKind::Eof && t.kind != TokenKind::Newline).unwrap(); | |
| 1737 | + let ident_count = tokens | |
| 1738 | + .iter() | |
| 1739 | + .filter(|t| t.kind == TokenKind::Identifier) | |
| 1740 | + .count(); | |
| 1741 | + assert!( | |
| 1742 | + ident_count >= 8, | |
| 1743 | + "expected 8+ identifiers, got {}", | |
| 1744 | + ident_count | |
| 1745 | + ); | |
| 1746 | + let last_non_eof = tokens | |
| 1747 | + .iter() | |
| 1748 | + .rev() | |
| 1749 | + .find(|t| t.kind != TokenKind::Eof && t.kind != TokenKind::Newline) | |
| 1750 | + .unwrap(); | |
| 1434 | 1751 | assert_eq!(last_non_eof.text, "hello"); |
| 1435 | 1752 | } |
| 1436 | 1753 | |
@@ -1438,17 +1755,22 @@ end program hello | ||
| 1438 | 1755 | |
| 1439 | 1756 | /// Try to tokenize a fortsh source file. Strips preprocessor directives first. |
| 1440 | 1757 | fn try_lex_fortsh_file(path: &str) -> Result<usize, String> { |
| 1441 | - let src = std::fs::read_to_string(path) | |
| 1442 | - .map_err(|e| format!("{}: {}", path, e))?; | |
| 1758 | + let src = std::fs::read_to_string(path).map_err(|e| format!("{}: {}", path, e))?; | |
| 1443 | 1759 | |
| 1444 | 1760 | // Strip preprocessor directives (lexer expects preprocessed input). |
| 1445 | - let filtered: String = src.lines() | |
| 1446 | - .map(|line| if line.trim_start().starts_with('#') { "" } else { line }) | |
| 1761 | + let filtered: String = src | |
| 1762 | + .lines() | |
| 1763 | + .map(|line| { | |
| 1764 | + if line.trim_start().starts_with('#') { | |
| 1765 | + "" | |
| 1766 | + } else { | |
| 1767 | + line | |
| 1768 | + } | |
| 1769 | + }) | |
| 1447 | 1770 | .collect::<Vec<_>>() |
| 1448 | 1771 | .join("\n"); |
| 1449 | 1772 | |
| 1450 | - let tokens = Lexer::tokenize(&filtered, 0) | |
| 1451 | - .map_err(|e| format!("{}: {}", path, e))?; | |
| 1773 | + let tokens = Lexer::tokenize(&filtered, 0).map_err(|e| format!("{}: {}", path, e))?; | |
| 1452 | 1774 | |
| 1453 | 1775 | Ok(tokens.len()) |
| 1454 | 1776 | } |
@@ -1545,12 +1867,18 @@ end program hello | ||
| 1545 | 1867 | #[test] |
| 1546 | 1868 | fn multiple_continuations() { |
| 1547 | 1869 | let k = kinds("x = a + &\n b + &\n c"); |
| 1548 | - assert_eq!(k, vec![ | |
| 1549 | - TokenKind::Identifier, TokenKind::Assign, | |
| 1550 | - TokenKind::Identifier, TokenKind::Plus, | |
| 1551 | - TokenKind::Identifier, TokenKind::Plus, | |
| 1552 | - TokenKind::Identifier, | |
| 1553 | - ]); | |
| 1870 | + assert_eq!( | |
| 1871 | + k, | |
| 1872 | + vec![ | |
| 1873 | + TokenKind::Identifier, | |
| 1874 | + TokenKind::Assign, | |
| 1875 | + TokenKind::Identifier, | |
| 1876 | + TokenKind::Plus, | |
| 1877 | + TokenKind::Identifier, | |
| 1878 | + TokenKind::Plus, | |
| 1879 | + TokenKind::Identifier, | |
| 1880 | + ] | |
| 1881 | + ); | |
| 1554 | 1882 | } |
| 1555 | 1883 | |
| 1556 | 1884 | #[test] |
@@ -1575,11 +1903,17 @@ end program hello | ||
| 1575 | 1903 | #[test] |
| 1576 | 1904 | fn spec_ambiguity_real_function_call() { |
| 1577 | 1905 | let k = kinds("x = real(i)"); |
| 1578 | - assert_eq!(k, vec![ | |
| 1579 | - TokenKind::Identifier, TokenKind::Assign, | |
| 1580 | - TokenKind::Identifier, TokenKind::LParen, | |
| 1581 | - TokenKind::Identifier, TokenKind::RParen, | |
| 1582 | - ]); | |
| 1906 | + assert_eq!( | |
| 1907 | + k, | |
| 1908 | + vec![ | |
| 1909 | + TokenKind::Identifier, | |
| 1910 | + TokenKind::Assign, | |
| 1911 | + TokenKind::Identifier, | |
| 1912 | + TokenKind::LParen, | |
| 1913 | + TokenKind::Identifier, | |
| 1914 | + TokenKind::RParen, | |
| 1915 | + ] | |
| 1916 | + ); | |
| 1583 | 1917 | } |
| 1584 | 1918 | |
| 1585 | 1919 | #[test] |
@@ -1595,7 +1929,8 @@ end program hello | ||
| 1595 | 1929 | assert!(k.contains(&TokenKind::Comma)); |
| 1596 | 1930 | // "do" and "i" are both identifiers. |
| 1597 | 1931 | let tokens = toks("do i = 1, 10"); |
| 1598 | - let idents: Vec<_> = tokens.iter() | |
| 1932 | + let idents: Vec<_> = tokens | |
| 1933 | + .iter() | |
| 1599 | 1934 | .filter(|t| t.kind == TokenKind::Identifier) |
| 1600 | 1935 | .map(|t| t.text.as_str()) |
| 1601 | 1936 | .collect(); |
@@ -1606,9 +1941,14 @@ end program hello | ||
| 1606 | 1941 | #[test] |
| 1607 | 1942 | fn spec_ambiguity_do_as_variable() { |
| 1608 | 1943 | let k = kinds("do = 3.14"); |
| 1609 | - assert_eq!(k, vec![ | |
| 1610 | - TokenKind::Identifier, TokenKind::Assign, TokenKind::RealLiteral, | |
| 1611 | - ]); | |
| 1944 | + assert_eq!( | |
| 1945 | + k, | |
| 1946 | + vec![ | |
| 1947 | + TokenKind::Identifier, | |
| 1948 | + TokenKind::Assign, | |
| 1949 | + TokenKind::RealLiteral, | |
| 1950 | + ] | |
| 1951 | + ); | |
| 1612 | 1952 | assert_eq!(toks("do = 3.14")[0].text, "do"); |
| 1613 | 1953 | } |
| 1614 | 1954 | |
@@ -1629,11 +1969,18 @@ end program hello | ||
| 1629 | 1969 | #[test] |
| 1630 | 1970 | fn semicolon_separates_statements() { |
| 1631 | 1971 | let k = kinds("x = 1; y = 2"); |
| 1632 | - assert_eq!(k, vec![ | |
| 1633 | - TokenKind::Identifier, TokenKind::Assign, TokenKind::IntegerLiteral, | |
| 1634 | - TokenKind::Semicolon, | |
| 1635 | - TokenKind::Identifier, TokenKind::Assign, TokenKind::IntegerLiteral, | |
| 1636 | - ]); | |
| 1972 | + assert_eq!( | |
| 1973 | + k, | |
| 1974 | + vec![ | |
| 1975 | + TokenKind::Identifier, | |
| 1976 | + TokenKind::Assign, | |
| 1977 | + TokenKind::IntegerLiteral, | |
| 1978 | + TokenKind::Semicolon, | |
| 1979 | + TokenKind::Identifier, | |
| 1980 | + TokenKind::Assign, | |
| 1981 | + TokenKind::IntegerLiteral, | |
| 1982 | + ] | |
| 1983 | + ); | |
| 1637 | 1984 | } |
| 1638 | 1985 | |
| 1639 | 1986 | // ---- Performance ---- |
@@ -1642,7 +1989,9 @@ end program hello | ||
| 1642 | 1989 | fn performance_fortsh_under_one_second() { |
| 1643 | 1990 | let src_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/../fortsh/src"); |
| 1644 | 1991 | let root = std::path::Path::new(src_dir); |
| 1645 | - if !root.exists() { return; } | |
| 1992 | + if !root.exists() { | |
| 1993 | + return; | |
| 1994 | + } | |
| 1646 | 1995 | |
| 1647 | 1996 | // Collect all source. |
| 1648 | 1997 | let mut all_source = String::new(); |
@@ -1650,12 +1999,21 @@ end program hello | ||
| 1650 | 1999 | if let Ok(entries) = std::fs::read_dir(dir) { |
| 1651 | 2000 | for entry in entries.flatten() { |
| 1652 | 2001 | let path = entry.path(); |
| 1653 | - if path.is_dir() { collect(&path, buf); } | |
| 1654 | - else if path.extension().map_or(false, |e| e == "f90") { | |
| 2002 | + if path.is_dir() { | |
| 2003 | + collect(&path, buf); | |
| 2004 | + } else if path.extension().map_or(false, |e| e == "f90") { | |
| 1655 | 2005 | if let Ok(src) = std::fs::read_to_string(&path) { |
| 1656 | - let filtered: String = src.lines() | |
| 1657 | - .map(|l| if l.trim_start().starts_with('#') { "" } else { l }) | |
| 1658 | - .collect::<Vec<_>>().join("\n"); | |
| 2006 | + let filtered: String = src | |
| 2007 | + .lines() | |
| 2008 | + .map(|l| { | |
| 2009 | + if l.trim_start().starts_with('#') { | |
| 2010 | + "" | |
| 2011 | + } else { | |
| 2012 | + l | |
| 2013 | + } | |
| 2014 | + }) | |
| 2015 | + .collect::<Vec<_>>() | |
| 2016 | + .join("\n"); | |
| 1659 | 2017 | buf.push_str(&filtered); |
| 1660 | 2018 | buf.push('\n'); |
| 1661 | 2019 | } |
@@ -1671,10 +2029,17 @@ end program hello | ||
| 1671 | 2029 | |
| 1672 | 2030 | assert!(result.is_ok(), "failed to tokenize: {:?}", result.err()); |
| 1673 | 2031 | let tokens = result.unwrap(); |
| 1674 | - eprintln!("lexed {} tokens from ~{} lines in {:?}", | |
| 1675 | - tokens.len(), all_source.lines().count(), elapsed); | |
| 1676 | - assert!(elapsed.as_secs_f64() < 1.0, | |
| 1677 | - "too slow: {:?} (must be < 1s)", elapsed); | |
| 2032 | + eprintln!( | |
| 2033 | + "lexed {} tokens from ~{} lines in {:?}", | |
| 2034 | + tokens.len(), | |
| 2035 | + all_source.lines().count(), | |
| 2036 | + elapsed | |
| 2037 | + ); | |
| 2038 | + assert!( | |
| 2039 | + elapsed.as_secs_f64() < 1.0, | |
| 2040 | + "too slow: {:?} (must be < 1s)", | |
| 2041 | + elapsed | |
| 2042 | + ); | |
| 1678 | 2043 | } |
| 1679 | 2044 | |
| 1680 | 2045 | // ====================================================================== |
@@ -1685,19 +2050,40 @@ end program hello | ||
| 1685 | 2050 | fn continuation_over_comment_line() { |
| 1686 | 2051 | // Per F2018 6.3.2.4: comment lines between continued lines are allowed. |
| 1687 | 2052 | let k = kinds("x + & \n! this is a comment\n y"); |
| 1688 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 2053 | + assert_eq!( | |
| 2054 | + k, | |
| 2055 | + vec![ | |
| 2056 | + TokenKind::Identifier, | |
| 2057 | + TokenKind::Plus, | |
| 2058 | + TokenKind::Identifier | |
| 2059 | + ] | |
| 2060 | + ); | |
| 1689 | 2061 | } |
| 1690 | 2062 | |
| 1691 | 2063 | #[test] |
| 1692 | 2064 | fn continuation_over_blank_line() { |
| 1693 | 2065 | let k = kinds("x + &\n\n y"); |
| 1694 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 2066 | + assert_eq!( | |
| 2067 | + k, | |
| 2068 | + vec![ | |
| 2069 | + TokenKind::Identifier, | |
| 2070 | + TokenKind::Plus, | |
| 2071 | + TokenKind::Identifier | |
| 2072 | + ] | |
| 2073 | + ); | |
| 1695 | 2074 | } |
| 1696 | 2075 | |
| 1697 | 2076 | #[test] |
| 1698 | 2077 | fn continuation_over_multiple_comments_and_blanks() { |
| 1699 | 2078 | let k = kinds("x + &\n! comment 1\n\n! comment 2\n y"); |
| 1700 | - assert_eq!(k, vec![TokenKind::Identifier, TokenKind::Plus, TokenKind::Identifier]); | |
| 2079 | + assert_eq!( | |
| 2080 | + k, | |
| 2081 | + vec![ | |
| 2082 | + TokenKind::Identifier, | |
| 2083 | + TokenKind::Plus, | |
| 2084 | + TokenKind::Identifier | |
| 2085 | + ] | |
| 2086 | + ); | |
| 1701 | 2087 | } |
| 1702 | 2088 | |
| 1703 | 2089 | #[test] |
@@ -1721,19 +2107,31 @@ end program hello | ||
| 1721 | 2107 | // Round-trip: tokens -> text -> tokens should produce identical kinds. |
| 1722 | 2108 | let src = "integer :: x = 42\n"; |
| 1723 | 2109 | let tokens1 = Lexer::tokenize(src, 0).unwrap(); |
| 1724 | - let reconstructed: String = tokens1.iter().map(|t| t.text.as_str()).collect::<Vec<_>>().join(" "); | |
| 2110 | + let reconstructed: String = tokens1 | |
| 2111 | + .iter() | |
| 2112 | + .map(|t| t.text.as_str()) | |
| 2113 | + .collect::<Vec<_>>() | |
| 2114 | + .join(" "); | |
| 1725 | 2115 | let tokens2 = Lexer::tokenize(&reconstructed, 0).unwrap(); |
| 1726 | 2116 | |
| 1727 | 2117 | let kinds1: Vec<_> = tokens1.iter().map(|t| &t.kind).collect(); |
| 1728 | 2118 | let kinds2: Vec<_> = tokens2.iter().map(|t| &t.kind).collect(); |
| 1729 | - assert_eq!(kinds1, kinds2, "round-trip failed:\n original: {:?}\n reconstructed: {:?}", src, reconstructed); | |
| 2119 | + assert_eq!( | |
| 2120 | + kinds1, kinds2, | |
| 2121 | + "round-trip failed:\n original: {:?}\n reconstructed: {:?}", | |
| 2122 | + src, reconstructed | |
| 2123 | + ); | |
| 1730 | 2124 | } |
| 1731 | 2125 | |
| 1732 | 2126 | #[test] |
| 1733 | 2127 | fn round_trip_operators() { |
| 1734 | 2128 | let src = "x = a + b * c ** d // e\n"; |
| 1735 | 2129 | let tokens1 = Lexer::tokenize(src, 0).unwrap(); |
| 1736 | - let reconstructed: String = tokens1.iter().map(|t| t.text.as_str()).collect::<Vec<_>>().join(" "); | |
| 2130 | + let reconstructed: String = tokens1 | |
| 2131 | + .iter() | |
| 2132 | + .map(|t| t.text.as_str()) | |
| 2133 | + .collect::<Vec<_>>() | |
| 2134 | + .join(" "); | |
| 1737 | 2135 | let tokens2 = Lexer::tokenize(&reconstructed, 0).unwrap(); |
| 1738 | 2136 | |
| 1739 | 2137 | let kinds1: Vec<_> = tokens1.iter().map(|t| &t.kind).collect(); |
@@ -1751,19 +2149,31 @@ end program hello | ||
| 1751 | 2149 | |
| 1752 | 2150 | #[test] |
| 1753 | 2151 | fn tokenize_fortsh_types() { |
| 1754 | - let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../fortsh/src/common/types.f90"); | |
| 2152 | + let path = concat!( | |
| 2153 | + env!("CARGO_MANIFEST_DIR"), | |
| 2154 | + "/../fortsh/src/common/types.f90" | |
| 2155 | + ); | |
| 1755 | 2156 | if !std::path::Path::new(path).exists() { |
| 1756 | 2157 | // fortsh not available — skip gracefully. |
| 1757 | 2158 | return; |
| 1758 | 2159 | } |
| 1759 | 2160 | let count = try_lex_fortsh_file(path).unwrap(); |
| 1760 | - assert!(count > 100, "expected 100+ tokens from types.f90, got {}", count); | |
| 2161 | + assert!( | |
| 2162 | + count > 100, | |
| 2163 | + "expected 100+ tokens from types.f90, got {}", | |
| 2164 | + count | |
| 2165 | + ); | |
| 1761 | 2166 | } |
| 1762 | 2167 | |
| 1763 | 2168 | #[test] |
| 1764 | 2169 | fn tokenize_fortsh_error_handling() { |
| 1765 | - let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../fortsh/src/common/error_handling.f90"); | |
| 1766 | - if !std::path::Path::new(path).exists() { return; } | |
| 2170 | + let path = concat!( | |
| 2171 | + env!("CARGO_MANIFEST_DIR"), | |
| 2172 | + "/../fortsh/src/common/error_handling.f90" | |
| 2173 | + ); | |
| 2174 | + if !std::path::Path::new(path).exists() { | |
| 2175 | + return; | |
| 2176 | + } | |
| 1767 | 2177 | let count = try_lex_fortsh_file(path).unwrap(); |
| 1768 | 2178 | assert!(count > 50, "expected 50+ tokens, got {}", count); |
| 1769 | 2179 | } |
@@ -1772,7 +2182,9 @@ end program hello | ||
| 1772 | 2182 | fn tokenize_fortsh_all_common() { |
| 1773 | 2183 | let common_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/../fortsh/src/common"); |
| 1774 | 2184 | let dir = std::path::Path::new(common_dir); |
| 1775 | - if !dir.exists() { return; } | |
| 2185 | + if !dir.exists() { | |
| 2186 | + return; | |
| 2187 | + } | |
| 1776 | 2188 | |
| 1777 | 2189 | let mut files_tested = 0; |
| 1778 | 2190 | let mut total_tokens = 0; |
@@ -1791,20 +2203,30 @@ end program hello | ||
| 1791 | 2203 | } |
| 1792 | 2204 | } |
| 1793 | 2205 | assert!(files_tested > 0, "no .f90 files found in fortsh/src/common"); |
| 1794 | - eprintln!("tokenized {} fortsh common/ files, {} total tokens", files_tested, total_tokens); | |
| 2206 | + eprintln!( | |
| 2207 | + "tokenized {} fortsh common/ files, {} total tokens", | |
| 2208 | + files_tested, total_tokens | |
| 2209 | + ); | |
| 1795 | 2210 | } |
| 1796 | 2211 | |
| 1797 | 2212 | #[test] |
| 1798 | 2213 | fn tokenize_fortsh_all_sources() { |
| 1799 | 2214 | let src_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/../fortsh/src"); |
| 1800 | 2215 | let root = std::path::Path::new(src_dir); |
| 1801 | - if !root.exists() { return; } | |
| 2216 | + if !root.exists() { | |
| 2217 | + return; | |
| 2218 | + } | |
| 1802 | 2219 | |
| 1803 | 2220 | let mut files_tested = 0; |
| 1804 | 2221 | let mut total_tokens = 0; |
| 1805 | 2222 | let mut failures = Vec::new(); |
| 1806 | 2223 | |
| 1807 | - fn visit_dir(dir: &std::path::Path, files_tested: &mut usize, total_tokens: &mut usize, failures: &mut Vec<String>) { | |
| 2224 | + fn visit_dir( | |
| 2225 | + dir: &std::path::Path, | |
| 2226 | + files_tested: &mut usize, | |
| 2227 | + total_tokens: &mut usize, | |
| 2228 | + failures: &mut Vec<String>, | |
| 2229 | + ) { | |
| 1808 | 2230 | if let Ok(entries) = std::fs::read_dir(dir) { |
| 1809 | 2231 | for entry in entries.flatten() { |
| 1810 | 2232 | let path = entry.path(); |
@@ -1827,11 +2249,22 @@ end program hello | ||
| 1827 | 2249 | visit_dir(root, &mut files_tested, &mut total_tokens, &mut failures); |
| 1828 | 2250 | |
| 1829 | 2251 | if !failures.is_empty() { |
| 1830 | - panic!("{} of {} files failed to tokenize:\n{}", failures.len(), files_tested + failures.len(), | |
| 1831 | - failures.join("\n")); | |
| 2252 | + panic!( | |
| 2253 | + "{} of {} files failed to tokenize:\n{}", | |
| 2254 | + failures.len(), | |
| 2255 | + files_tested + failures.len(), | |
| 2256 | + failures.join("\n") | |
| 2257 | + ); | |
| 1832 | 2258 | } |
| 1833 | 2259 | |
| 1834 | - assert!(files_tested > 40, "expected 40+ .f90 files, found {}", files_tested); | |
| 1835 | - eprintln!("tokenized ALL {} fortsh .f90 files, {} total tokens", files_tested, total_tokens); | |
| 2260 | + assert!( | |
| 2261 | + files_tested > 40, | |
| 2262 | + "expected 40+ .f90 files, found {}", | |
| 2263 | + files_tested | |
| 2264 | + ); | |
| 2265 | + eprintln!( | |
| 2266 | + "tokenized ALL {} fortsh .f90 files, {} total tokens", | |
| 2267 | + files_tested, total_tokens | |
| 2268 | + ); | |
| 1836 | 2269 | } |
| 1837 | 2270 | } |
src/opt/alias.rsmodified@@ -13,9 +13,9 @@ | ||
| 13 | 13 | //! Used by GVN (to determine if a store kills a prior load's value) |
| 14 | 14 | //! cross-block load-store forwarding, and LICM load hoisting. |
| 15 | 15 | |
| 16 | +use super::loop_utils::resolve_const_int; | |
| 16 | 17 | use crate::ir::inst::*; |
| 17 | 18 | use crate::ir::types::IrType; |
| 18 | -use super::loop_utils::resolve_const_int; | |
| 19 | 19 | |
| 20 | 20 | /// Result of an alias query between two pointer values. |
| 21 | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -71,7 +71,9 @@ pub fn may_reach_through_call_arg(func: &Function, entry_ptr: ValueId, call_arg: | ||
| 71 | 71 | /// GlobalAddr, GetElementPtr, or function parameters). |
| 72 | 72 | pub fn query(func: &Function, a: ValueId, b: ValueId) -> AliasResult { |
| 73 | 73 | // Same value → must alias. |
| 74 | - if a == b { return AliasResult::MustAlias; } | |
| 74 | + if a == b { | |
| 75 | + return AliasResult::MustAlias; | |
| 76 | + } | |
| 75 | 77 | |
| 76 | 78 | // Trace both pointers to their base + offset. |
| 77 | 79 | let base_a = trace_base(func, a); |
@@ -80,10 +82,14 @@ pub fn query(func: &Function, a: ValueId, b: ValueId) -> AliasResult { | ||
| 80 | 82 | // Different base pointers → no alias (Fortran guarantee). |
| 81 | 83 | match (&base_a, &base_b) { |
| 82 | 84 | (PtrBase::Alloca(id_a), PtrBase::Alloca(id_b)) => { |
| 83 | - if id_a != id_b { return AliasResult::NoAlias; } | |
| 85 | + if id_a != id_b { | |
| 86 | + return AliasResult::NoAlias; | |
| 87 | + } | |
| 84 | 88 | } |
| 85 | 89 | (PtrBase::Global(name_a), PtrBase::Global(name_b)) => { |
| 86 | - if name_a != name_b { return AliasResult::NoAlias; } | |
| 90 | + if name_a != name_b { | |
| 91 | + return AliasResult::NoAlias; | |
| 92 | + } | |
| 87 | 93 | } |
| 88 | 94 | (PtrBase::Param(id_a), PtrBase::Param(id_b)) => { |
| 89 | 95 | if id_a != id_b |
@@ -93,10 +99,10 @@ pub fn query(func: &Function, a: ValueId, b: ValueId) -> AliasResult { | ||
| 93 | 99 | return AliasResult::NoAlias; |
| 94 | 100 | } |
| 95 | 101 | } |
| 96 | - (PtrBase::Alloca(_), PtrBase::Global(_)) | | |
| 97 | - (PtrBase::Global(_), PtrBase::Alloca(_)) | | |
| 98 | - (PtrBase::Alloca(_), PtrBase::Param(_)) | | |
| 99 | - (PtrBase::Param(_), PtrBase::Alloca(_)) => { | |
| 102 | + (PtrBase::Alloca(_), PtrBase::Global(_)) | |
| 103 | + | (PtrBase::Global(_), PtrBase::Alloca(_)) | |
| 104 | + | (PtrBase::Alloca(_), PtrBase::Param(_)) | |
| 105 | + | (PtrBase::Param(_), PtrBase::Alloca(_)) => { | |
| 100 | 106 | return AliasResult::NoAlias; |
| 101 | 107 | } |
| 102 | 108 | _ => {} |
@@ -150,7 +156,9 @@ impl PtrBase { | ||
| 150 | 156 | fn trace_base(func: &Function, ptr: ValueId) -> PtrBase { |
| 151 | 157 | // Check if this is a function parameter (pointer arg). |
| 152 | 158 | for param in &func.params { |
| 153 | - if param.id == ptr { return PtrBase::Param(ptr); } | |
| 159 | + if param.id == ptr { | |
| 160 | + return PtrBase::Param(ptr); | |
| 161 | + } | |
| 154 | 162 | } |
| 155 | 163 | |
| 156 | 164 | // Find the defining instruction. |
@@ -180,7 +188,9 @@ fn trace_offset(func: &Function, ptr: ValueId) -> Option<i64> { | ||
| 180 | 188 | InstKind::Load(addr) => trace_param_wrapper(func, *addr).map(|_| 0), |
| 181 | 189 | InstKind::GetElementPtr(base, indices) => { |
| 182 | 190 | let base_offset = trace_offset(func, *base)?; |
| 183 | - if indices.len() != 1 { return None; } | |
| 191 | + if indices.len() != 1 { | |
| 192 | + return None; | |
| 193 | + } | |
| 184 | 194 | let idx = resolve_const_int(func, indices[0])?; |
| 185 | 195 | let step = match &inst.ty { |
| 186 | 196 | IrType::Ptr(inner) => inner.size_bytes() as i64, |
@@ -236,7 +246,9 @@ fn trace_param_wrapper(func: &Function, addr: ValueId) -> Option<ValueId> { | ||
| 236 | 246 | fn find_inst(func: &Function, vid: ValueId) -> Option<&Inst> { |
| 237 | 247 | for block in &func.blocks { |
| 238 | 248 | for inst in &block.insts { |
| 239 | - if inst.id == vid { return Some(inst); } | |
| 249 | + if inst.id == vid { | |
| 250 | + return Some(inst); | |
| 251 | + } | |
| 240 | 252 | } |
| 241 | 253 | } |
| 242 | 254 | None |
@@ -249,12 +261,16 @@ fn find_inst(func: &Function, vid: ValueId) -> Option<&Inst> { | ||
| 249 | 261 | #[cfg(test)] |
| 250 | 262 | mod tests { |
| 251 | 263 | use super::*; |
| 252 | - use crate::ir::types::{IrType, IntWidth}; | |
| 253 | - use crate::lexer::{Span, Position}; | |
| 264 | + use crate::ir::types::{IntWidth, IrType}; | |
| 265 | + use crate::lexer::{Position, Span}; | |
| 254 | 266 | |
| 255 | 267 | fn span() -> Span { |
| 256 | 268 | let pos = Position { line: 0, col: 0 }; |
| 257 | - Span { file_id: 0, start: pos, end: pos } | |
| 269 | + Span { | |
| 270 | + file_id: 0, | |
| 271 | + start: pos, | |
| 272 | + end: pos, | |
| 273 | + } | |
| 258 | 274 | } |
| 259 | 275 | |
| 260 | 276 | fn param(name: &str, id: u32, ty: IrType, fortran_noalias: bool) -> Param { |
@@ -272,13 +288,17 @@ mod tests { | ||
| 272 | 288 | let a = f.next_value_id(); |
| 273 | 289 | f.register_type(a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 274 | 290 | f.block_mut(f.entry).insts.push(Inst { |
| 275 | - id: a, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 291 | + id: a, | |
| 292 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 293 | + span: span(), | |
| 276 | 294 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 277 | 295 | }); |
| 278 | 296 | let b = f.next_value_id(); |
| 279 | 297 | f.register_type(b, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 280 | 298 | f.block_mut(f.entry).insts.push(Inst { |
| 281 | - id: b, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 299 | + id: b, | |
| 300 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 301 | + span: span(), | |
| 282 | 302 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 283 | 303 | }); |
| 284 | 304 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -292,7 +312,9 @@ mod tests { | ||
| 292 | 312 | let a = f.next_value_id(); |
| 293 | 313 | f.register_type(a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 294 | 314 | f.block_mut(f.entry).insts.push(Inst { |
| 295 | - id: a, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 315 | + id: a, | |
| 316 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 317 | + span: span(), | |
| 296 | 318 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 297 | 319 | }); |
| 298 | 320 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -351,33 +373,43 @@ mod tests { | ||
| 351 | 373 | let base = f.next_value_id(); |
| 352 | 374 | f.register_type(base, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 353 | 375 | f.block_mut(f.entry).insts.push(Inst { |
| 354 | - id: base, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 376 | + id: base, | |
| 377 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 378 | + span: span(), | |
| 355 | 379 | kind: InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10)), |
| 356 | 380 | }); |
| 357 | 381 | // GEP at offset 0 |
| 358 | 382 | let c0 = f.next_value_id(); |
| 359 | 383 | f.register_type(c0, IrType::Int(IntWidth::I64)); |
| 360 | 384 | f.block_mut(f.entry).insts.push(Inst { |
| 361 | - id: c0, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 385 | + id: c0, | |
| 386 | + ty: IrType::Int(IntWidth::I64), | |
| 387 | + span: span(), | |
| 362 | 388 | kind: InstKind::ConstInt(0, IntWidth::I64), |
| 363 | 389 | }); |
| 364 | 390 | let gep0 = f.next_value_id(); |
| 365 | 391 | f.register_type(gep0, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 366 | 392 | f.block_mut(f.entry).insts.push(Inst { |
| 367 | - id: gep0, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 393 | + id: gep0, | |
| 394 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 395 | + span: span(), | |
| 368 | 396 | kind: InstKind::GetElementPtr(base, vec![c0]), |
| 369 | 397 | }); |
| 370 | 398 | // GEP at offset 1 |
| 371 | 399 | let c1 = f.next_value_id(); |
| 372 | 400 | f.register_type(c1, IrType::Int(IntWidth::I64)); |
| 373 | 401 | f.block_mut(f.entry).insts.push(Inst { |
| 374 | - id: c1, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 402 | + id: c1, | |
| 403 | + ty: IrType::Int(IntWidth::I64), | |
| 404 | + span: span(), | |
| 375 | 405 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 376 | 406 | }); |
| 377 | 407 | let gep1 = f.next_value_id(); |
| 378 | 408 | f.register_type(gep1, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 379 | 409 | f.block_mut(f.entry).insts.push(Inst { |
| 380 | - id: gep1, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 410 | + id: gep1, | |
| 411 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 412 | + span: span(), | |
| 381 | 413 | kind: InstKind::GetElementPtr(base, vec![c1]), |
| 382 | 414 | }); |
| 383 | 415 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -389,10 +421,19 @@ mod tests { | ||
| 389 | 421 | fn aggregate_base_and_element_gep_may_alias() { |
| 390 | 422 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 391 | 423 | let base = f.next_value_id(); |
| 392 | - f.register_type(base, IrType::Ptr(Box::new(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10)))); | |
| 424 | + f.register_type( | |
| 425 | + base, | |
| 426 | + IrType::Ptr(Box::new(IrType::Array( | |
| 427 | + Box::new(IrType::Int(IntWidth::I32)), | |
| 428 | + 10, | |
| 429 | + ))), | |
| 430 | + ); | |
| 393 | 431 | f.block_mut(f.entry).insts.push(Inst { |
| 394 | 432 | id: base, |
| 395 | - ty: IrType::Ptr(Box::new(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10))), | |
| 433 | + ty: IrType::Ptr(Box::new(IrType::Array( | |
| 434 | + Box::new(IrType::Int(IntWidth::I32)), | |
| 435 | + 10, | |
| 436 | + ))), | |
| 396 | 437 | span: span(), |
| 397 | 438 | kind: InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10)), |
| 398 | 439 | }); |
@@ -425,13 +466,17 @@ mod tests { | ||
| 425 | 466 | let ga = f.next_value_id(); |
| 426 | 467 | f.register_type(ga, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 427 | 468 | f.block_mut(f.entry).insts.push(Inst { |
| 428 | - id: ga, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 469 | + id: ga, | |
| 470 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 471 | + span: span(), | |
| 429 | 472 | kind: InstKind::GlobalAddr("array_a".into()), |
| 430 | 473 | }); |
| 431 | 474 | let gb = f.next_value_id(); |
| 432 | 475 | f.register_type(gb, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 433 | 476 | f.block_mut(f.entry).insts.push(Inst { |
| 434 | - id: gb, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 477 | + id: gb, | |
| 478 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 479 | + span: span(), | |
| 435 | 480 | kind: InstKind::GlobalAddr("array_b".into()), |
| 436 | 481 | }); |
| 437 | 482 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -442,8 +487,18 @@ mod tests { | ||
| 442 | 487 | #[test] |
| 443 | 488 | fn distinct_noalias_params_do_not_alias() { |
| 444 | 489 | let params = vec![ |
| 445 | - param("a", 0, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), true), | |
| 446 | - param("b", 1, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), true), | |
| 490 | + param( | |
| 491 | + "a", | |
| 492 | + 0, | |
| 493 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 494 | + true, | |
| 495 | + ), | |
| 496 | + param( | |
| 497 | + "b", | |
| 498 | + 1, | |
| 499 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 500 | + true, | |
| 501 | + ), | |
| 447 | 502 | ]; |
| 448 | 503 | let mut f = Function::new("test".into(), params, IrType::Void); |
| 449 | 504 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -454,13 +509,26 @@ mod tests { | ||
| 454 | 509 | #[test] |
| 455 | 510 | fn wrapper_loads_trace_back_to_noalias_params() { |
| 456 | 511 | let params = vec![ |
| 457 | - param("a", 0, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), true), | |
| 458 | - param("b", 1, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), true), | |
| 512 | + param( | |
| 513 | + "a", | |
| 514 | + 0, | |
| 515 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 516 | + true, | |
| 517 | + ), | |
| 518 | + param( | |
| 519 | + "b", | |
| 520 | + 1, | |
| 521 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 522 | + true, | |
| 523 | + ), | |
| 459 | 524 | ]; |
| 460 | 525 | let mut f = Function::new("test".into(), params, IrType::Void); |
| 461 | 526 | |
| 462 | 527 | let slot_a = f.next_value_id(); |
| 463 | - f.register_type(slot_a, IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))))); | |
| 528 | + f.register_type( | |
| 529 | + slot_a, | |
| 530 | + IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), | |
| 531 | + ); | |
| 464 | 532 | f.block_mut(f.entry).insts.push(Inst { |
| 465 | 533 | id: slot_a, |
| 466 | 534 | ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
@@ -468,7 +536,10 @@ mod tests { | ||
| 468 | 536 | kind: InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 469 | 537 | }); |
| 470 | 538 | let slot_b = f.next_value_id(); |
| 471 | - f.register_type(slot_b, IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))))); | |
| 539 | + f.register_type( | |
| 540 | + slot_b, | |
| 541 | + IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), | |
| 542 | + ); | |
| 472 | 543 | f.block_mut(f.entry).insts.push(Inst { |
| 473 | 544 | id: slot_b, |
| 474 | 545 | ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
src/opt/audit_tests.rsmodified@@ -6,26 +6,35 @@ | ||
| 6 | 6 | |
| 7 | 7 | #![cfg(test)] |
| 8 | 8 | |
| 9 | -use crate::ir::inst::*; | |
| 10 | -use crate::ir::types::{IrType, IntWidth, FloatWidth}; | |
| 11 | -use crate::lexer::{Span, Position}; | |
| 12 | -use crate::ir::verify::verify_module; | |
| 13 | -use super::pass::Pass; | |
| 14 | 9 | use super::const_fold::ConstFold; |
| 15 | 10 | use super::const_prop::ConstProp; |
| 16 | 11 | use super::dce::Dce; |
| 17 | -use super::strength_reduce::StrengthReduce; | |
| 18 | 12 | use super::licm::Licm; |
| 13 | +use super::pass::Pass; | |
| 14 | +use super::strength_reduce::StrengthReduce; | |
| 15 | +use crate::ir::inst::*; | |
| 16 | +use crate::ir::types::{FloatWidth, IntWidth, IrType}; | |
| 17 | +use crate::ir::verify::verify_module; | |
| 18 | +use crate::lexer::{Position, Span}; | |
| 19 | 19 | |
| 20 | 20 | fn dummy_span() -> Span { |
| 21 | 21 | let p = Position { line: 1, col: 1 }; |
| 22 | - Span { start: p, end: p, file_id: 0 } | |
| 22 | + Span { | |
| 23 | + start: p, | |
| 24 | + end: p, | |
| 25 | + file_id: 0, | |
| 26 | + } | |
| 23 | 27 | } |
| 24 | 28 | |
| 25 | 29 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 26 | 30 | let id = f.next_value_id(); |
| 27 | 31 | let entry = f.entry; |
| 28 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 32 | + f.block_mut(entry).insts.push(Inst { | |
| 33 | + id, | |
| 34 | + kind, | |
| 35 | + ty, | |
| 36 | + span: dummy_span(), | |
| 37 | + }); | |
| 29 | 38 | id |
| 30 | 39 | } |
| 31 | 40 | |
@@ -41,18 +50,33 @@ fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { | ||
| 41 | 50 | fn audit_const_fold_int_to_f32_must_round() { |
| 42 | 51 | let mut m = Module::new("t".into()); |
| 43 | 52 | let mut f = Function::new("f".into(), vec![], IrType::Float(FloatWidth::F32)); |
| 44 | - let i = push(&mut f, InstKind::ConstInt(16_777_217, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 45 | - let cv = push(&mut f, InstKind::IntToFloat(i, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 53 | + let i = push( | |
| 54 | + &mut f, | |
| 55 | + InstKind::ConstInt(16_777_217, IntWidth::I32), | |
| 56 | + IrType::Int(IntWidth::I32), | |
| 57 | + ); | |
| 58 | + let cv = push( | |
| 59 | + &mut f, | |
| 60 | + InstKind::IntToFloat(i, FloatWidth::F32), | |
| 61 | + IrType::Float(FloatWidth::F32), | |
| 62 | + ); | |
| 46 | 63 | let entry = f.entry; |
| 47 | 64 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(cv))); |
| 48 | 65 | m.add_function(f); |
| 49 | 66 | |
| 50 | 67 | ConstFold.run(&mut m); |
| 51 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == cv).unwrap(); | |
| 68 | + let folded = m.functions[0].blocks[0] | |
| 69 | + .insts | |
| 70 | + .iter() | |
| 71 | + .find(|i| i.id == cv) | |
| 72 | + .unwrap(); | |
| 52 | 73 | match folded.kind { |
| 53 | 74 | InstKind::ConstFloat(v, FloatWidth::F32) => { |
| 54 | - assert_eq!(v, 16_777_216.0, | |
| 55 | - "expected f32-precision result 16_777_216.0, got {}", v); | |
| 75 | + assert_eq!( | |
| 76 | + v, 16_777_216.0, | |
| 77 | + "expected f32-precision result 16_777_216.0, got {}", | |
| 78 | + v | |
| 79 | + ); | |
| 56 | 80 | } |
| 57 | 81 | ref other => panic!("expected ConstFloat, got {:?}", other), |
| 58 | 82 | } |
@@ -67,14 +91,26 @@ fn audit_const_fold_int_to_f32_must_round() { | ||
| 67 | 91 | fn audit_const_fold_float_trunc_must_round() { |
| 68 | 92 | let mut m = Module::new("t".into()); |
| 69 | 93 | let mut f = Function::new("f".into(), vec![], IrType::Float(FloatWidth::F32)); |
| 70 | - let d = push(&mut f, InstKind::ConstFloat(16_777_217.0, FloatWidth::F64), IrType::Float(FloatWidth::F64)); | |
| 71 | - let cv = push(&mut f, InstKind::FloatTrunc(d, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 94 | + let d = push( | |
| 95 | + &mut f, | |
| 96 | + InstKind::ConstFloat(16_777_217.0, FloatWidth::F64), | |
| 97 | + IrType::Float(FloatWidth::F64), | |
| 98 | + ); | |
| 99 | + let cv = push( | |
| 100 | + &mut f, | |
| 101 | + InstKind::FloatTrunc(d, FloatWidth::F32), | |
| 102 | + IrType::Float(FloatWidth::F32), | |
| 103 | + ); | |
| 72 | 104 | let entry = f.entry; |
| 73 | 105 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(cv))); |
| 74 | 106 | m.add_function(f); |
| 75 | 107 | |
| 76 | 108 | ConstFold.run(&mut m); |
| 77 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == cv).unwrap(); | |
| 109 | + let folded = m.functions[0].blocks[0] | |
| 110 | + .insts | |
| 111 | + .iter() | |
| 112 | + .find(|i| i.id == cv) | |
| 113 | + .unwrap(); | |
| 78 | 114 | match folded.kind { |
| 79 | 115 | InstKind::ConstFloat(v, FloatWidth::F32) => { |
| 80 | 116 | assert_eq!(v, 16_777_216.0, "expected f32-rounded value, got {}", v); |
@@ -93,13 +129,37 @@ fn audit_const_fold_float_trunc_must_round() { | ||
| 93 | 129 | fn audit_strength_reduce_chained_identities() { |
| 94 | 130 | let mut m = Module::new("t".into()); |
| 95 | 131 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 96 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 97 | - let one1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 132 | + let x = push( | |
| 133 | + &mut f, | |
| 134 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 135 | + IrType::Int(IntWidth::I32), | |
| 136 | + ); | |
| 137 | + let one1 = push( | |
| 138 | + &mut f, | |
| 139 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 140 | + IrType::Int(IntWidth::I32), | |
| 141 | + ); | |
| 98 | 142 | let mul1 = push(&mut f, InstKind::IMul(x, one1), IrType::Int(IntWidth::I32)); |
| 99 | - let one2 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 100 | - let mul2 = push(&mut f, InstKind::IMul(mul1, one2), IrType::Int(IntWidth::I32)); | |
| 101 | - let zero = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 102 | - let add = push(&mut f, InstKind::IAdd(mul2, zero), IrType::Int(IntWidth::I32)); | |
| 143 | + let one2 = push( | |
| 144 | + &mut f, | |
| 145 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 146 | + IrType::Int(IntWidth::I32), | |
| 147 | + ); | |
| 148 | + let mul2 = push( | |
| 149 | + &mut f, | |
| 150 | + InstKind::IMul(mul1, one2), | |
| 151 | + IrType::Int(IntWidth::I32), | |
| 152 | + ); | |
| 153 | + let zero = push( | |
| 154 | + &mut f, | |
| 155 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 156 | + IrType::Int(IntWidth::I32), | |
| 157 | + ); | |
| 158 | + let add = push( | |
| 159 | + &mut f, | |
| 160 | + InstKind::IAdd(mul2, zero), | |
| 161 | + IrType::Int(IntWidth::I32), | |
| 162 | + ); | |
| 103 | 163 | let entry = f.entry; |
| 104 | 164 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(add))); |
| 105 | 165 | m.add_function(f); |
@@ -108,13 +168,18 @@ fn audit_strength_reduce_chained_identities() { | ||
| 108 | 168 | // After three chained identities, the return must reference x. |
| 109 | 169 | let term = m.functions[0].blocks[0].terminator.as_ref().unwrap(); |
| 110 | 170 | match term { |
| 111 | - Terminator::Return(Some(v)) => assert_eq!(*v, x, | |
| 112 | - "expected return %{} (x), got %{}", x.0, v.0), | |
| 171 | + Terminator::Return(Some(v)) => { | |
| 172 | + assert_eq!(*v, x, "expected return %{} (x), got %{}", x.0, v.0) | |
| 173 | + } | |
| 113 | 174 | other => panic!("expected Return(x), got {:?}", other), |
| 114 | 175 | } |
| 115 | 176 | // And the IR must still verify. |
| 116 | 177 | let errs = verify_module(&m); |
| 117 | - assert!(errs.is_empty(), "verifier errors after chained rewrites: {:?}", errs); | |
| 178 | + assert!( | |
| 179 | + errs.is_empty(), | |
| 180 | + "verifier errors after chained rewrites: {:?}", | |
| 181 | + errs | |
| 182 | + ); | |
| 118 | 183 | } |
| 119 | 184 | |
| 120 | 185 | // ============================================================= |
@@ -131,11 +196,27 @@ fn audit_strength_reduce_mixed_shl_and_identity_in_block() { | ||
| 131 | 196 | // ret %4 |
| 132 | 197 | let mut m = Module::new("t".into()); |
| 133 | 198 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 134 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 135 | - let eight = push(&mut f, InstKind::ConstInt(8, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 199 | + let x = push( | |
| 200 | + &mut f, | |
| 201 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 202 | + IrType::Int(IntWidth::I32), | |
| 203 | + ); | |
| 204 | + let eight = push( | |
| 205 | + &mut f, | |
| 206 | + InstKind::ConstInt(8, IntWidth::I32), | |
| 207 | + IrType::Int(IntWidth::I32), | |
| 208 | + ); | |
| 136 | 209 | let mul1 = push(&mut f, InstKind::IMul(x, eight), IrType::Int(IntWidth::I32)); |
| 137 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 138 | - let mul2 = push(&mut f, InstKind::IMul(mul1, one), IrType::Int(IntWidth::I32)); | |
| 210 | + let one = push( | |
| 211 | + &mut f, | |
| 212 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 213 | + IrType::Int(IntWidth::I32), | |
| 214 | + ); | |
| 215 | + let mul2 = push( | |
| 216 | + &mut f, | |
| 217 | + InstKind::IMul(mul1, one), | |
| 218 | + IrType::Int(IntWidth::I32), | |
| 219 | + ); | |
| 139 | 220 | let entry = f.entry; |
| 140 | 221 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(mul2))); |
| 141 | 222 | m.add_function(f); |
@@ -151,8 +232,11 @@ fn audit_strength_reduce_mixed_shl_and_identity_in_block() { | ||
| 151 | 232 | _ => panic!(), |
| 152 | 233 | }; |
| 153 | 234 | let term_kind = &block.insts.iter().find(|i| i.id == term_val).unwrap().kind; |
| 154 | - assert!(matches!(term_kind, InstKind::Shl(..)), | |
| 155 | - "expected return value to define a Shl, got {:?}", term_kind); | |
| 235 | + assert!( | |
| 236 | + matches!(term_kind, InstKind::Shl(..)), | |
| 237 | + "expected return value to define a Shl, got {:?}", | |
| 238 | + term_kind | |
| 239 | + ); | |
| 156 | 240 | } |
| 157 | 241 | |
| 158 | 242 | // ============================================================= |
@@ -165,20 +249,33 @@ fn audit_strength_reduce_mixed_shl_and_identity_in_block() { | ||
| 165 | 249 | fn audit_const_fold_idiv_i8_min_neg_one() { |
| 166 | 250 | let mut m = Module::new("t".into()); |
| 167 | 251 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I8)); |
| 168 | - let a = push(&mut f, InstKind::ConstInt(-128, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 169 | - let b = push(&mut f, InstKind::ConstInt(-1, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 252 | + let a = push( | |
| 253 | + &mut f, | |
| 254 | + InstKind::ConstInt(-128, IntWidth::I8), | |
| 255 | + IrType::Int(IntWidth::I8), | |
| 256 | + ); | |
| 257 | + let b = push( | |
| 258 | + &mut f, | |
| 259 | + InstKind::ConstInt(-1, IntWidth::I8), | |
| 260 | + IrType::Int(IntWidth::I8), | |
| 261 | + ); | |
| 170 | 262 | let q = push(&mut f, InstKind::IDiv(a, b), IrType::Int(IntWidth::I8)); |
| 171 | 263 | let entry = f.entry; |
| 172 | 264 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(q))); |
| 173 | 265 | m.add_function(f); |
| 174 | 266 | |
| 175 | 267 | ConstFold.run(&mut m); |
| 176 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == q).unwrap(); | |
| 268 | + let folded = m.functions[0].blocks[0] | |
| 269 | + .insts | |
| 270 | + .iter() | |
| 271 | + .find(|i| i.id == q) | |
| 272 | + .unwrap(); | |
| 177 | 273 | // Either we fold to -128 (match hardware), or we leave the IDiv alone. |
| 178 | 274 | // Anything else would be a divergence. |
| 179 | 275 | match folded.kind { |
| 180 | - InstKind::ConstInt(v, IntWidth::I8) => assert_eq!(v, -128, | |
| 181 | - "expected -128 (i8 wraparound), got {}", v), | |
| 276 | + InstKind::ConstInt(v, IntWidth::I8) => { | |
| 277 | + assert_eq!(v, -128, "expected -128 (i8 wraparound), got {}", v) | |
| 278 | + } | |
| 182 | 279 | InstKind::IDiv(..) => { /* left alone, also acceptable */ } |
| 183 | 280 | ref other => panic!("unexpected fold: {:?}", other), |
| 184 | 281 | } |
@@ -198,7 +295,11 @@ fn audit_dce_removes_dead_block_param() { | ||
| 198 | 295 | id: param_id, |
| 199 | 296 | ty: IrType::Int(IntWidth::I32), |
| 200 | 297 | }); |
| 201 | - let init = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 298 | + let init = push( | |
| 299 | + &mut f, | |
| 300 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 301 | + IrType::Int(IntWidth::I32), | |
| 302 | + ); | |
| 202 | 303 | let entry = f.entry; |
| 203 | 304 | f.block_mut(entry).terminator = Some(Terminator::Branch(target, vec![init])); |
| 204 | 305 | f.block_mut(target).terminator = Some(Terminator::Return(None)); |
@@ -206,19 +307,31 @@ fn audit_dce_removes_dead_block_param() { | ||
| 206 | 307 | |
| 207 | 308 | assert!(Dce.run(&mut m), "DCE should report change"); |
| 208 | 309 | let f = &m.functions[0]; |
| 209 | - assert_eq!(f.block(target).params.len(), 0, "dead block param must be removed"); | |
| 310 | + assert_eq!( | |
| 311 | + f.block(target).params.len(), | |
| 312 | + 0, | |
| 313 | + "dead block param must be removed" | |
| 314 | + ); | |
| 210 | 315 | // The predecessor's branch arg list must shrink in lockstep. |
| 211 | 316 | let entry_term = f.block(f.entry).terminator.as_ref().unwrap(); |
| 212 | 317 | match entry_term { |
| 213 | - Terminator::Branch(_, args) => assert_eq!(args.len(), 0, | |
| 214 | - "predecessor branch arg must be dropped alongside the dead param"), | |
| 318 | + Terminator::Branch(_, args) => assert_eq!( | |
| 319 | + args.len(), | |
| 320 | + 0, | |
| 321 | + "predecessor branch arg must be dropped alongside the dead param" | |
| 322 | + ), | |
| 215 | 323 | _ => panic!(), |
| 216 | 324 | } |
| 217 | 325 | // The const(0) inst is now unreferenced and should also be DCE'd. |
| 218 | - let const_remains = f.blocks.iter() | |
| 326 | + let const_remains = f | |
| 327 | + .blocks | |
| 328 | + .iter() | |
| 219 | 329 | .flat_map(|b| b.insts.iter()) |
| 220 | 330 | .any(|i| matches!(i.kind, InstKind::ConstInt(0, _))); |
| 221 | - assert!(!const_remains, "const(0) should also be DCE'd after its only use disappears"); | |
| 331 | + assert!( | |
| 332 | + !const_remains, | |
| 333 | + "const(0) should also be DCE'd after its only use disappears" | |
| 334 | + ); | |
| 222 | 335 | } |
| 223 | 336 | |
| 224 | 337 | #[test] |
@@ -232,7 +345,11 @@ fn audit_dce_keeps_live_block_param() { | ||
| 232 | 345 | id: param_id, |
| 233 | 346 | ty: IrType::Int(IntWidth::I32), |
| 234 | 347 | }); |
| 235 | - let init = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 348 | + let init = push( | |
| 349 | + &mut f, | |
| 350 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 351 | + IrType::Int(IntWidth::I32), | |
| 352 | + ); | |
| 236 | 353 | let entry = f.entry; |
| 237 | 354 | f.block_mut(entry).terminator = Some(Terminator::Branch(target, vec![init])); |
| 238 | 355 | f.block_mut(target).terminator = Some(Terminator::Return(Some(param_id))); |
@@ -240,7 +357,11 @@ fn audit_dce_keeps_live_block_param() { | ||
| 240 | 357 | |
| 241 | 358 | Dce.run(&mut m); |
| 242 | 359 | let f = &m.functions[0]; |
| 243 | - assert_eq!(f.block(target).params.len(), 1, "live block param must survive"); | |
| 360 | + assert_eq!( | |
| 361 | + f.block(target).params.len(), | |
| 362 | + 1, | |
| 363 | + "live block param must survive" | |
| 364 | + ); | |
| 244 | 365 | } |
| 245 | 366 | |
| 246 | 367 | #[test] |
@@ -254,10 +375,24 @@ fn audit_dce_removes_one_of_two_block_params_keeps_correct_arg() { | ||
| 254 | 375 | let target = f.create_block("target"); |
| 255 | 376 | let p0 = f.next_value_id(); |
| 256 | 377 | let p1 = f.next_value_id(); |
| 257 | - f.block_mut(target).params.push(BlockParam { id: p0, ty: IrType::Int(IntWidth::I32) }); | |
| 258 | - f.block_mut(target).params.push(BlockParam { id: p1, ty: IrType::Int(IntWidth::I32) }); | |
| 259 | - let c0 = push(&mut f, InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 260 | - let c1 = push(&mut f, InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 378 | + f.block_mut(target).params.push(BlockParam { | |
| 379 | + id: p0, | |
| 380 | + ty: IrType::Int(IntWidth::I32), | |
| 381 | + }); | |
| 382 | + f.block_mut(target).params.push(BlockParam { | |
| 383 | + id: p1, | |
| 384 | + ty: IrType::Int(IntWidth::I32), | |
| 385 | + }); | |
| 386 | + let c0 = push( | |
| 387 | + &mut f, | |
| 388 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 389 | + IrType::Int(IntWidth::I32), | |
| 390 | + ); | |
| 391 | + let c1 = push( | |
| 392 | + &mut f, | |
| 393 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 394 | + IrType::Int(IntWidth::I32), | |
| 395 | + ); | |
| 261 | 396 | let entry = f.entry; |
| 262 | 397 | f.block_mut(entry).terminator = Some(Terminator::Branch(target, vec![c0, c1])); |
| 263 | 398 | f.block_mut(target).terminator = Some(Terminator::Return(Some(p1))); |
@@ -281,10 +416,15 @@ fn audit_dce_removes_one_of_two_block_params_keeps_correct_arg() { | ||
| 281 | 416 | |
| 282 | 417 | // Now const(10) is dead — should also be gone after the |
| 283 | 418 | // outer-loop re-runs the inner DCE. |
| 284 | - let c0_remains = f.blocks.iter() | |
| 419 | + let c0_remains = f | |
| 420 | + .blocks | |
| 421 | + .iter() | |
| 285 | 422 | .flat_map(|b| b.insts.iter()) |
| 286 | 423 | .any(|i| i.id == c0); |
| 287 | - assert!(!c0_remains, "const(10) should be DCE'd after its arg slot is gone"); | |
| 424 | + assert!( | |
| 425 | + !c0_remains, | |
| 426 | + "const(10) should be DCE'd after its arg slot is gone" | |
| 427 | + ); | |
| 288 | 428 | } |
| 289 | 429 | |
| 290 | 430 | // ============================================================= |
@@ -324,25 +464,33 @@ fn audit_dce_cascading_across_outer_iterations() { | ||
| 324 | 464 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 325 | 465 | |
| 326 | 466 | // entry: computes %c, branches to other |
| 327 | - let c = push(&mut f, InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 467 | + let c = push( | |
| 468 | + &mut f, | |
| 469 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 470 | + IrType::Int(IntWidth::I32), | |
| 471 | + ); | |
| 328 | 472 | |
| 329 | 473 | // other: has param p_other, computes %u = ineg p_other, branches to target |
| 330 | 474 | let other = f.create_block("other"); |
| 331 | 475 | let p_other = f.next_value_id(); |
| 332 | 476 | f.block_mut(other).params.push(BlockParam { |
| 333 | - id: p_other, ty: IrType::Int(IntWidth::I32), | |
| 477 | + id: p_other, | |
| 478 | + ty: IrType::Int(IntWidth::I32), | |
| 334 | 479 | }); |
| 335 | 480 | let u = f.next_value_id(); |
| 336 | 481 | f.block_mut(other).insts.push(Inst { |
| 337 | - id: u, kind: InstKind::INeg(p_other), | |
| 338 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 482 | + id: u, | |
| 483 | + kind: InstKind::INeg(p_other), | |
| 484 | + ty: IrType::Int(IntWidth::I32), | |
| 485 | + span: dummy_span(), | |
| 339 | 486 | }); |
| 340 | 487 | |
| 341 | 488 | // target: has param p_target, returns (p_target never used). |
| 342 | 489 | let target = f.create_block("target"); |
| 343 | 490 | let p_target = f.next_value_id(); |
| 344 | 491 | f.block_mut(target).params.push(BlockParam { |
| 345 | - id: p_target, ty: IrType::Int(IntWidth::I32), | |
| 492 | + id: p_target, | |
| 493 | + ty: IrType::Int(IntWidth::I32), | |
| 346 | 494 | }); |
| 347 | 495 | f.block_mut(target).terminator = Some(Terminator::Return(None)); |
| 348 | 496 | |
@@ -357,30 +505,46 @@ fn audit_dce_cascading_across_outer_iterations() { | ||
| 357 | 505 | let f = &m.functions[0]; |
| 358 | 506 | |
| 359 | 507 | // Both block params must be gone. |
| 360 | - assert_eq!(f.block(target).params.len(), 0, | |
| 361 | - "p_target should be removed in round 1"); | |
| 362 | - assert_eq!(f.block(other).params.len(), 0, | |
| 363 | - "p_other should be removed in round 2 (cascading)"); | |
| 508 | + assert_eq!( | |
| 509 | + f.block(target).params.len(), | |
| 510 | + 0, | |
| 511 | + "p_target should be removed in round 1" | |
| 512 | + ); | |
| 513 | + assert_eq!( | |
| 514 | + f.block(other).params.len(), | |
| 515 | + 0, | |
| 516 | + "p_other should be removed in round 2 (cascading)" | |
| 517 | + ); | |
| 364 | 518 | |
| 365 | 519 | // Entry's branch arg list must be empty. |
| 366 | 520 | match f.block(f.entry).terminator.as_ref().unwrap() { |
| 367 | - Terminator::Branch(_, args) => assert_eq!(args.len(), 0, | |
| 368 | - "entry's branch to other should have no args after cascade"), | |
| 521 | + Terminator::Branch(_, args) => assert_eq!( | |
| 522 | + args.len(), | |
| 523 | + 0, | |
| 524 | + "entry's branch to other should have no args after cascade" | |
| 525 | + ), | |
| 369 | 526 | _ => panic!(), |
| 370 | 527 | } |
| 371 | 528 | // other's branch to target must also have no args. |
| 372 | 529 | match f.block(other).terminator.as_ref().unwrap() { |
| 373 | - Terminator::Branch(_, args) => assert_eq!(args.len(), 0, | |
| 374 | - "other's branch to target should have no args"), | |
| 530 | + Terminator::Branch(_, args) => assert_eq!( | |
| 531 | + args.len(), | |
| 532 | + 0, | |
| 533 | + "other's branch to target should have no args" | |
| 534 | + ), | |
| 375 | 535 | _ => panic!(), |
| 376 | 536 | } |
| 377 | 537 | |
| 378 | 538 | // All formerly-live values should be DCE'd: %c, %u. |
| 379 | - let any_inst = f.blocks.iter() | |
| 539 | + let any_inst = f | |
| 540 | + .blocks | |
| 541 | + .iter() | |
| 380 | 542 | .flat_map(|b| b.insts.iter()) |
| 381 | 543 | .any(|i| i.id == c || i.id == u); |
| 382 | - assert!(!any_inst, | |
| 383 | - "dead instructions %c (const) and %u (ineg) should be DCE'd after the cascade"); | |
| 544 | + assert!( | |
| 545 | + !any_inst, | |
| 546 | + "dead instructions %c (const) and %u (ineg) should be DCE'd after the cascade" | |
| 547 | + ); | |
| 384 | 548 | } |
| 385 | 549 | |
| 386 | 550 | // ============================================================= |
@@ -471,26 +635,45 @@ fn audit_licm_nested_loop_body_computation() { | ||
| 471 | 635 | assert_eq!(loops.len(), 2, "expected exactly two natural loops"); |
| 472 | 636 | |
| 473 | 637 | // Find the inner and outer loops by their headers. |
| 474 | - let inner = loops.iter().find(|l| l.header == inner_header).expect("inner loop not found"); | |
| 475 | - let outer = loops.iter().find(|l| l.header == outer_header).expect("outer loop not found"); | |
| 638 | + let inner = loops | |
| 639 | + .iter() | |
| 640 | + .find(|l| l.header == inner_header) | |
| 641 | + .expect("inner loop not found"); | |
| 642 | + let outer = loops | |
| 643 | + .iter() | |
| 644 | + .find(|l| l.header == outer_header) | |
| 645 | + .expect("outer loop not found"); | |
| 476 | 646 | |
| 477 | 647 | // Inner loop body: {inner_header, inner_latch}. |
| 478 | - assert_eq!(inner.body.len(), 2, | |
| 479 | - "inner body should be {{inner_header, inner_latch}}, got {:?}", inner.body); | |
| 648 | + assert_eq!( | |
| 649 | + inner.body.len(), | |
| 650 | + 2, | |
| 651 | + "inner body should be {{inner_header, inner_latch}}, got {:?}", | |
| 652 | + inner.body | |
| 653 | + ); | |
| 480 | 654 | assert!(inner.body.contains(&inner_header)); |
| 481 | 655 | assert!(inner.body.contains(&inner_latch)); |
| 482 | 656 | |
| 483 | 657 | // Outer loop body: {outer_header, inner_header, inner_latch, outer_latch}. |
| 484 | 658 | // MUST NOT contain entry or exit. |
| 485 | - assert_eq!(outer.body.len(), 4, | |
| 659 | + assert_eq!( | |
| 660 | + outer.body.len(), | |
| 661 | + 4, | |
| 486 | 662 | "outer body should be {{outer_header, inner_header, inner_latch, outer_latch}}, got {:?}", |
| 487 | - outer.body); | |
| 663 | + outer.body | |
| 664 | + ); | |
| 488 | 665 | assert!(outer.body.contains(&outer_header)); |
| 489 | 666 | assert!(outer.body.contains(&inner_header)); |
| 490 | 667 | assert!(outer.body.contains(&inner_latch)); |
| 491 | 668 | assert!(outer.body.contains(&outer_latch)); |
| 492 | - assert!(!outer.body.contains(&entry), "outer body must not include entry"); | |
| 493 | - assert!(!outer.body.contains(&exit), "outer body must not include exit"); | |
| 669 | + assert!( | |
| 670 | + !outer.body.contains(&entry), | |
| 671 | + "outer body must not include entry" | |
| 672 | + ); | |
| 673 | + assert!( | |
| 674 | + !outer.body.contains(&exit), | |
| 675 | + "outer body must not include exit" | |
| 676 | + ); | |
| 494 | 677 | } |
| 495 | 678 | |
| 496 | 679 | // ============================================================= |
@@ -560,9 +743,15 @@ fn audit_licm_multi_latch_with_self_loop() { | ||
| 560 | 743 | assert_eq!(lp.header, header); |
| 561 | 744 | // Body must include header and other, NOT entry. |
| 562 | 745 | assert!(lp.body.contains(&header), "body must include header"); |
| 563 | - assert!(lp.body.contains(&other), "body must include other"); | |
| 564 | - assert!(!lp.body.contains(&entry), "body must NOT include the preheader (entry)"); | |
| 565 | - assert!(!lp.body.contains(&exit), "body must NOT include the exit block"); | |
| 746 | + assert!(lp.body.contains(&other), "body must include other"); | |
| 747 | + assert!( | |
| 748 | + !lp.body.contains(&entry), | |
| 749 | + "body must NOT include the preheader (entry)" | |
| 750 | + ); | |
| 751 | + assert!( | |
| 752 | + !lp.body.contains(&exit), | |
| 753 | + "body must NOT include the exit block" | |
| 754 | + ); | |
| 566 | 755 | // Latches: both header (self-loop) and other (back edge). |
| 567 | 756 | assert_eq!(lp.latches.len(), 2, "expected two latches"); |
| 568 | 757 | assert!(lp.latches.contains(&header)); |
@@ -581,15 +770,24 @@ fn audit_licm_multi_latch_with_self_loop() { | ||
| 581 | 770 | fn audit_licm_hoists_clean_alloca_load() { |
| 582 | 771 | let mut m = Module::new("t".into()); |
| 583 | 772 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 584 | - let slot = push(&mut f, | |
| 773 | + let slot = push( | |
| 774 | + &mut f, | |
| 585 | 775 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 586 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 587 | - let init = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 776 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 777 | + ); | |
| 778 | + let init = push( | |
| 779 | + &mut f, | |
| 780 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 781 | + IrType::Int(IntWidth::I32), | |
| 782 | + ); | |
| 588 | 783 | push(&mut f, InstKind::Store(init, slot), IrType::Void); |
| 589 | 784 | |
| 590 | 785 | let header = f.create_block("header"); |
| 591 | 786 | let i_param = f.next_value_id(); |
| 592 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 787 | + f.block_mut(header).params.push(BlockParam { | |
| 788 | + id: i_param, | |
| 789 | + ty: IrType::Int(IntWidth::I32), | |
| 790 | + }); | |
| 593 | 791 | let v = f.next_value_id(); |
| 594 | 792 | f.block_mut(header).insts.push(Inst { |
| 595 | 793 | id: v, |
@@ -627,20 +825,32 @@ fn audit_licm_hoists_clean_alloca_load() { | ||
| 627 | 825 | // The const(1) inside the header IS loop-invariant — LICM should hoist it. |
| 628 | 826 | Licm.run(&mut m); |
| 629 | 827 | let entry_block = m.functions[0].block(m.functions[0].entry); |
| 630 | - let const1_in_entry = entry_block.insts.iter() | |
| 828 | + let const1_in_entry = entry_block | |
| 829 | + .insts | |
| 830 | + .iter() | |
| 631 | 831 | .any(|i| matches!(i.kind, InstKind::ConstInt(1, IntWidth::I32))); |
| 632 | - assert!(const1_in_entry, | |
| 633 | - "LICM should have hoisted const(1) into the preheader"); | |
| 832 | + assert!( | |
| 833 | + const1_in_entry, | |
| 834 | + "LICM should have hoisted const(1) into the preheader" | |
| 835 | + ); | |
| 634 | 836 | // The load is now loop-invariant too: no loop store/call can clobber it. |
| 635 | 837 | let header_block = &m.functions[0].blocks[1]; |
| 636 | - let load_still_in_header = header_block.insts.iter() | |
| 838 | + let load_still_in_header = header_block | |
| 839 | + .insts | |
| 840 | + .iter() | |
| 637 | 841 | .any(|i| matches!(i.kind, InstKind::Load(_))); |
| 638 | - assert!(!load_still_in_header, | |
| 639 | - "LICM should hoist a load when the loop is memory-clean"); | |
| 640 | - let load_in_entry = entry_block.insts.iter() | |
| 842 | + assert!( | |
| 843 | + !load_still_in_header, | |
| 844 | + "LICM should hoist a load when the loop is memory-clean" | |
| 845 | + ); | |
| 846 | + let load_in_entry = entry_block | |
| 847 | + .insts | |
| 848 | + .iter() | |
| 641 | 849 | .any(|i| matches!(i.kind, InstKind::Load(_))); |
| 642 | - assert!(load_in_entry, | |
| 643 | - "LICM should move the memory-clean load into the preheader"); | |
| 850 | + assert!( | |
| 851 | + load_in_entry, | |
| 852 | + "LICM should move the memory-clean load into the preheader" | |
| 853 | + ); | |
| 644 | 854 | } |
| 645 | 855 | |
| 646 | 856 | // ============================================================= |
@@ -660,11 +870,18 @@ fn audit_pipeline_o2_e2e_loop_through_passmanager() { | ||
| 660 | 870 | // %d=icmp ge %i, %lim; cbr %d, exit, latch(%i2) } |
| 661 | 871 | // latch(%i_in:i32) { %1=const 1; %n=iadd %i_in, %1; br header(%n) } |
| 662 | 872 | // exit { ret } |
| 663 | - let init = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 873 | + let init = push( | |
| 874 | + &mut f, | |
| 875 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 876 | + IrType::Int(IntWidth::I32), | |
| 877 | + ); | |
| 664 | 878 | |
| 665 | 879 | let header = f.create_block("header"); |
| 666 | 880 | let i_param = f.next_value_id(); |
| 667 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 881 | + f.block_mut(header).params.push(BlockParam { | |
| 882 | + id: i_param, | |
| 883 | + ty: IrType::Int(IntWidth::I32), | |
| 884 | + }); | |
| 668 | 885 | |
| 669 | 886 | let k = f.next_value_id(); |
| 670 | 887 | f.block_mut(header).insts.push(Inst { |
@@ -697,7 +914,10 @@ fn audit_pipeline_o2_e2e_loop_through_passmanager() { | ||
| 697 | 914 | |
| 698 | 915 | let latch = f.create_block("latch"); |
| 699 | 916 | let i_in = f.next_value_id(); |
| 700 | - f.block_mut(latch).params.push(BlockParam { id: i_in, ty: IrType::Int(IntWidth::I32) }); | |
| 917 | + f.block_mut(latch).params.push(BlockParam { | |
| 918 | + id: i_in, | |
| 919 | + ty: IrType::Int(IntWidth::I32), | |
| 920 | + }); | |
| 701 | 921 | let one = f.next_value_id(); |
| 702 | 922 | f.block_mut(latch).insts.push(Inst { |
| 703 | 923 | id: one, |
@@ -736,7 +956,11 @@ fn audit_pipeline_o2_e2e_loop_through_passmanager() { | ||
| 736 | 956 | |
| 737 | 957 | // Final IR must verify. |
| 738 | 958 | let errs = verify_module(&m); |
| 739 | - assert!(errs.is_empty(), "O2 pipeline left an invalid module: {:?}", errs); | |
| 959 | + assert!( | |
| 960 | + errs.is_empty(), | |
| 961 | + "O2 pipeline left an invalid module: {:?}", | |
| 962 | + errs | |
| 963 | + ); | |
| 740 | 964 | } |
| 741 | 965 | |
| 742 | 966 | // ============================================================= |
@@ -755,7 +979,11 @@ fn audit_interaction_const_prop_then_dce_removes_orphan_const() { | ||
| 755 | 979 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 756 | 980 | let cond = push(&mut f, InstKind::ConstBool(true), IrType::Bool); |
| 757 | 981 | // This constant is ONLY used in the about-to-be-dead else block. |
| 758 | - let dead_only = push(&mut f, InstKind::ConstInt(99, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 982 | + let dead_only = push( | |
| 983 | + &mut f, | |
| 984 | + InstKind::ConstInt(99, IntWidth::I32), | |
| 985 | + IrType::Int(IntWidth::I32), | |
| 986 | + ); | |
| 759 | 987 | |
| 760 | 988 | let then_b = f.create_block("then"); |
| 761 | 989 | let else_b = f.create_block("else"); |
@@ -789,11 +1017,19 @@ fn audit_interaction_const_prop_then_dce_removes_orphan_const() { | ||
| 789 | 1017 | |
| 790 | 1018 | // After: else block gone, const(99) and ineg gone too. |
| 791 | 1019 | let f = &m.functions[0]; |
| 792 | - assert!(!f.blocks.iter().any(|b| b.id == else_b), "else block should be pruned"); | |
| 793 | - let dead_remains = f.blocks.iter() | |
| 1020 | + assert!( | |
| 1021 | + !f.blocks.iter().any(|b| b.id == else_b), | |
| 1022 | + "else block should be pruned" | |
| 1023 | + ); | |
| 1024 | + let dead_remains = f | |
| 1025 | + .blocks | |
| 1026 | + .iter() | |
| 794 | 1027 | .flat_map(|b| b.insts.iter()) |
| 795 | 1028 | .any(|i| matches!(i.kind, InstKind::ConstInt(99, _))); |
| 796 | - assert!(!dead_remains, "const(99) should be DCE'd after const_prop drops its only use"); | |
| 1029 | + assert!( | |
| 1030 | + !dead_remains, | |
| 1031 | + "const(99) should be DCE'd after const_prop drops its only use" | |
| 1032 | + ); | |
| 797 | 1033 | } |
| 798 | 1034 | |
| 799 | 1035 | // ============================================================= |
@@ -806,8 +1042,16 @@ fn audit_interaction_strength_reduce_orphans_get_dced() { | ||
| 806 | 1042 | |
| 807 | 1043 | let mut m = Module::new("t".into()); |
| 808 | 1044 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 809 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 810 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1045 | + let x = push( | |
| 1046 | + &mut f, | |
| 1047 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 1048 | + IrType::Int(IntWidth::I32), | |
| 1049 | + ); | |
| 1050 | + let one = push( | |
| 1051 | + &mut f, | |
| 1052 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1053 | + IrType::Int(IntWidth::I32), | |
| 1054 | + ); | |
| 811 | 1055 | let mul = push(&mut f, InstKind::IMul(x, one), IrType::Int(IntWidth::I32)); |
| 812 | 1056 | let entry = f.entry; |
| 813 | 1057 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(mul))); |
@@ -830,11 +1074,15 @@ fn audit_interaction_strength_reduce_orphans_get_dced() { | ||
| 830 | 1074 | assert_eq!(term_val, x, "expected return to be x directly"); |
| 831 | 1075 | |
| 832 | 1076 | // The orphan placeholder should be gone. |
| 833 | - let extra_const = f.blocks[0].insts.iter() | |
| 1077 | + let extra_const = f.blocks[0] | |
| 1078 | + .insts | |
| 1079 | + .iter() | |
| 834 | 1080 | .filter(|i| matches!(i.kind, InstKind::ConstInt(0, _))) |
| 835 | 1081 | .count(); |
| 836 | - assert_eq!(extra_const, 0, | |
| 837 | - "strength_reduce orphan placeholder Const(0) should be DCE'd"); | |
| 1082 | + assert_eq!( | |
| 1083 | + extra_const, 0, | |
| 1084 | + "strength_reduce orphan placeholder Const(0) should be DCE'd" | |
| 1085 | + ); | |
| 838 | 1086 | } |
| 839 | 1087 | |
| 840 | 1088 | // ============================================================= |
@@ -847,15 +1095,27 @@ fn audit_const_fold_shl_at_width_bails() { | ||
| 847 | 1095 | // so the runtime answer is 1. The fold MUST NOT silently emit 0. |
| 848 | 1096 | let mut m = Module::new("t".into()); |
| 849 | 1097 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 850 | - let v = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 851 | - let cnt = push(&mut f, InstKind::ConstInt(32, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1098 | + let v = push( | |
| 1099 | + &mut f, | |
| 1100 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1101 | + IrType::Int(IntWidth::I32), | |
| 1102 | + ); | |
| 1103 | + let cnt = push( | |
| 1104 | + &mut f, | |
| 1105 | + InstKind::ConstInt(32, IntWidth::I32), | |
| 1106 | + IrType::Int(IntWidth::I32), | |
| 1107 | + ); | |
| 852 | 1108 | let s = push(&mut f, InstKind::Shl(v, cnt), IrType::Int(IntWidth::I32)); |
| 853 | 1109 | let entry = f.entry; |
| 854 | 1110 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 855 | 1111 | m.add_function(f); |
| 856 | 1112 | |
| 857 | 1113 | ConstFold.run(&mut m); |
| 858 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 1114 | + let folded = m.functions[0].blocks[0] | |
| 1115 | + .insts | |
| 1116 | + .iter() | |
| 1117 | + .find(|i| i.id == s) | |
| 1118 | + .unwrap(); | |
| 859 | 1119 | // Acceptable outcomes: leave the Shl alone, OR mask the count. |
| 860 | 1120 | // NOT acceptable: ConstInt(0, _). |
| 861 | 1121 | if let InstKind::ConstInt(0, _) = folded.kind { |
@@ -870,15 +1130,27 @@ fn audit_const_fold_shl_at_width_bails() { | ||
| 870 | 1130 | fn audit_const_fold_shl_negative_count_bails() { |
| 871 | 1131 | let mut m = Module::new("t".into()); |
| 872 | 1132 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 873 | - let v = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 874 | - let cnt = push(&mut f, InstKind::ConstInt(-1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1133 | + let v = push( | |
| 1134 | + &mut f, | |
| 1135 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 1136 | + IrType::Int(IntWidth::I32), | |
| 1137 | + ); | |
| 1138 | + let cnt = push( | |
| 1139 | + &mut f, | |
| 1140 | + InstKind::ConstInt(-1, IntWidth::I32), | |
| 1141 | + IrType::Int(IntWidth::I32), | |
| 1142 | + ); | |
| 875 | 1143 | let s = push(&mut f, InstKind::Shl(v, cnt), IrType::Int(IntWidth::I32)); |
| 876 | 1144 | let entry = f.entry; |
| 877 | 1145 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 878 | 1146 | m.add_function(f); |
| 879 | 1147 | |
| 880 | 1148 | ConstFold.run(&mut m); |
| 881 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 1149 | + let folded = m.functions[0].blocks[0] | |
| 1150 | + .insts | |
| 1151 | + .iter() | |
| 1152 | + .find(|i| i.id == s) | |
| 1153 | + .unwrap(); | |
| 882 | 1154 | if let InstKind::ConstInt(0, _) = folded.kind { |
| 883 | 1155 | panic!("audit M-C: shl with negative count folded to 0 — wrong on AArch64"); |
| 884 | 1156 | } |
@@ -891,15 +1163,27 @@ fn audit_const_fold_shl_negative_count_bails() { | ||
| 891 | 1163 | fn audit_const_fold_lshr_negative_count_bails() { |
| 892 | 1164 | let mut m = Module::new("t".into()); |
| 893 | 1165 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 894 | - let v = push(&mut f, InstKind::ConstInt(64, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 895 | - let cnt = push(&mut f, InstKind::ConstInt(-2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1166 | + let v = push( | |
| 1167 | + &mut f, | |
| 1168 | + InstKind::ConstInt(64, IntWidth::I32), | |
| 1169 | + IrType::Int(IntWidth::I32), | |
| 1170 | + ); | |
| 1171 | + let cnt = push( | |
| 1172 | + &mut f, | |
| 1173 | + InstKind::ConstInt(-2, IntWidth::I32), | |
| 1174 | + IrType::Int(IntWidth::I32), | |
| 1175 | + ); | |
| 896 | 1176 | let s = push(&mut f, InstKind::LShr(v, cnt), IrType::Int(IntWidth::I32)); |
| 897 | 1177 | let entry = f.entry; |
| 898 | 1178 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 899 | 1179 | m.add_function(f); |
| 900 | 1180 | |
| 901 | 1181 | ConstFold.run(&mut m); |
| 902 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 1182 | + let folded = m.functions[0].blocks[0] | |
| 1183 | + .insts | |
| 1184 | + .iter() | |
| 1185 | + .find(|i| i.id == s) | |
| 1186 | + .unwrap(); | |
| 903 | 1187 | if let InstKind::ConstInt(0, _) = folded.kind { |
| 904 | 1188 | panic!("audit M-C: lshr with negative count folded to 0"); |
| 905 | 1189 | } |
@@ -932,25 +1216,35 @@ fn audit_const_fold_width_drift_iadd() { | ||
| 932 | 1216 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 933 | 1217 | let a = f.next_value_id(); |
| 934 | 1218 | f.block_mut(f.entry).insts.push(Inst { |
| 935 | - id: a, kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 936 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1219 | + id: a, | |
| 1220 | + kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1221 | + ty: IrType::Int(IntWidth::I8), | |
| 1222 | + span: dummy_span(), | |
| 937 | 1223 | }); |
| 938 | 1224 | let b = f.next_value_id(); |
| 939 | 1225 | f.block_mut(f.entry).insts.push(Inst { |
| 940 | - id: b, kind: InstKind::ConstInt(2, IntWidth::I8), | |
| 941 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1226 | + id: b, | |
| 1227 | + kind: InstKind::ConstInt(2, IntWidth::I8), | |
| 1228 | + ty: IrType::Int(IntWidth::I8), | |
| 1229 | + span: dummy_span(), | |
| 942 | 1230 | }); |
| 943 | 1231 | let sum = f.next_value_id(); |
| 944 | 1232 | f.block_mut(f.entry).insts.push(Inst { |
| 945 | - id: sum, kind: InstKind::IAdd(a, b), | |
| 946 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 1233 | + id: sum, | |
| 1234 | + kind: InstKind::IAdd(a, b), | |
| 1235 | + ty: IrType::Int(IntWidth::I32), | |
| 1236 | + span: dummy_span(), | |
| 947 | 1237 | }); |
| 948 | 1238 | let entry = f.entry; |
| 949 | 1239 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(sum))); |
| 950 | 1240 | m.add_function(f); |
| 951 | 1241 | |
| 952 | 1242 | ConstFold.run(&mut m); |
| 953 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == sum).unwrap(); | |
| 1243 | + let folded = m.functions[0].blocks[0] | |
| 1244 | + .insts | |
| 1245 | + .iter() | |
| 1246 | + .find(|i| i.id == sum) | |
| 1247 | + .unwrap(); | |
| 954 | 1248 | match folded.kind { |
| 955 | 1249 | InstKind::ConstInt(1, IntWidth::I32) => { /* good */ } |
| 956 | 1250 | InstKind::ConstInt(v, w) => panic!( |
@@ -970,28 +1264,41 @@ fn audit_const_fold_width_drift_isub() { | ||
| 970 | 1264 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 971 | 1265 | let a = f.next_value_id(); |
| 972 | 1266 | f.block_mut(f.entry).insts.push(Inst { |
| 973 | - id: a, kind: InstKind::ConstInt(250, IntWidth::I8), | |
| 974 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1267 | + id: a, | |
| 1268 | + kind: InstKind::ConstInt(250, IntWidth::I8), | |
| 1269 | + ty: IrType::Int(IntWidth::I8), | |
| 1270 | + span: dummy_span(), | |
| 975 | 1271 | }); |
| 976 | 1272 | let b = f.next_value_id(); |
| 977 | 1273 | f.block_mut(f.entry).insts.push(Inst { |
| 978 | - id: b, kind: InstKind::ConstInt(1, IntWidth::I8), | |
| 979 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1274 | + id: b, | |
| 1275 | + kind: InstKind::ConstInt(1, IntWidth::I8), | |
| 1276 | + ty: IrType::Int(IntWidth::I8), | |
| 1277 | + span: dummy_span(), | |
| 980 | 1278 | }); |
| 981 | 1279 | let diff = f.next_value_id(); |
| 982 | 1280 | f.block_mut(f.entry).insts.push(Inst { |
| 983 | - id: diff, kind: InstKind::ISub(a, b), | |
| 984 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 1281 | + id: diff, | |
| 1282 | + kind: InstKind::ISub(a, b), | |
| 1283 | + ty: IrType::Int(IntWidth::I32), | |
| 1284 | + span: dummy_span(), | |
| 985 | 1285 | }); |
| 986 | 1286 | let entry = f.entry; |
| 987 | 1287 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(diff))); |
| 988 | 1288 | m.add_function(f); |
| 989 | 1289 | |
| 990 | 1290 | ConstFold.run(&mut m); |
| 991 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == diff).unwrap(); | |
| 1291 | + let folded = m.functions[0].blocks[0] | |
| 1292 | + .insts | |
| 1293 | + .iter() | |
| 1294 | + .find(|i| i.id == diff) | |
| 1295 | + .unwrap(); | |
| 992 | 1296 | match folded.kind { |
| 993 | 1297 | InstKind::ConstInt(-7, IntWidth::I32) => { /* good */ } |
| 994 | - ref other => panic!("audit N-1 ISub: expected ConstInt(-7, I32), got {:?}", other), | |
| 1298 | + ref other => panic!( | |
| 1299 | + "audit N-1 ISub: expected ConstInt(-7, I32), got {:?}", | |
| 1300 | + other | |
| 1301 | + ), | |
| 995 | 1302 | } |
| 996 | 1303 | } |
| 997 | 1304 | |
@@ -1004,28 +1311,41 @@ fn audit_const_fold_width_drift_imul() { | ||
| 1004 | 1311 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1005 | 1312 | let a = f.next_value_id(); |
| 1006 | 1313 | f.block_mut(f.entry).insts.push(Inst { |
| 1007 | - id: a, kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1008 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1314 | + id: a, | |
| 1315 | + kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1316 | + ty: IrType::Int(IntWidth::I8), | |
| 1317 | + span: dummy_span(), | |
| 1009 | 1318 | }); |
| 1010 | 1319 | let b = f.next_value_id(); |
| 1011 | 1320 | f.block_mut(f.entry).insts.push(Inst { |
| 1012 | - id: b, kind: InstKind::ConstInt(3, IntWidth::I8), | |
| 1013 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1321 | + id: b, | |
| 1322 | + kind: InstKind::ConstInt(3, IntWidth::I8), | |
| 1323 | + ty: IrType::Int(IntWidth::I8), | |
| 1324 | + span: dummy_span(), | |
| 1014 | 1325 | }); |
| 1015 | 1326 | let prod = f.next_value_id(); |
| 1016 | 1327 | f.block_mut(f.entry).insts.push(Inst { |
| 1017 | - id: prod, kind: InstKind::IMul(a, b), | |
| 1018 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 1328 | + id: prod, | |
| 1329 | + kind: InstKind::IMul(a, b), | |
| 1330 | + ty: IrType::Int(IntWidth::I32), | |
| 1331 | + span: dummy_span(), | |
| 1019 | 1332 | }); |
| 1020 | 1333 | let entry = f.entry; |
| 1021 | 1334 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(prod))); |
| 1022 | 1335 | m.add_function(f); |
| 1023 | 1336 | |
| 1024 | 1337 | ConstFold.run(&mut m); |
| 1025 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == prod).unwrap(); | |
| 1338 | + let folded = m.functions[0].blocks[0] | |
| 1339 | + .insts | |
| 1340 | + .iter() | |
| 1341 | + .find(|i| i.id == prod) | |
| 1342 | + .unwrap(); | |
| 1026 | 1343 | match folded.kind { |
| 1027 | 1344 | InstKind::ConstInt(-3, IntWidth::I32) => { /* good */ } |
| 1028 | - ref other => panic!("audit N-1 IMul: expected ConstInt(-3, I32), got {:?}", other), | |
| 1345 | + ref other => panic!( | |
| 1346 | + "audit N-1 IMul: expected ConstInt(-3, I32), got {:?}", | |
| 1347 | + other | |
| 1348 | + ), | |
| 1029 | 1349 | } |
| 1030 | 1350 | } |
| 1031 | 1351 | |
@@ -1039,25 +1359,35 @@ fn audit_const_fold_width_drift_idiv() { | ||
| 1039 | 1359 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1040 | 1360 | let a = f.next_value_id(); |
| 1041 | 1361 | f.block_mut(f.entry).insts.push(Inst { |
| 1042 | - id: a, kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1043 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1362 | + id: a, | |
| 1363 | + kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1364 | + ty: IrType::Int(IntWidth::I8), | |
| 1365 | + span: dummy_span(), | |
| 1044 | 1366 | }); |
| 1045 | 1367 | let b = f.next_value_id(); |
| 1046 | 1368 | f.block_mut(f.entry).insts.push(Inst { |
| 1047 | - id: b, kind: InstKind::ConstInt(2, IntWidth::I8), | |
| 1048 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1369 | + id: b, | |
| 1370 | + kind: InstKind::ConstInt(2, IntWidth::I8), | |
| 1371 | + ty: IrType::Int(IntWidth::I8), | |
| 1372 | + span: dummy_span(), | |
| 1049 | 1373 | }); |
| 1050 | 1374 | let q = f.next_value_id(); |
| 1051 | 1375 | f.block_mut(f.entry).insts.push(Inst { |
| 1052 | - id: q, kind: InstKind::IDiv(a, b), | |
| 1053 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 1376 | + id: q, | |
| 1377 | + kind: InstKind::IDiv(a, b), | |
| 1378 | + ty: IrType::Int(IntWidth::I32), | |
| 1379 | + span: dummy_span(), | |
| 1054 | 1380 | }); |
| 1055 | 1381 | let entry = f.entry; |
| 1056 | 1382 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(q))); |
| 1057 | 1383 | m.add_function(f); |
| 1058 | 1384 | |
| 1059 | 1385 | ConstFold.run(&mut m); |
| 1060 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == q).unwrap(); | |
| 1386 | + let folded = m.functions[0].blocks[0] | |
| 1387 | + .insts | |
| 1388 | + .iter() | |
| 1389 | + .find(|i| i.id == q) | |
| 1390 | + .unwrap(); | |
| 1061 | 1391 | match folded.kind { |
| 1062 | 1392 | InstKind::ConstInt(0, IntWidth::I32) => { /* good */ } |
| 1063 | 1393 | ref other => panic!("audit N-1 IDiv: expected ConstInt(0, I32), got {:?}", other), |
@@ -1074,25 +1404,35 @@ fn audit_const_fold_width_drift_icmp() { | ||
| 1074 | 1404 | let mut f = Function::new("f".into(), vec![], IrType::Bool); |
| 1075 | 1405 | let a = f.next_value_id(); |
| 1076 | 1406 | f.block_mut(f.entry).insts.push(Inst { |
| 1077 | - id: a, kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1078 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1407 | + id: a, | |
| 1408 | + kind: InstKind::ConstInt(255, IntWidth::I8), | |
| 1409 | + ty: IrType::Int(IntWidth::I8), | |
| 1410 | + span: dummy_span(), | |
| 1079 | 1411 | }); |
| 1080 | 1412 | let b = f.next_value_id(); |
| 1081 | 1413 | f.block_mut(f.entry).insts.push(Inst { |
| 1082 | - id: b, kind: InstKind::ConstInt(0, IntWidth::I8), | |
| 1083 | - ty: IrType::Int(IntWidth::I8), span: dummy_span(), | |
| 1414 | + id: b, | |
| 1415 | + kind: InstKind::ConstInt(0, IntWidth::I8), | |
| 1416 | + ty: IrType::Int(IntWidth::I8), | |
| 1417 | + span: dummy_span(), | |
| 1084 | 1418 | }); |
| 1085 | 1419 | let lt = f.next_value_id(); |
| 1086 | 1420 | f.block_mut(f.entry).insts.push(Inst { |
| 1087 | - id: lt, kind: InstKind::ICmp(CmpOp::Lt, a, b), | |
| 1088 | - ty: IrType::Bool, span: dummy_span(), | |
| 1421 | + id: lt, | |
| 1422 | + kind: InstKind::ICmp(CmpOp::Lt, a, b), | |
| 1423 | + ty: IrType::Bool, | |
| 1424 | + span: dummy_span(), | |
| 1089 | 1425 | }); |
| 1090 | 1426 | let entry = f.entry; |
| 1091 | 1427 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(lt))); |
| 1092 | 1428 | m.add_function(f); |
| 1093 | 1429 | |
| 1094 | 1430 | ConstFold.run(&mut m); |
| 1095 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == lt).unwrap(); | |
| 1431 | + let folded = m.functions[0].blocks[0] | |
| 1432 | + .insts | |
| 1433 | + .iter() | |
| 1434 | + .find(|i| i.id == lt) | |
| 1435 | + .unwrap(); | |
| 1096 | 1436 | match folded.kind { |
| 1097 | 1437 | InstKind::ConstBool(true) => { /* good */ } |
| 1098 | 1438 | ref other => panic!("audit N-1 ICmp: expected ConstBool(true), got {:?}", other), |
@@ -1112,13 +1452,25 @@ fn audit_cse_dedupes_const_int_bit_pattern() { | ||
| 1112 | 1452 | use crate::opt::LocalCse; |
| 1113 | 1453 | let mut m = Module::new("t".into()); |
| 1114 | 1454 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I8)); |
| 1115 | - let a = push(&mut f, InstKind::ConstInt(255, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 1116 | - let b = push(&mut f, InstKind::ConstInt(-1, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 1455 | + let a = push( | |
| 1456 | + &mut f, | |
| 1457 | + InstKind::ConstInt(255, IntWidth::I8), | |
| 1458 | + IrType::Int(IntWidth::I8), | |
| 1459 | + ); | |
| 1460 | + let b = push( | |
| 1461 | + &mut f, | |
| 1462 | + InstKind::ConstInt(-1, IntWidth::I8), | |
| 1463 | + IrType::Int(IntWidth::I8), | |
| 1464 | + ); | |
| 1117 | 1465 | // Use a in some op and b in another so neither is dead. |
| 1118 | 1466 | let neg_a = push(&mut f, InstKind::INeg(a), IrType::Int(IntWidth::I8)); |
| 1119 | 1467 | let neg_b = push(&mut f, InstKind::INeg(b), IrType::Int(IntWidth::I8)); |
| 1120 | 1468 | // Add them so both are live to the terminator. |
| 1121 | - let sum = push(&mut f, InstKind::IAdd(neg_a, neg_b), IrType::Int(IntWidth::I8)); | |
| 1469 | + let sum = push( | |
| 1470 | + &mut f, | |
| 1471 | + InstKind::IAdd(neg_a, neg_b), | |
| 1472 | + IrType::Int(IntWidth::I8), | |
| 1473 | + ); | |
| 1122 | 1474 | let entry = f.entry; |
| 1123 | 1475 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(sum))); |
| 1124 | 1476 | m.add_function(f); |
@@ -1130,11 +1482,19 @@ fn audit_cse_dedupes_const_int_bit_pattern() { | ||
| 1130 | 1482 | let f = &m.functions[0]; |
| 1131 | 1483 | let neg_a_inst = f.blocks[0].insts.iter().find(|i| i.id == neg_a).unwrap(); |
| 1132 | 1484 | let neg_b_inst = f.blocks[0].insts.iter().find(|i| i.id == neg_b).unwrap(); |
| 1133 | - let neg_a_op = match &neg_a_inst.kind { InstKind::INeg(v) => *v, _ => panic!() }; | |
| 1134 | - let neg_b_op = match &neg_b_inst.kind { InstKind::INeg(v) => *v, _ => panic!() }; | |
| 1135 | - assert_eq!(neg_a_op, neg_b_op, | |
| 1485 | + let neg_a_op = match &neg_a_inst.kind { | |
| 1486 | + InstKind::INeg(v) => *v, | |
| 1487 | + _ => panic!(), | |
| 1488 | + }; | |
| 1489 | + let neg_b_op = match &neg_b_inst.kind { | |
| 1490 | + InstKind::INeg(v) => *v, | |
| 1491 | + _ => panic!(), | |
| 1492 | + }; | |
| 1493 | + assert_eq!( | |
| 1494 | + neg_a_op, neg_b_op, | |
| 1136 | 1495 | "audit B-8: ConstInt(255, I8) and ConstInt(-1, I8) should CSE-dedupe \ |
| 1137 | - (both represent -1 at i8 precision), but the INeg operands differ"); | |
| 1496 | + (both represent -1 at i8 precision), but the INeg operands differ" | |
| 1497 | + ); | |
| 1138 | 1498 | } |
| 1139 | 1499 | |
| 1140 | 1500 | // ============================================================= |
@@ -1144,15 +1504,27 @@ fn audit_cse_dedupes_const_int_bit_pattern() { | ||
| 1144 | 1504 | fn audit_const_fold_ashr_negative_count_bails() { |
| 1145 | 1505 | let mut m = Module::new("t".into()); |
| 1146 | 1506 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1147 | - let v = push(&mut f, InstKind::ConstInt(-128, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1148 | - let cnt = push(&mut f, InstKind::ConstInt(-3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1507 | + let v = push( | |
| 1508 | + &mut f, | |
| 1509 | + InstKind::ConstInt(-128, IntWidth::I32), | |
| 1510 | + IrType::Int(IntWidth::I32), | |
| 1511 | + ); | |
| 1512 | + let cnt = push( | |
| 1513 | + &mut f, | |
| 1514 | + InstKind::ConstInt(-3, IntWidth::I32), | |
| 1515 | + IrType::Int(IntWidth::I32), | |
| 1516 | + ); | |
| 1149 | 1517 | let s = push(&mut f, InstKind::AShr(v, cnt), IrType::Int(IntWidth::I32)); |
| 1150 | 1518 | let entry = f.entry; |
| 1151 | 1519 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 1152 | 1520 | m.add_function(f); |
| 1153 | 1521 | |
| 1154 | 1522 | ConstFold.run(&mut m); |
| 1155 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 1523 | + let folded = m.functions[0].blocks[0] | |
| 1524 | + .insts | |
| 1525 | + .iter() | |
| 1526 | + .find(|i| i.id == s) | |
| 1527 | + .unwrap(); | |
| 1156 | 1528 | if let InstKind::ConstInt(0, _) = folded.kind { |
| 1157 | 1529 | panic!("audit M-C: ashr with negative count folded to 0 — wrong on AArch64"); |
| 1158 | 1530 | } |
@@ -1169,13 +1541,28 @@ fn audit_licm_does_not_hoist_idiv() { | ||
| 1169 | 1541 | // skipped it (causing SIGFPE on b == 0). |
| 1170 | 1542 | let mut m = Module::new("t".into()); |
| 1171 | 1543 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 1172 | - let a = push(&mut f, InstKind::ConstInt(100, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1173 | - let b = push(&mut f, InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1174 | - let init = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1544 | + let a = push( | |
| 1545 | + &mut f, | |
| 1546 | + InstKind::ConstInt(100, IntWidth::I32), | |
| 1547 | + IrType::Int(IntWidth::I32), | |
| 1548 | + ); | |
| 1549 | + let b = push( | |
| 1550 | + &mut f, | |
| 1551 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 1552 | + IrType::Int(IntWidth::I32), | |
| 1553 | + ); | |
| 1554 | + let init = push( | |
| 1555 | + &mut f, | |
| 1556 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 1557 | + IrType::Int(IntWidth::I32), | |
| 1558 | + ); | |
| 1175 | 1559 | |
| 1176 | 1560 | let header = f.create_block("header"); |
| 1177 | 1561 | let i_param = f.next_value_id(); |
| 1178 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 1562 | + f.block_mut(header).params.push(BlockParam { | |
| 1563 | + id: i_param, | |
| 1564 | + ty: IrType::Int(IntWidth::I32), | |
| 1565 | + }); | |
| 1179 | 1566 | |
| 1180 | 1567 | // Inside the body: %q = idiv a, b — both operands invariant. |
| 1181 | 1568 | let q = f.next_value_id(); |
@@ -1224,12 +1611,28 @@ fn audit_licm_does_not_hoist_idiv() { | ||
| 1224 | 1611 | Licm.run(&mut m); |
| 1225 | 1612 | let f = &m.functions[0]; |
| 1226 | 1613 | // The IDiv must STILL be in the header block, not in entry. |
| 1227 | - let header_block = f.blocks.iter().find(|b| b.name.starts_with("header")).unwrap(); | |
| 1614 | + let header_block = f | |
| 1615 | + .blocks | |
| 1616 | + .iter() | |
| 1617 | + .find(|b| b.name.starts_with("header")) | |
| 1618 | + .unwrap(); | |
| 1228 | 1619 | let entry_block = f.block(f.entry); |
| 1229 | - let idiv_in_header = header_block.insts.iter().any(|i| matches!(i.kind, InstKind::IDiv(..))); | |
| 1230 | - let idiv_in_entry = entry_block.insts.iter().any(|i| matches!(i.kind, InstKind::IDiv(..))); | |
| 1231 | - assert!(idiv_in_header, "audit Med-6: LICM should leave IDiv in body (trap-prone)"); | |
| 1232 | - assert!(!idiv_in_entry, "audit Med-6: LICM must not hoist IDiv into preheader"); | |
| 1620 | + let idiv_in_header = header_block | |
| 1621 | + .insts | |
| 1622 | + .iter() | |
| 1623 | + .any(|i| matches!(i.kind, InstKind::IDiv(..))); | |
| 1624 | + let idiv_in_entry = entry_block | |
| 1625 | + .insts | |
| 1626 | + .iter() | |
| 1627 | + .any(|i| matches!(i.kind, InstKind::IDiv(..))); | |
| 1628 | + assert!( | |
| 1629 | + idiv_in_header, | |
| 1630 | + "audit Med-6: LICM should leave IDiv in body (trap-prone)" | |
| 1631 | + ); | |
| 1632 | + assert!( | |
| 1633 | + !idiv_in_entry, | |
| 1634 | + "audit Med-6: LICM must not hoist IDiv into preheader" | |
| 1635 | + ); | |
| 1233 | 1636 | } |
| 1234 | 1637 | |
| 1235 | 1638 | // ============================================================= |
@@ -1243,16 +1646,32 @@ fn audit_licm_does_not_hoist_idiv() { | ||
| 1243 | 1646 | fn audit_const_fold_select_uses_declared_type() { |
| 1244 | 1647 | let mut m = Module::new("t".into()); |
| 1245 | 1648 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1246 | - let a = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1247 | - let b = push(&mut f, InstKind::ConstInt(99, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1649 | + let a = push( | |
| 1650 | + &mut f, | |
| 1651 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1652 | + IrType::Int(IntWidth::I32), | |
| 1653 | + ); | |
| 1654 | + let b = push( | |
| 1655 | + &mut f, | |
| 1656 | + InstKind::ConstInt(99, IntWidth::I32), | |
| 1657 | + IrType::Int(IntWidth::I32), | |
| 1658 | + ); | |
| 1248 | 1659 | let cond = push(&mut f, InstKind::ConstBool(true), IrType::Bool); |
| 1249 | - let sel = push(&mut f, InstKind::Select(cond, a, b), IrType::Int(IntWidth::I32)); | |
| 1660 | + let sel = push( | |
| 1661 | + &mut f, | |
| 1662 | + InstKind::Select(cond, a, b), | |
| 1663 | + IrType::Int(IntWidth::I32), | |
| 1664 | + ); | |
| 1250 | 1665 | let entry = f.entry; |
| 1251 | 1666 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(sel))); |
| 1252 | 1667 | m.add_function(f); |
| 1253 | 1668 | |
| 1254 | 1669 | ConstFold.run(&mut m); |
| 1255 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == sel).unwrap(); | |
| 1670 | + let folded = m.functions[0].blocks[0] | |
| 1671 | + .insts | |
| 1672 | + .iter() | |
| 1673 | + .find(|i| i.id == sel) | |
| 1674 | + .unwrap(); | |
| 1256 | 1675 | match folded.kind { |
| 1257 | 1676 | InstKind::ConstInt(1, IntWidth::I32) => { /* good */ } |
| 1258 | 1677 | ref other => panic!("expected ConstInt(1, I32), got {:?}", other), |
@@ -1292,7 +1711,11 @@ fn audit_const_fold_select_width_drift_int() { | ||
| 1292 | 1711 | ty: IrType::Int(IntWidth::I8), |
| 1293 | 1712 | span: dummy_span(), |
| 1294 | 1713 | }); |
| 1295 | - let b = push(&mut f, InstKind::ConstInt(99, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 1714 | + let b = push( | |
| 1715 | + &mut f, | |
| 1716 | + InstKind::ConstInt(99, IntWidth::I64), | |
| 1717 | + IrType::Int(IntWidth::I64), | |
| 1718 | + ); | |
| 1296 | 1719 | let cond = push(&mut f, InstKind::ConstBool(true), IrType::Bool); |
| 1297 | 1720 | |
| 1298 | 1721 | // Select declared as i64, chosen branch is i8 (255 → -1). |
@@ -1307,7 +1730,11 @@ fn audit_const_fold_select_width_drift_int() { | ||
| 1307 | 1730 | m.add_function(f); |
| 1308 | 1731 | |
| 1309 | 1732 | ConstFold.run(&mut m); |
| 1310 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == sel).unwrap(); | |
| 1733 | + let folded = m.functions[0].blocks[0] | |
| 1734 | + .insts | |
| 1735 | + .iter() | |
| 1736 | + .find(|i| i.id == sel) | |
| 1737 | + .unwrap(); | |
| 1311 | 1738 | match folded.kind { |
| 1312 | 1739 | InstKind::ConstInt(-1, IntWidth::I64) => { /* good — i8 -1 sign-extended to i64 */ } |
| 1313 | 1740 | InstKind::ConstInt(v, w) => panic!( |
@@ -1338,8 +1765,16 @@ fn audit_const_fold_select_width_drift_int() { | ||
| 1338 | 1765 | fn audit_strength_reduce_identity_does_not_write_placeholder() { |
| 1339 | 1766 | let mut m = Module::new("t".into()); |
| 1340 | 1767 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1341 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1342 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1768 | + let x = push( | |
| 1769 | + &mut f, | |
| 1770 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 1771 | + IrType::Int(IntWidth::I32), | |
| 1772 | + ); | |
| 1773 | + let one = push( | |
| 1774 | + &mut f, | |
| 1775 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1776 | + IrType::Int(IntWidth::I32), | |
| 1777 | + ); | |
| 1343 | 1778 | let mul = push(&mut f, InstKind::IMul(x, one), IrType::Int(IntWidth::I32)); |
| 1344 | 1779 | let entry = f.entry; |
| 1345 | 1780 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(mul))); |
@@ -1349,7 +1784,11 @@ fn audit_strength_reduce_identity_does_not_write_placeholder() { | ||
| 1349 | 1784 | // The orphan instruction at `mul`'s ID must still exist and |
| 1350 | 1785 | // have its original kind. The earlier (broken) version would |
| 1351 | 1786 | // have replaced its kind with ConstInt(0, _). |
| 1352 | - let orphan = m.functions[0].blocks[0].insts.iter().find(|i| i.id == mul).unwrap(); | |
| 1787 | + let orphan = m.functions[0].blocks[0] | |
| 1788 | + .insts | |
| 1789 | + .iter() | |
| 1790 | + .find(|i| i.id == mul) | |
| 1791 | + .unwrap(); | |
| 1353 | 1792 | match orphan.kind { |
| 1354 | 1793 | InstKind::IMul(_, _) => { /* good — original kind preserved */ } |
| 1355 | 1794 | InstKind::ConstInt(0, _) => panic!( |
@@ -1369,15 +1808,26 @@ fn audit_strength_reduce_identity_does_not_write_placeholder() { | ||
| 1369 | 1808 | fn audit_strength_reduce_identity_reports_changed() { |
| 1370 | 1809 | let mut m = Module::new("t".into()); |
| 1371 | 1810 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1372 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1373 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1811 | + let x = push( | |
| 1812 | + &mut f, | |
| 1813 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 1814 | + IrType::Int(IntWidth::I32), | |
| 1815 | + ); | |
| 1816 | + let one = push( | |
| 1817 | + &mut f, | |
| 1818 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1819 | + IrType::Int(IntWidth::I32), | |
| 1820 | + ); | |
| 1374 | 1821 | let mul = push(&mut f, InstKind::IMul(x, one), IrType::Int(IntWidth::I32)); |
| 1375 | 1822 | let entry = f.entry; |
| 1376 | 1823 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(mul))); |
| 1377 | 1824 | m.add_function(f); |
| 1378 | 1825 | |
| 1379 | 1826 | let changed = StrengthReduce.run(&mut m); |
| 1380 | - assert!(changed, "strength_reduce must report `changed = true` after Identity rewrite"); | |
| 1827 | + assert!( | |
| 1828 | + changed, | |
| 1829 | + "strength_reduce must report `changed = true` after Identity rewrite" | |
| 1830 | + ); | |
| 1381 | 1831 | |
| 1382 | 1832 | // The terminator now references x. |
| 1383 | 1833 | match m.functions[0].blocks[0].terminator.as_ref().unwrap() { |
@@ -1403,15 +1853,27 @@ fn audit_const_fold_icmp_uses_each_operand_width() { | ||
| 1403 | 1853 | // be FALSE: 255 != -1. |
| 1404 | 1854 | let mut m = Module::new("t".into()); |
| 1405 | 1855 | let mut f = Function::new("f".into(), vec![], IrType::Bool); |
| 1406 | - let a = push(&mut f, InstKind::ConstInt(255, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1407 | - let b = push(&mut f, InstKind::ConstInt(255, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 1856 | + let a = push( | |
| 1857 | + &mut f, | |
| 1858 | + InstKind::ConstInt(255, IntWidth::I32), | |
| 1859 | + IrType::Int(IntWidth::I32), | |
| 1860 | + ); | |
| 1861 | + let b = push( | |
| 1862 | + &mut f, | |
| 1863 | + InstKind::ConstInt(255, IntWidth::I8), | |
| 1864 | + IrType::Int(IntWidth::I8), | |
| 1865 | + ); | |
| 1408 | 1866 | let eq = push(&mut f, InstKind::ICmp(CmpOp::Eq, a, b), IrType::Bool); |
| 1409 | 1867 | let entry = f.entry; |
| 1410 | 1868 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(eq))); |
| 1411 | 1869 | m.add_function(f); |
| 1412 | 1870 | |
| 1413 | 1871 | ConstFold.run(&mut m); |
| 1414 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == eq).unwrap(); | |
| 1872 | + let folded = m.functions[0].blocks[0] | |
| 1873 | + .insts | |
| 1874 | + .iter() | |
| 1875 | + .find(|i| i.id == eq) | |
| 1876 | + .unwrap(); | |
| 1415 | 1877 | match folded.kind { |
| 1416 | 1878 | // After the M-D fix: bv sign-extended at i8 → -1; av at i32 → 255. |
| 1417 | 1879 | // 255 == -1 → false. |
@@ -1436,18 +1898,37 @@ fn audit_const_fold_fcmp_f32_after_m1_fix() { | ||
| 1436 | 1898 | // ConstFloat(16777216.0, F32) should now return true. |
| 1437 | 1899 | let mut m = Module::new("t".into()); |
| 1438 | 1900 | let mut f = Function::new("f".into(), vec![], IrType::Bool); |
| 1439 | - let i = push(&mut f, InstKind::ConstInt(16_777_217, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1440 | - let fv_a = push(&mut f, InstKind::IntToFloat(i, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 1441 | - let fv_b = push(&mut f, InstKind::ConstFloat(16_777_216.0, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 1901 | + let i = push( | |
| 1902 | + &mut f, | |
| 1903 | + InstKind::ConstInt(16_777_217, IntWidth::I32), | |
| 1904 | + IrType::Int(IntWidth::I32), | |
| 1905 | + ); | |
| 1906 | + let fv_a = push( | |
| 1907 | + &mut f, | |
| 1908 | + InstKind::IntToFloat(i, FloatWidth::F32), | |
| 1909 | + IrType::Float(FloatWidth::F32), | |
| 1910 | + ); | |
| 1911 | + let fv_b = push( | |
| 1912 | + &mut f, | |
| 1913 | + InstKind::ConstFloat(16_777_216.0, FloatWidth::F32), | |
| 1914 | + IrType::Float(FloatWidth::F32), | |
| 1915 | + ); | |
| 1442 | 1916 | let eq = push(&mut f, InstKind::FCmp(CmpOp::Eq, fv_a, fv_b), IrType::Bool); |
| 1443 | 1917 | let entry = f.entry; |
| 1444 | 1918 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(eq))); |
| 1445 | 1919 | m.add_function(f); |
| 1446 | 1920 | |
| 1447 | 1921 | ConstFold.run(&mut m); |
| 1448 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == eq).unwrap(); | |
| 1449 | - assert!(matches!(folded.kind, InstKind::ConstBool(true)), | |
| 1450 | - "audit M-E: FCmp on f32-rounded values should return true, got {:?}", folded.kind); | |
| 1922 | + let folded = m.functions[0].blocks[0] | |
| 1923 | + .insts | |
| 1924 | + .iter() | |
| 1925 | + .find(|i| i.id == eq) | |
| 1926 | + .unwrap(); | |
| 1927 | + assert!( | |
| 1928 | + matches!(folded.kind, InstKind::ConstBool(true)), | |
| 1929 | + "audit M-E: FCmp on f32-rounded values should return true, got {:?}", | |
| 1930 | + folded.kind | |
| 1931 | + ); | |
| 1451 | 1932 | } |
| 1452 | 1933 | |
| 1453 | 1934 | // ============================================================= |
@@ -1460,18 +1941,37 @@ fn audit_const_fold_floattoint_from_f32_after_m1_fix() { | ||
| 1460 | 1941 | // FloatToInt(_, I32) should produce 16777216, not 16777217. |
| 1461 | 1942 | let mut m = Module::new("t".into()); |
| 1462 | 1943 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1463 | - let i = push(&mut f, InstKind::ConstInt(16_777_217, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1464 | - let fv = push(&mut f, InstKind::IntToFloat(i, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 1465 | - let back = push(&mut f, InstKind::FloatToInt(fv, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1944 | + let i = push( | |
| 1945 | + &mut f, | |
| 1946 | + InstKind::ConstInt(16_777_217, IntWidth::I32), | |
| 1947 | + IrType::Int(IntWidth::I32), | |
| 1948 | + ); | |
| 1949 | + let fv = push( | |
| 1950 | + &mut f, | |
| 1951 | + InstKind::IntToFloat(i, FloatWidth::F32), | |
| 1952 | + IrType::Float(FloatWidth::F32), | |
| 1953 | + ); | |
| 1954 | + let back = push( | |
| 1955 | + &mut f, | |
| 1956 | + InstKind::FloatToInt(fv, IntWidth::I32), | |
| 1957 | + IrType::Int(IntWidth::I32), | |
| 1958 | + ); | |
| 1466 | 1959 | let entry = f.entry; |
| 1467 | 1960 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(back))); |
| 1468 | 1961 | m.add_function(f); |
| 1469 | 1962 | |
| 1470 | 1963 | ConstFold.run(&mut m); |
| 1471 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == back).unwrap(); | |
| 1964 | + let folded = m.functions[0].blocks[0] | |
| 1965 | + .insts | |
| 1966 | + .iter() | |
| 1967 | + .find(|i| i.id == back) | |
| 1968 | + .unwrap(); | |
| 1472 | 1969 | match folded.kind { |
| 1473 | 1970 | InstKind::ConstInt(16_777_216, IntWidth::I32) => { /* good */ } |
| 1474 | - ref other => panic!("audit M-F: expected ConstInt(16777216, I32), got {:?}", other), | |
| 1971 | + ref other => panic!( | |
| 1972 | + "audit M-F: expected ConstInt(16777216, I32), got {:?}", | |
| 1973 | + other | |
| 1974 | + ), | |
| 1475 | 1975 | } |
| 1476 | 1976 | } |
| 1477 | 1977 | |
@@ -1486,14 +1986,22 @@ fn audit_const_fold_popcount_uses_inst_ty() { | ||
| 1486 | 1986 | // change makes the source carry a different width. |
| 1487 | 1987 | let mut m = Module::new("t".into()); |
| 1488 | 1988 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1489 | - let v = push(&mut f, InstKind::ConstInt(0xFF, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1989 | + let v = push( | |
| 1990 | + &mut f, | |
| 1991 | + InstKind::ConstInt(0xFF, IntWidth::I32), | |
| 1992 | + IrType::Int(IntWidth::I32), | |
| 1993 | + ); | |
| 1490 | 1994 | let pc = push(&mut f, InstKind::PopCount(v), IrType::Int(IntWidth::I32)); |
| 1491 | 1995 | let entry = f.entry; |
| 1492 | 1996 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(pc))); |
| 1493 | 1997 | m.add_function(f); |
| 1494 | 1998 | |
| 1495 | 1999 | ConstFold.run(&mut m); |
| 1496 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == pc).unwrap(); | |
| 2000 | + let folded = m.functions[0].blocks[0] | |
| 2001 | + .insts | |
| 2002 | + .iter() | |
| 2003 | + .find(|i| i.id == pc) | |
| 2004 | + .unwrap(); | |
| 1497 | 2005 | match folded.kind { |
| 1498 | 2006 | InstKind::ConstInt(8, IntWidth::I32) => { /* good */ } |
| 1499 | 2007 | ref other => panic!("expected ConstInt(8, I32), got {:?}", other), |
@@ -1514,23 +2022,50 @@ fn audit_const_fold_popcount_uses_inst_ty() { | ||
| 1514 | 2022 | fn audit_int_to_f32_then_fsub_wrong_answer_today() { |
| 1515 | 2023 | let mut m = Module::new("t".into()); |
| 1516 | 2024 | let mut f = Function::new("f".into(), vec![], IrType::Float(FloatWidth::F32)); |
| 1517 | - let i_a = push(&mut f, InstKind::ConstInt(16_777_217, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1518 | - let i_b = push(&mut f, InstKind::ConstInt(16_777_216, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1519 | - let f_a = push(&mut f, InstKind::IntToFloat(i_a, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 1520 | - let f_b = push(&mut f, InstKind::IntToFloat(i_b, FloatWidth::F32), IrType::Float(FloatWidth::F32)); | |
| 1521 | - let diff = push(&mut f, InstKind::FSub(f_a, f_b), IrType::Float(FloatWidth::F32)); | |
| 2025 | + let i_a = push( | |
| 2026 | + &mut f, | |
| 2027 | + InstKind::ConstInt(16_777_217, IntWidth::I32), | |
| 2028 | + IrType::Int(IntWidth::I32), | |
| 2029 | + ); | |
| 2030 | + let i_b = push( | |
| 2031 | + &mut f, | |
| 2032 | + InstKind::ConstInt(16_777_216, IntWidth::I32), | |
| 2033 | + IrType::Int(IntWidth::I32), | |
| 2034 | + ); | |
| 2035 | + let f_a = push( | |
| 2036 | + &mut f, | |
| 2037 | + InstKind::IntToFloat(i_a, FloatWidth::F32), | |
| 2038 | + IrType::Float(FloatWidth::F32), | |
| 2039 | + ); | |
| 2040 | + let f_b = push( | |
| 2041 | + &mut f, | |
| 2042 | + InstKind::IntToFloat(i_b, FloatWidth::F32), | |
| 2043 | + IrType::Float(FloatWidth::F32), | |
| 2044 | + ); | |
| 2045 | + let diff = push( | |
| 2046 | + &mut f, | |
| 2047 | + InstKind::FSub(f_a, f_b), | |
| 2048 | + IrType::Float(FloatWidth::F32), | |
| 2049 | + ); | |
| 1522 | 2050 | let entry = f.entry; |
| 1523 | 2051 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(diff))); |
| 1524 | 2052 | m.add_function(f); |
| 1525 | 2053 | |
| 1526 | 2054 | ConstFold.run(&mut m); |
| 1527 | 2055 | |
| 1528 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == diff).unwrap(); | |
| 2056 | + let folded = m.functions[0].blocks[0] | |
| 2057 | + .insts | |
| 2058 | + .iter() | |
| 2059 | + .find(|i| i.id == diff) | |
| 2060 | + .unwrap(); | |
| 1529 | 2061 | match folded.kind { |
| 1530 | 2062 | // Expected: 0.0 (after correct f32 rounding both inputs are 16777216). |
| 1531 | 2063 | // Bug: 1.0 (without rounding 16777217.0 - 16777216.0 = 1.0). |
| 1532 | - InstKind::ConstFloat(v, FloatWidth::F32) => assert_eq!(v, 0.0, | |
| 1533 | - "expected 0.0 (both inputs round to 16777216 in f32), got {}", v), | |
| 2064 | + InstKind::ConstFloat(v, FloatWidth::F32) => assert_eq!( | |
| 2065 | + v, 0.0, | |
| 2066 | + "expected 0.0 (both inputs round to 16777216 in f32), got {}", | |
| 2067 | + v | |
| 2068 | + ), | |
| 1534 | 2069 | ref other => panic!("expected ConstFloat, got {:?}", other), |
| 1535 | 2070 | } |
| 1536 | 2071 | } |
@@ -1542,15 +2077,27 @@ fn audit_int_to_f32_then_fsub_wrong_answer_today() { | ||
| 1542 | 2077 | fn audit_const_fold_imul_i8_overflow_wraps() { |
| 1543 | 2078 | let mut m = Module::new("t".into()); |
| 1544 | 2079 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I8)); |
| 1545 | - let a = push(&mut f, InstKind::ConstInt(100, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 1546 | - let b = push(&mut f, InstKind::ConstInt(100, IntWidth::I8), IrType::Int(IntWidth::I8)); | |
| 2080 | + let a = push( | |
| 2081 | + &mut f, | |
| 2082 | + InstKind::ConstInt(100, IntWidth::I8), | |
| 2083 | + IrType::Int(IntWidth::I8), | |
| 2084 | + ); | |
| 2085 | + let b = push( | |
| 2086 | + &mut f, | |
| 2087 | + InstKind::ConstInt(100, IntWidth::I8), | |
| 2088 | + IrType::Int(IntWidth::I8), | |
| 2089 | + ); | |
| 1547 | 2090 | let p = push(&mut f, InstKind::IMul(a, b), IrType::Int(IntWidth::I8)); |
| 1548 | 2091 | let entry = f.entry; |
| 1549 | 2092 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(p))); |
| 1550 | 2093 | m.add_function(f); |
| 1551 | 2094 | |
| 1552 | 2095 | ConstFold.run(&mut m); |
| 1553 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == p).unwrap(); | |
| 2096 | + let folded = m.functions[0].blocks[0] | |
| 2097 | + .insts | |
| 2098 | + .iter() | |
| 2099 | + .find(|i| i.id == p) | |
| 2100 | + .unwrap(); | |
| 1554 | 2101 | match folded.kind { |
| 1555 | 2102 | // 100 * 100 = 10000; low byte = 0x10 = 16; sext = 16 |
| 1556 | 2103 | InstKind::ConstInt(v, IntWidth::I8) => assert_eq!(v, 16, "expected i8 wrap, got {}", v), |
@@ -1583,7 +2130,11 @@ fn audit_const_fold_non_rpo_block_order() { | ||
| 1583 | 2130 | |
| 1584 | 2131 | let a_block = f.create_block("a"); |
| 1585 | 2132 | let b_block = f.create_block("b"); |
| 1586 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 2133 | + let one = push( | |
| 2134 | + &mut f, | |
| 2135 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 2136 | + IrType::Int(IntWidth::I32), | |
| 2137 | + ); | |
| 1587 | 2138 | let entry = f.entry; |
| 1588 | 2139 | f.block_mut(entry).terminator = Some(Terminator::Branch(a_block, vec![])); |
| 1589 | 2140 | |
@@ -1616,20 +2167,28 @@ fn audit_const_fold_non_rpo_block_order() { | ||
| 1616 | 2167 | pm.run(&mut m); |
| 1617 | 2168 | |
| 1618 | 2169 | let post = verify_module(&m); |
| 1619 | - assert!(post.is_empty(), "non-RPO block order broke optimization: {:?}", post); | |
| 2170 | + assert!( | |
| 2171 | + post.is_empty(), | |
| 2172 | + "non-RPO block order broke optimization: {:?}", | |
| 2173 | + post | |
| 2174 | + ); | |
| 1620 | 2175 | |
| 1621 | 2176 | // The fold MUST have converged: the IAdd's defining instruction |
| 1622 | 2177 | // (still carrying `sum_id`) should now be the constant `3`, or |
| 1623 | 2178 | // the entire dead chain should have been DCE'd and the terminator |
| 1624 | 2179 | // should reference a const(3) directly. |
| 1625 | 2180 | let f = &m.functions[0]; |
| 1626 | - let terminator_val = f.blocks.iter() | |
| 2181 | + let terminator_val = f | |
| 2182 | + .blocks | |
| 2183 | + .iter() | |
| 1627 | 2184 | .find_map(|b| match &b.terminator { |
| 1628 | 2185 | Some(Terminator::Return(Some(v))) => Some(*v), |
| 1629 | 2186 | _ => None, |
| 1630 | 2187 | }) |
| 1631 | 2188 | .expect("no Return terminator"); |
| 1632 | - let term_kind = f.blocks.iter() | |
| 2189 | + let term_kind = f | |
| 2190 | + .blocks | |
| 2191 | + .iter() | |
| 1633 | 2192 | .flat_map(|b| b.insts.iter()) |
| 1634 | 2193 | .find(|i| i.id == terminator_val) |
| 1635 | 2194 | .map(|i| i.kind.clone()) |
@@ -1705,7 +2264,7 @@ fn audit_const_fold_inner_fixpoint_across_vec_order() { | ||
| 1705 | 2264 | let a_sum = f.next_value_id(); |
| 1706 | 2265 | f.block_mut(a_block).insts.push(Inst { |
| 1707 | 2266 | id: a_sum, |
| 1708 | - kind: InstKind::IAdd(c1, c2), // folds to const(30) | |
| 2267 | + kind: InstKind::IAdd(c1, c2), // folds to const(30) | |
| 1709 | 2268 | ty: IrType::Int(IntWidth::I32), |
| 1710 | 2269 | span: dummy_span(), |
| 1711 | 2270 | }); |
@@ -1722,7 +2281,7 @@ fn audit_const_fold_inner_fixpoint_across_vec_order() { | ||
| 1722 | 2281 | }); |
| 1723 | 2282 | f.block_mut(b_block).insts.push(Inst { |
| 1724 | 2283 | id: b_sum, |
| 1725 | - kind: InstKind::IAdd(a_sum, seven), // folds to const(37) | |
| 2284 | + kind: InstKind::IAdd(a_sum, seven), // folds to const(37) | |
| 1726 | 2285 | ty: IrType::Int(IntWidth::I32), |
| 1727 | 2286 | span: dummy_span(), |
| 1728 | 2287 | }); |
@@ -1762,13 +2321,17 @@ fn audit_const_fold_inner_fixpoint_across_vec_order() { | ||
| 1762 | 2321 | // The fold MUST converge in a single `ConstFold.run()` call — |
| 1763 | 2322 | // the terminator's value should now define ConstInt(37) (= 10+20+7). |
| 1764 | 2323 | let f = &m.functions[0]; |
| 1765 | - let terminator_val = f.blocks.iter() | |
| 2324 | + let terminator_val = f | |
| 2325 | + .blocks | |
| 2326 | + .iter() | |
| 1766 | 2327 | .find_map(|blk| match &blk.terminator { |
| 1767 | 2328 | Some(Terminator::Return(Some(v))) => Some(*v), |
| 1768 | 2329 | _ => None, |
| 1769 | 2330 | }) |
| 1770 | 2331 | .expect("no Return terminator"); |
| 1771 | - let term_kind = f.blocks.iter() | |
| 2332 | + let term_kind = f | |
| 2333 | + .blocks | |
| 2334 | + .iter() | |
| 1772 | 2335 | .flat_map(|b| b.insts.iter()) |
| 1773 | 2336 | .find(|i| i.id == terminator_val) |
| 1774 | 2337 | .map(|i| i.kind.clone()) |
@@ -1793,15 +2356,27 @@ fn audit_const_fold_inner_fixpoint_across_vec_order() { | ||
| 1793 | 2356 | fn audit_const_fold_shl_full_width_wrap() { |
| 1794 | 2357 | let mut m = Module::new("t".into()); |
| 1795 | 2358 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1796 | - let v = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1797 | - let cnt = push(&mut f, InstKind::ConstInt(31, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 2359 | + let v = push( | |
| 2360 | + &mut f, | |
| 2361 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 2362 | + IrType::Int(IntWidth::I32), | |
| 2363 | + ); | |
| 2364 | + let cnt = push( | |
| 2365 | + &mut f, | |
| 2366 | + InstKind::ConstInt(31, IntWidth::I32), | |
| 2367 | + IrType::Int(IntWidth::I32), | |
| 2368 | + ); | |
| 1798 | 2369 | let s = push(&mut f, InstKind::Shl(v, cnt), IrType::Int(IntWidth::I32)); |
| 1799 | 2370 | let entry = f.entry; |
| 1800 | 2371 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 1801 | 2372 | m.add_function(f); |
| 1802 | 2373 | |
| 1803 | 2374 | ConstFold.run(&mut m); |
| 1804 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 2375 | + let folded = m.functions[0].blocks[0] | |
| 2376 | + .insts | |
| 2377 | + .iter() | |
| 2378 | + .find(|i| i.id == s) | |
| 2379 | + .unwrap(); | |
| 1805 | 2380 | match folded.kind { |
| 1806 | 2381 | InstKind::ConstInt(v, IntWidth::I32) => { |
| 1807 | 2382 | // 1 << 31 in i32 is INT_MIN = -2147483648. |
@@ -1818,15 +2393,27 @@ fn audit_const_fold_shl_full_width_wrap() { | ||
| 1818 | 2393 | fn audit_const_fold_iadd_i16_overflow() { |
| 1819 | 2394 | let mut m = Module::new("t".into()); |
| 1820 | 2395 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I16)); |
| 1821 | - let a = push(&mut f, InstKind::ConstInt(32000, IntWidth::I16), IrType::Int(IntWidth::I16)); | |
| 1822 | - let b = push(&mut f, InstKind::ConstInt(1000, IntWidth::I16), IrType::Int(IntWidth::I16)); | |
| 2396 | + let a = push( | |
| 2397 | + &mut f, | |
| 2398 | + InstKind::ConstInt(32000, IntWidth::I16), | |
| 2399 | + IrType::Int(IntWidth::I16), | |
| 2400 | + ); | |
| 2401 | + let b = push( | |
| 2402 | + &mut f, | |
| 2403 | + InstKind::ConstInt(1000, IntWidth::I16), | |
| 2404 | + IrType::Int(IntWidth::I16), | |
| 2405 | + ); | |
| 1823 | 2406 | let s = push(&mut f, InstKind::IAdd(a, b), IrType::Int(IntWidth::I16)); |
| 1824 | 2407 | let entry = f.entry; |
| 1825 | 2408 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(s))); |
| 1826 | 2409 | m.add_function(f); |
| 1827 | 2410 | |
| 1828 | 2411 | ConstFold.run(&mut m); |
| 1829 | - let folded = m.functions[0].blocks[0].insts.iter().find(|i| i.id == s).unwrap(); | |
| 2412 | + let folded = m.functions[0].blocks[0] | |
| 2413 | + .insts | |
| 2414 | + .iter() | |
| 2415 | + .find(|i| i.id == s) | |
| 2416 | + .unwrap(); | |
| 1830 | 2417 | match folded.kind { |
| 1831 | 2418 | InstKind::ConstInt(v, IntWidth::I16) => { |
| 1832 | 2419 | // 32000 + 1000 = 33000, wraps in i16 to -32536 |
@@ -1850,14 +2437,25 @@ fn audit_const_prop_merge_after_drop() { | ||
| 1850 | 2437 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1851 | 2438 | |
| 1852 | 2439 | let cond = push(&mut f, InstKind::ConstBool(true), IrType::Bool); |
| 1853 | - let v_a = push(&mut f, InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1854 | - let v_b = push(&mut f, InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 2440 | + let v_a = push( | |
| 2441 | + &mut f, | |
| 2442 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 2443 | + IrType::Int(IntWidth::I32), | |
| 2444 | + ); | |
| 2445 | + let v_b = push( | |
| 2446 | + &mut f, | |
| 2447 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 2448 | + IrType::Int(IntWidth::I32), | |
| 2449 | + ); | |
| 1855 | 2450 | |
| 1856 | 2451 | let a = f.create_block("a"); |
| 1857 | 2452 | let b = f.create_block("b"); |
| 1858 | 2453 | let merge = f.create_block("merge"); |
| 1859 | 2454 | let m_param = f.next_value_id(); |
| 1860 | - f.block_mut(merge).params.push(BlockParam { id: m_param, ty: IrType::Int(IntWidth::I32) }); | |
| 2455 | + f.block_mut(merge).params.push(BlockParam { | |
| 2456 | + id: m_param, | |
| 2457 | + ty: IrType::Int(IntWidth::I32), | |
| 2458 | + }); | |
| 1861 | 2459 | |
| 1862 | 2460 | f.block_mut(a).terminator = Some(Terminator::Branch(merge, vec![v_a])); |
| 1863 | 2461 | f.block_mut(b).terminator = Some(Terminator::Branch(merge, vec![v_b])); |
@@ -1879,11 +2477,20 @@ fn audit_const_prop_merge_after_drop() { | ||
| 1879 | 2477 | ConstProp.run(&mut m); |
| 1880 | 2478 | |
| 1881 | 2479 | let post = verify_module(&m); |
| 1882 | - assert!(post.is_empty(), | |
| 1883 | - "const_prop produced an invalid module after dropping the false arm: {:?}", post); | |
| 2480 | + assert!( | |
| 2481 | + post.is_empty(), | |
| 2482 | + "const_prop produced an invalid module after dropping the false arm: {:?}", | |
| 2483 | + post | |
| 2484 | + ); | |
| 1884 | 2485 | |
| 1885 | 2486 | // After folding, B should be gone but merge should still exist. |
| 1886 | 2487 | let f = &m.functions[0]; |
| 1887 | - assert!(!f.blocks.iter().any(|bk| bk.id == b), "block B should be pruned"); | |
| 1888 | - assert!(f.blocks.iter().any(|bk| bk.id == merge), "merge should remain"); | |
| 2488 | + assert!( | |
| 2489 | + !f.blocks.iter().any(|bk| bk.id == b), | |
| 2490 | + "block B should be pruned" | |
| 2491 | + ); | |
| 2492 | + assert!( | |
| 2493 | + f.blocks.iter().any(|bk| bk.id == merge), | |
| 2494 | + "merge should remain" | |
| 2495 | + ); | |
| 1889 | 2496 | } |
src/opt/bce.rsmodified@@ -13,20 +13,24 @@ | ||
| 13 | 13 | //! element accesses. This pass removes the checks when the index is |
| 14 | 14 | //! provably safe. |
| 15 | 15 | |
| 16 | -use crate::ir::inst::*; | |
| 17 | -use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 18 | 16 | use super::loop_utils::{find_preheader, resolve_const_int}; |
| 19 | 17 | use super::pass::Pass; |
| 18 | +use crate::ir::inst::*; | |
| 19 | +use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 20 | 20 | |
| 21 | 21 | pub struct Bce; |
| 22 | 22 | |
| 23 | 23 | impl Pass for Bce { |
| 24 | - fn name(&self) -> &'static str { "bce" } | |
| 24 | + fn name(&self) -> &'static str { | |
| 25 | + "bce" | |
| 26 | + } | |
| 25 | 27 | |
| 26 | 28 | fn run(&self, module: &mut Module) -> bool { |
| 27 | 29 | let mut changed = false; |
| 28 | 30 | for func in &mut module.functions { |
| 29 | - if bce_function(func) { changed = true; } | |
| 31 | + if bce_function(func) { | |
| 32 | + changed = true; | |
| 33 | + } | |
| 30 | 34 | } |
| 31 | 35 | changed |
| 32 | 36 | } |
@@ -53,7 +57,9 @@ fn bce_function(func: &mut Function) -> bool { | ||
| 53 | 57 | } |
| 54 | 58 | } |
| 55 | 59 | |
| 56 | - if to_remove.is_empty() { return false; } | |
| 60 | + if to_remove.is_empty() { | |
| 61 | + return false; | |
| 62 | + } | |
| 57 | 63 | |
| 58 | 64 | // Remove in reverse order to preserve indices. |
| 59 | 65 | to_remove.sort_by(|a, b| b.1.cmp(&a.1)); |
@@ -86,8 +92,12 @@ fn is_provably_safe( | ||
| 86 | 92 | |
| 87 | 93 | // Case 2: index is a canonical loop IV, and the loop's closed range |
| 88 | 94 | // stays within the checked bounds. |
| 89 | - let Some(lo_const) = resolve_int_scalar(func, lower) else { return false; }; | |
| 90 | - let Some(hi_const) = resolve_int_scalar(func, upper) else { return false; }; | |
| 95 | + let Some(lo_const) = resolve_int_scalar(func, lower) else { | |
| 96 | + return false; | |
| 97 | + }; | |
| 98 | + let Some(hi_const) = resolve_int_scalar(func, upper) else { | |
| 99 | + return false; | |
| 100 | + }; | |
| 91 | 101 | for lp in loops { |
| 92 | 102 | let Some((range_lo, range_hi)) = loop_index_range(func, lp, preds, index) else { |
| 93 | 103 | continue; |
@@ -178,9 +188,7 @@ fn loop_init_const( | ||
| 178 | 188 | ) -> Option<i64> { |
| 179 | 189 | let preheader = find_preheader(func, lp, preds)?; |
| 180 | 190 | match &func.block(preheader).terminator { |
| 181 | - Some(Terminator::Branch(dest, args)) | |
| 182 | - if *dest == lp.header && param_idx < args.len() => | |
| 183 | - { | |
| 191 | + Some(Terminator::Branch(dest, args)) if *dest == lp.header && param_idx < args.len() => { | |
| 184 | 192 | resolve_int_scalar(func, args[param_idx]) |
| 185 | 193 | } |
| 186 | 194 | _ => None, |
@@ -193,18 +201,15 @@ enum LoopDir { | ||
| 193 | 201 | Descending, |
| 194 | 202 | } |
| 195 | 203 | |
| 196 | -fn loop_bound_const( | |
| 197 | - func: &Function, | |
| 198 | - header: BlockId, | |
| 199 | - index: ValueId, | |
| 200 | -) -> Option<(i64, LoopDir)> { | |
| 204 | +fn loop_bound_const(func: &Function, header: BlockId, index: ValueId) -> Option<(i64, LoopDir)> { | |
| 201 | 205 | for inst in &func.block(header).insts { |
| 202 | - let InstKind::ICmp(op, lhs, rhs) = &inst.kind else { continue }; | |
| 206 | + let InstKind::ICmp(op, lhs, rhs) = &inst.kind else { | |
| 207 | + continue; | |
| 208 | + }; | |
| 203 | 209 | match op { |
| 204 | 210 | CmpOp::Le => { |
| 205 | 211 | if *lhs == index { |
| 206 | - return resolve_int_scalar(func, *rhs) | |
| 207 | - .map(|bound| (bound, LoopDir::Ascending)); | |
| 212 | + return resolve_int_scalar(func, *rhs).map(|bound| (bound, LoopDir::Ascending)); | |
| 208 | 213 | } |
| 209 | 214 | if *rhs == index { |
| 210 | 215 | return resolve_int_scalar(func, *lhs) |
@@ -217,8 +222,7 @@ fn loop_bound_const( | ||
| 217 | 222 | .map(|bound| (bound, LoopDir::Descending)); |
| 218 | 223 | } |
| 219 | 224 | if *rhs == index { |
| 220 | - return resolve_int_scalar(func, *lhs) | |
| 221 | - .map(|bound| (bound, LoopDir::Ascending)); | |
| 225 | + return resolve_int_scalar(func, *lhs).map(|bound| (bound, LoopDir::Ascending)); | |
| 222 | 226 | } |
| 223 | 227 | } |
| 224 | 228 | _ => {} |
@@ -235,8 +239,7 @@ fn loop_step_const( | ||
| 235 | 239 | ) -> Option<i64> { |
| 236 | 240 | let mut step: Option<i64> = None; |
| 237 | 241 | for &latch in &lp.latches { |
| 238 | - let next = | |
| 239 | - edge_arg_value(func.block(latch).terminator.as_ref()?, lp.header, param_idx)?; | |
| 242 | + let next = edge_arg_value(func.block(latch).terminator.as_ref()?, lp.header, param_idx)?; | |
| 240 | 243 | let latch_step = update_step_const(func, index, next)?; |
| 241 | 244 | if latch_step == 0 { |
| 242 | 245 | return None; |
@@ -309,7 +312,9 @@ fn resolve_int_scalar(func: &Function, value: ValueId) -> Option<i64> { | ||
| 309 | 312 | |
| 310 | 313 | fn strip_int_casts(func: &Function, mut value: ValueId) -> ValueId { |
| 311 | 314 | loop { |
| 312 | - let Some(kind) = find_inst_kind(func, value) else { return value }; | |
| 315 | + let Some(kind) = find_inst_kind(func, value) else { | |
| 316 | + return value; | |
| 317 | + }; | |
| 313 | 318 | match kind { |
| 314 | 319 | InstKind::IntExtend(src, _, _) | InstKind::IntTrunc(src, _) => value = *src, |
| 315 | 320 | _ => return value, |
@@ -331,7 +336,7 @@ fn find_inst_kind(func: &Function, value: ValueId) -> Option<&InstKind> { | ||
| 331 | 336 | #[cfg(test)] |
| 332 | 337 | mod tests { |
| 333 | 338 | use super::*; |
| 334 | - use crate::ir::types::{IrType, IntWidth}; | |
| 339 | + use crate::ir::types::{IntWidth, IrType}; | |
| 335 | 340 | use crate::opt::pass::Pass; |
| 336 | 341 | |
| 337 | 342 | #[test] |
@@ -358,25 +363,33 @@ mod tests { | ||
| 358 | 363 | let idx = f.next_value_id(); |
| 359 | 364 | f.register_type(idx, IrType::Int(IntWidth::I32)); |
| 360 | 365 | f.block_mut(f.entry).insts.push(Inst { |
| 361 | - id: idx, ty: IrType::Int(IntWidth::I32), span, | |
| 366 | + id: idx, | |
| 367 | + ty: IrType::Int(IntWidth::I32), | |
| 368 | + span, | |
| 362 | 369 | kind: InstKind::ConstInt(3, IntWidth::I32), |
| 363 | 370 | }); |
| 364 | 371 | let lo = f.next_value_id(); |
| 365 | 372 | f.register_type(lo, IrType::Int(IntWidth::I32)); |
| 366 | 373 | f.block_mut(f.entry).insts.push(Inst { |
| 367 | - id: lo, ty: IrType::Int(IntWidth::I32), span, | |
| 374 | + id: lo, | |
| 375 | + ty: IrType::Int(IntWidth::I32), | |
| 376 | + span, | |
| 368 | 377 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 369 | 378 | }); |
| 370 | 379 | let hi = f.next_value_id(); |
| 371 | 380 | f.register_type(hi, IrType::Int(IntWidth::I32)); |
| 372 | 381 | f.block_mut(f.entry).insts.push(Inst { |
| 373 | - id: hi, ty: IrType::Int(IntWidth::I32), span, | |
| 382 | + id: hi, | |
| 383 | + ty: IrType::Int(IntWidth::I32), | |
| 384 | + span, | |
| 374 | 385 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 375 | 386 | }); |
| 376 | 387 | let check = f.next_value_id(); |
| 377 | 388 | f.register_type(check, IrType::Void); |
| 378 | 389 | f.block_mut(f.entry).insts.push(Inst { |
| 379 | - id: check, ty: IrType::Void, span, | |
| 390 | + id: check, | |
| 391 | + ty: IrType::Void, | |
| 392 | + span, | |
| 380 | 393 | kind: InstKind::RuntimeCall(RuntimeFunc::CheckBounds, vec![idx, lo, hi]), |
| 381 | 394 | }); |
| 382 | 395 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -387,7 +400,9 @@ mod tests { | ||
| 387 | 400 | assert!(changed, "constant 3 in [1,10] should be eliminated"); |
| 388 | 401 | |
| 389 | 402 | // Verify the CheckBounds call was removed. |
| 390 | - let has_check = m.functions[0].blocks[0].insts.iter() | |
| 403 | + let has_check = m.functions[0].blocks[0] | |
| 404 | + .insts | |
| 405 | + .iter() | |
| 391 | 406 | .any(|i| matches!(i.kind, InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _))); |
| 392 | 407 | assert!(!has_check, "CheckBounds should be removed"); |
| 393 | 408 | } |
@@ -428,8 +443,14 @@ mod tests { | ||
| 428 | 443 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 429 | 444 | let sum = f.next_value_id(); |
| 430 | 445 | f.register_type(sum, IrType::Int(IntWidth::I32)); |
| 431 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 432 | - f.block_mut(header).params.push(BlockParam { id: sum, ty: IrType::Int(IntWidth::I32) }); | |
| 446 | + f.block_mut(header).params.push(BlockParam { | |
| 447 | + id: iv, | |
| 448 | + ty: IrType::Int(IntWidth::I32), | |
| 449 | + }); | |
| 450 | + f.block_mut(header).params.push(BlockParam { | |
| 451 | + id: sum, | |
| 452 | + ty: IrType::Int(IntWidth::I32), | |
| 453 | + }); | |
| 433 | 454 | |
| 434 | 455 | let bound = f.next_value_id(); |
| 435 | 456 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -511,15 +532,20 @@ mod tests { | ||
| 511 | 532 | span, |
| 512 | 533 | kind: InstKind::IAdd(sum, step), |
| 513 | 534 | }); |
| 514 | - f.block_mut(body).terminator = | |
| 515 | - Some(Terminator::Branch(header, vec![next_iv, next_sum])); | |
| 535 | + f.block_mut(body).terminator = Some(Terminator::Branch(header, vec![next_iv, next_sum])); | |
| 516 | 536 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 517 | 537 | m.add_function(f); |
| 518 | 538 | |
| 519 | 539 | let pass = Bce; |
| 520 | - assert!(pass.run(&mut m), "canonical counted-loop bounds check should be removed"); | |
| 540 | + assert!( | |
| 541 | + pass.run(&mut m), | |
| 542 | + "canonical counted-loop bounds check should be removed" | |
| 543 | + ); | |
| 521 | 544 | let has_check = m.functions[0].block(body).insts.iter().any(|inst| { |
| 522 | - matches!(inst.kind, InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _)) | |
| 545 | + matches!( | |
| 546 | + inst.kind, | |
| 547 | + InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _) | |
| 548 | + ) | |
| 523 | 549 | }); |
| 524 | 550 | assert!(!has_check, "loop body should no longer contain CheckBounds"); |
| 525 | 551 | } |
@@ -560,8 +586,14 @@ mod tests { | ||
| 560 | 586 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 561 | 587 | let sum = f.next_value_id(); |
| 562 | 588 | f.register_type(sum, IrType::Int(IntWidth::I32)); |
| 563 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 564 | - f.block_mut(header).params.push(BlockParam { id: sum, ty: IrType::Int(IntWidth::I32) }); | |
| 589 | + f.block_mut(header).params.push(BlockParam { | |
| 590 | + id: iv, | |
| 591 | + ty: IrType::Int(IntWidth::I32), | |
| 592 | + }); | |
| 593 | + f.block_mut(header).params.push(BlockParam { | |
| 594 | + id: sum, | |
| 595 | + ty: IrType::Int(IntWidth::I32), | |
| 596 | + }); | |
| 565 | 597 | |
| 566 | 598 | let bound = f.next_value_id(); |
| 567 | 599 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -643,8 +675,7 @@ mod tests { | ||
| 643 | 675 | span, |
| 644 | 676 | kind: InstKind::IAdd(sum, step), |
| 645 | 677 | }); |
| 646 | - f.block_mut(body).terminator = | |
| 647 | - Some(Terminator::Branch(header, vec![next_iv, next_sum])); | |
| 678 | + f.block_mut(body).terminator = Some(Terminator::Branch(header, vec![next_iv, next_sum])); | |
| 648 | 679 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 649 | 680 | m.add_function(f); |
| 650 | 681 | |
@@ -681,7 +712,10 @@ mod tests { | ||
| 681 | 712 | |
| 682 | 713 | let iv = f.next_value_id(); |
| 683 | 714 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 684 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 715 | + f.block_mut(header).params.push(BlockParam { | |
| 716 | + id: iv, | |
| 717 | + ty: IrType::Int(IntWidth::I32), | |
| 718 | + }); | |
| 685 | 719 | |
| 686 | 720 | let bound = f.next_value_id(); |
| 687 | 721 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -768,9 +802,15 @@ mod tests { | ||
| 768 | 802 | m.add_function(f); |
| 769 | 803 | |
| 770 | 804 | let pass = Bce; |
| 771 | - assert!(pass.run(&mut m), "iv+1 check should be removed for trip range 2..7 and checked bounds 1..8"); | |
| 805 | + assert!( | |
| 806 | + pass.run(&mut m), | |
| 807 | + "iv+1 check should be removed for trip range 2..7 and checked bounds 1..8" | |
| 808 | + ); | |
| 772 | 809 | let has_check = m.functions[0].block(body).insts.iter().any(|inst| { |
| 773 | - matches!(inst.kind, InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _)) | |
| 810 | + matches!( | |
| 811 | + inst.kind, | |
| 812 | + InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _) | |
| 813 | + ) | |
| 774 | 814 | }); |
| 775 | 815 | assert!(!has_check, "loop body should no longer contain CheckBounds"); |
| 776 | 816 | } |
@@ -801,7 +841,10 @@ mod tests { | ||
| 801 | 841 | |
| 802 | 842 | let iv = f.next_value_id(); |
| 803 | 843 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 804 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 844 | + f.block_mut(header).params.push(BlockParam { | |
| 845 | + id: iv, | |
| 846 | + ty: IrType::Int(IntWidth::I32), | |
| 847 | + }); | |
| 805 | 848 | |
| 806 | 849 | let bound = f.next_value_id(); |
| 807 | 850 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -888,9 +931,15 @@ mod tests { | ||
| 888 | 931 | m.add_function(f); |
| 889 | 932 | |
| 890 | 933 | let pass = Bce; |
| 891 | - assert!(pass.run(&mut m), "iv-1 check should be removed for trip range 2..7 and checked bounds 1..8"); | |
| 934 | + assert!( | |
| 935 | + pass.run(&mut m), | |
| 936 | + "iv-1 check should be removed for trip range 2..7 and checked bounds 1..8" | |
| 937 | + ); | |
| 892 | 938 | let has_check = m.functions[0].block(body).insts.iter().any(|inst| { |
| 893 | - matches!(inst.kind, InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _)) | |
| 939 | + matches!( | |
| 940 | + inst.kind, | |
| 941 | + InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _) | |
| 942 | + ) | |
| 894 | 943 | }); |
| 895 | 944 | assert!(!has_check, "loop body should no longer contain CheckBounds"); |
| 896 | 945 | } |
@@ -921,7 +970,10 @@ mod tests { | ||
| 921 | 970 | |
| 922 | 971 | let iv = f.next_value_id(); |
| 923 | 972 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 924 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 973 | + f.block_mut(header).params.push(BlockParam { | |
| 974 | + id: iv, | |
| 975 | + ty: IrType::Int(IntWidth::I32), | |
| 976 | + }); | |
| 925 | 977 | |
| 926 | 978 | let bound = f.next_value_id(); |
| 927 | 979 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -1048,7 +1100,10 @@ mod tests { | ||
| 1048 | 1100 | |
| 1049 | 1101 | let iv = f.next_value_id(); |
| 1050 | 1102 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 1051 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 1103 | + f.block_mut(header).params.push(BlockParam { | |
| 1104 | + id: iv, | |
| 1105 | + ty: IrType::Int(IntWidth::I32), | |
| 1106 | + }); | |
| 1052 | 1107 | |
| 1053 | 1108 | let bound = f.next_value_id(); |
| 1054 | 1109 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
@@ -1148,7 +1203,10 @@ mod tests { | ||
| 1148 | 1203 | "iv+5 check should be removed when the trip sequence is only 5 and checked bounds are 1..10" |
| 1149 | 1204 | ); |
| 1150 | 1205 | let has_check = m.functions[0].block(body).insts.iter().any(|inst| { |
| 1151 | - matches!(inst.kind, InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _)) | |
| 1206 | + matches!( | |
| 1207 | + inst.kind, | |
| 1208 | + InstKind::RuntimeCall(RuntimeFunc::CheckBounds, _) | |
| 1209 | + ) | |
| 1152 | 1210 | }); |
| 1153 | 1211 | assert!(!has_check, "loop body should no longer contain CheckBounds"); |
| 1154 | 1212 | } |
src/opt/call_resolve.rsmodified@@ -7,18 +7,22 @@ | ||
| 7 | 7 | //! |
| 8 | 8 | //! Runs as the first pass at O1+ (before Mem2Reg). |
| 9 | 9 | |
| 10 | -use std::collections::HashMap; | |
| 11 | -use crate::ir::inst::*; | |
| 12 | 10 | use super::pass::Pass; |
| 11 | +use crate::ir::inst::*; | |
| 12 | +use std::collections::HashMap; | |
| 13 | 13 | |
| 14 | 14 | pub struct CallResolve; |
| 15 | 15 | |
| 16 | 16 | impl Pass for CallResolve { |
| 17 | - fn name(&self) -> &'static str { "call-resolve" } | |
| 17 | + fn name(&self) -> &'static str { | |
| 18 | + "call-resolve" | |
| 19 | + } | |
| 18 | 20 | |
| 19 | 21 | fn run(&self, module: &mut Module) -> bool { |
| 20 | 22 | // Build name → index mapping for all functions in the module. |
| 21 | - let name_to_idx: HashMap<String, u32> = module.functions.iter() | |
| 23 | + let name_to_idx: HashMap<String, u32> = module | |
| 24 | + .functions | |
| 25 | + .iter() | |
| 22 | 26 | .enumerate() |
| 23 | 27 | .map(|(i, f)| (f.name.clone(), i as u32)) |
| 24 | 28 | .collect(); |
@@ -47,8 +51,8 @@ impl Pass for CallResolve { | ||
| 47 | 51 | #[cfg(test)] |
| 48 | 52 | mod tests { |
| 49 | 53 | use super::*; |
| 50 | - use crate::ir::types::{IrType, IntWidth}; | |
| 51 | 54 | use crate::ir::inst::*; |
| 55 | + use crate::ir::types::{IntWidth, IrType}; | |
| 52 | 56 | use crate::opt::pass::Pass; |
| 53 | 57 | |
| 54 | 58 | #[test] |
src/opt/callgraph.rsmodified@@ -4,9 +4,9 @@ | ||
| 4 | 4 | //! Detects recursive functions via DFS cycle detection. Provides |
| 5 | 5 | //! reverse post-order iteration for bottom-up inlining (callees first). |
| 6 | 6 | |
| 7 | -use std::collections::HashSet; | |
| 8 | 7 | use crate::ir::inst::*; |
| 9 | 8 | use crate::ir::walk::find_natural_loops; |
| 9 | +use std::collections::HashSet; | |
| 10 | 10 | |
| 11 | 11 | /// A node in the call graph — one per function in the module. |
| 12 | 12 | #[derive(Debug)] |
@@ -33,13 +33,15 @@ impl CallGraph { | ||
| 33 | 33 | /// Build the call graph from a module. |
| 34 | 34 | pub fn build(module: &Module) -> Self { |
| 35 | 35 | let n = module.functions.len(); |
| 36 | - let mut nodes: Vec<CallNode> = (0..n).map(|i| CallNode { | |
| 37 | - func_idx: i as u32, | |
| 38 | - callees: Vec::new(), | |
| 39 | - callers: Vec::new(), | |
| 40 | - inst_count: 0, | |
| 41 | - is_recursive: false, | |
| 42 | - }).collect(); | |
| 36 | + let mut nodes: Vec<CallNode> = (0..n) | |
| 37 | + .map(|i| CallNode { | |
| 38 | + func_idx: i as u32, | |
| 39 | + callees: Vec::new(), | |
| 40 | + callers: Vec::new(), | |
| 41 | + inst_count: 0, | |
| 42 | + is_recursive: false, | |
| 43 | + }) | |
| 44 | + .collect(); | |
| 43 | 45 | |
| 44 | 46 | // Scan each function for Call(Internal) instructions. |
| 45 | 47 | for (i, func) in module.functions.iter().enumerate() { |
@@ -122,7 +124,9 @@ impl CallGraph { | ||
| 122 | 124 | } |
| 123 | 125 | |
| 124 | 126 | fn rpo_dfs(nodes: &[CallNode], idx: u32, visited: &mut [bool], order: &mut Vec<u32>) { |
| 125 | - if visited[idx as usize] { return; } | |
| 127 | + if visited[idx as usize] { | |
| 128 | + return; | |
| 129 | + } | |
| 126 | 130 | visited[idx as usize] = true; |
| 127 | 131 | for &callee in &nodes[idx as usize].callees { |
| 128 | 132 | if (callee as usize) < nodes.len() { |
@@ -154,12 +158,16 @@ fn dfs_cycle(nodes: &[CallNode], idx: u32, visiting: &mut HashSet<u32>) -> bool | ||
| 154 | 158 | #[cfg(test)] |
| 155 | 159 | mod tests { |
| 156 | 160 | use super::*; |
| 157 | - use crate::ir::types::{IrType, IntWidth}; | |
| 158 | - use crate::lexer::{Span, Position}; | |
| 161 | + use crate::ir::types::{IntWidth, IrType}; | |
| 162 | + use crate::lexer::{Position, Span}; | |
| 159 | 163 | |
| 160 | 164 | fn span() -> Span { |
| 161 | 165 | let pos = Position { line: 0, col: 0 }; |
| 162 | - Span { file_id: 0, start: pos, end: pos } | |
| 166 | + Span { | |
| 167 | + file_id: 0, | |
| 168 | + start: pos, | |
| 169 | + end: pos, | |
| 170 | + } | |
| 163 | 171 | } |
| 164 | 172 | |
| 165 | 173 | #[test] |
@@ -170,14 +178,19 @@ mod tests { | ||
| 170 | 178 | let call_id = f.next_value_id(); |
| 171 | 179 | f.register_type(call_id, IrType::Int(IntWidth::I32)); |
| 172 | 180 | f.block_mut(f.entry).insts.push(Inst { |
| 173 | - id: call_id, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 181 | + id: call_id, | |
| 182 | + ty: IrType::Int(IntWidth::I32), | |
| 183 | + span: span(), | |
| 174 | 184 | kind: InstKind::Call(FuncRef::Internal(0), vec![]), |
| 175 | 185 | }); |
| 176 | 186 | f.block_mut(f.entry).terminator = Some(Terminator::Return(Some(call_id))); |
| 177 | 187 | m.add_function(f); |
| 178 | 188 | |
| 179 | 189 | let cg = CallGraph::build(&m); |
| 180 | - assert!(cg.is_recursive(0), "self-calling function should be recursive"); | |
| 190 | + assert!( | |
| 191 | + cg.is_recursive(0), | |
| 192 | + "self-calling function should be recursive" | |
| 193 | + ); | |
| 181 | 194 | } |
| 182 | 195 | |
| 183 | 196 | #[test] |
@@ -193,7 +206,9 @@ mod tests { | ||
| 193 | 206 | let call_id = caller.next_value_id(); |
| 194 | 207 | caller.register_type(call_id, IrType::Int(IntWidth::I32)); |
| 195 | 208 | caller.block_mut(caller.entry).insts.push(Inst { |
| 196 | - id: call_id, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 209 | + id: call_id, | |
| 210 | + ty: IrType::Int(IntWidth::I32), | |
| 211 | + span: span(), | |
| 197 | 212 | kind: InstKind::Call(FuncRef::Internal(0), vec![]), |
| 198 | 213 | }); |
| 199 | 214 | caller.block_mut(caller.entry).terminator = Some(Terminator::Return(None)); |
@@ -218,7 +233,9 @@ mod tests { | ||
| 218 | 233 | let cid = caller.next_value_id(); |
| 219 | 234 | caller.register_type(cid, IrType::Void); |
| 220 | 235 | caller.block_mut(caller.entry).insts.push(Inst { |
| 221 | - id: cid, ty: IrType::Void, span: span(), | |
| 236 | + id: cid, | |
| 237 | + ty: IrType::Void, | |
| 238 | + span: span(), | |
| 222 | 239 | kind: InstKind::Call(FuncRef::Internal(0), vec![]), |
| 223 | 240 | }); |
| 224 | 241 | caller.block_mut(caller.entry).terminator = Some(Terminator::Return(None)); |
@@ -229,6 +246,9 @@ mod tests { | ||
| 229 | 246 | // Callee should come before caller in RPO. |
| 230 | 247 | let callee_pos = rpo.iter().position(|&i| i == 0).unwrap(); |
| 231 | 248 | let caller_pos = rpo.iter().position(|&i| i == 1).unwrap(); |
| 232 | - assert!(callee_pos < caller_pos, "callee should come before caller in RPO"); | |
| 249 | + assert!( | |
| 250 | + callee_pos < caller_pos, | |
| 251 | + "callee should come before caller in RPO" | |
| 252 | + ); | |
| 233 | 253 | } |
| 234 | 254 | } |
src/opt/const_arg.rsmodified@@ -16,13 +16,20 @@ use super::util::substitute_uses; | ||
| 16 | 16 | pub struct ConstArgSpecialize; |
| 17 | 17 | |
| 18 | 18 | impl Pass for ConstArgSpecialize { |
| 19 | - fn name(&self) -> &'static str { "const-arg-specialize" } | |
| 19 | + fn name(&self) -> &'static str { | |
| 20 | + "const-arg-specialize" | |
| 21 | + } | |
| 20 | 22 | |
| 21 | 23 | fn run(&self, module: &mut Module) -> bool { |
| 22 | 24 | let param_modes: Vec<Vec<Option<ParamMode>>> = module |
| 23 | 25 | .functions |
| 24 | 26 | .iter() |
| 25 | - .map(|func| func.params.iter().map(|param| classify_param(func, param)).collect()) | |
| 27 | + .map(|func| { | |
| 28 | + func.params | |
| 29 | + .iter() | |
| 30 | + .map(|param| classify_param(func, param)) | |
| 31 | + .collect() | |
| 32 | + }) | |
| 26 | 33 | .collect(); |
| 27 | 34 | |
| 28 | 35 | let plans = specialization_plans(module, ¶m_modes); |
@@ -309,7 +316,11 @@ fn default_span(func: &Function) -> Span { | ||
| 309 | 316 | span |
| 310 | 317 | } else { |
| 311 | 318 | let pos = Position { line: 0, col: 0 }; |
| 312 | - Span { file_id: 0, start: pos, end: pos } | |
| 319 | + Span { | |
| 320 | + file_id: 0, | |
| 321 | + start: pos, | |
| 322 | + end: pos, | |
| 323 | + } | |
| 313 | 324 | } |
| 314 | 325 | } |
| 315 | 326 | |
@@ -372,18 +383,27 @@ fn apply_plan(module: &mut Module, plan: SpecializationPlan) { | ||
| 372 | 383 | mod tests { |
| 373 | 384 | use super::*; |
| 374 | 385 | use crate::ir::types::IrType; |
| 375 | - use crate::opt::dead_arg::DeadArgElim; | |
| 376 | 386 | use crate::lexer::{Position, Span}; |
| 387 | + use crate::opt::dead_arg::DeadArgElim; | |
| 377 | 388 | |
| 378 | 389 | fn span() -> Span { |
| 379 | 390 | let pos = Position { line: 0, col: 0 }; |
| 380 | - Span { file_id: 0, start: pos, end: pos } | |
| 391 | + Span { | |
| 392 | + file_id: 0, | |
| 393 | + start: pos, | |
| 394 | + end: pos, | |
| 395 | + } | |
| 381 | 396 | } |
| 382 | 397 | |
| 383 | 398 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 384 | 399 | let id = f.next_value_id(); |
| 385 | 400 | let entry = f.entry; |
| 386 | - f.block_mut(entry).insts.push(Inst { id, kind, ty: ty.clone(), span: span() }); | |
| 401 | + f.block_mut(entry).insts.push(Inst { | |
| 402 | + id, | |
| 403 | + kind, | |
| 404 | + ty: ty.clone(), | |
| 405 | + span: span(), | |
| 406 | + }); | |
| 387 | 407 | f.register_type(id, ty); |
| 388 | 408 | id |
| 389 | 409 | } |
@@ -418,21 +438,41 @@ mod tests { | ||
| 418 | 438 | module.add_function(callee); |
| 419 | 439 | |
| 420 | 440 | let mut caller = Function::new("main".into(), vec![], IrType::Int(IntWidth::I32)); |
| 421 | - let x1 = push(&mut caller, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 422 | - let step1 = push(&mut caller, InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 441 | + let x1 = push( | |
| 442 | + &mut caller, | |
| 443 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 444 | + IrType::Int(IntWidth::I32), | |
| 445 | + ); | |
| 446 | + let step1 = push( | |
| 447 | + &mut caller, | |
| 448 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 449 | + IrType::Int(IntWidth::I32), | |
| 450 | + ); | |
| 423 | 451 | let c1 = push( |
| 424 | 452 | &mut caller, |
| 425 | 453 | InstKind::Call(FuncRef::Internal(0), vec![x1, step1]), |
| 426 | 454 | IrType::Int(IntWidth::I32), |
| 427 | 455 | ); |
| 428 | - let x2 = push(&mut caller, InstKind::ConstInt(9, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 429 | - let step2 = push(&mut caller, InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 456 | + let x2 = push( | |
| 457 | + &mut caller, | |
| 458 | + InstKind::ConstInt(9, IntWidth::I32), | |
| 459 | + IrType::Int(IntWidth::I32), | |
| 460 | + ); | |
| 461 | + let step2 = push( | |
| 462 | + &mut caller, | |
| 463 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 464 | + IrType::Int(IntWidth::I32), | |
| 465 | + ); | |
| 430 | 466 | let c2 = push( |
| 431 | 467 | &mut caller, |
| 432 | 468 | InstKind::Call(FuncRef::Internal(0), vec![x2, step2]), |
| 433 | 469 | IrType::Int(IntWidth::I32), |
| 434 | 470 | ); |
| 435 | - let total = push(&mut caller, InstKind::IAdd(c1, c2), IrType::Int(IntWidth::I32)); | |
| 471 | + let total = push( | |
| 472 | + &mut caller, | |
| 473 | + InstKind::IAdd(c1, c2), | |
| 474 | + IrType::Int(IntWidth::I32), | |
| 475 | + ); | |
| 436 | 476 | let caller_entry = caller.entry; |
| 437 | 477 | caller.block_mut(caller_entry).terminator = Some(Terminator::Return(Some(total))); |
| 438 | 478 | module.add_function(caller); |
@@ -440,12 +480,20 @@ mod tests { | ||
| 440 | 480 | assert!(ConstArgSpecialize.run(&mut module)); |
| 441 | 481 | assert!(DeadArgElim.run(&mut module)); |
| 442 | 482 | |
| 443 | - assert_eq!(module.functions[0].params.len(), 1, "specialized helper should drop constant dummy"); | |
| 483 | + assert_eq!( | |
| 484 | + module.functions[0].params.len(), | |
| 485 | + 1, | |
| 486 | + "specialized helper should drop constant dummy" | |
| 487 | + ); | |
| 444 | 488 | let call_args_1 = match &module.functions[1].blocks[0].insts[2].kind { |
| 445 | 489 | InstKind::Call(FuncRef::Internal(0), args) => args, |
| 446 | 490 | other => panic!("expected call, got {:?}", other), |
| 447 | 491 | }; |
| 448 | - assert_eq!(call_args_1.len(), 1, "call site should drop specialized constant arg"); | |
| 492 | + assert_eq!( | |
| 493 | + call_args_1.len(), | |
| 494 | + 1, | |
| 495 | + "call site should drop specialized constant arg" | |
| 496 | + ); | |
| 449 | 497 | } |
| 450 | 498 | |
| 451 | 499 | #[test] |
@@ -478,15 +526,31 @@ mod tests { | ||
| 478 | 526 | module.add_function(callee); |
| 479 | 527 | |
| 480 | 528 | let mut caller = Function::new("main".into(), vec![], IrType::Int(IntWidth::I32)); |
| 481 | - let x1 = push(&mut caller, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 482 | - let step1 = push(&mut caller, InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 529 | + let x1 = push( | |
| 530 | + &mut caller, | |
| 531 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 532 | + IrType::Int(IntWidth::I32), | |
| 533 | + ); | |
| 534 | + let step1 = push( | |
| 535 | + &mut caller, | |
| 536 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 537 | + IrType::Int(IntWidth::I32), | |
| 538 | + ); | |
| 483 | 539 | let _ = push( |
| 484 | 540 | &mut caller, |
| 485 | 541 | InstKind::Call(FuncRef::Internal(0), vec![x1, step1]), |
| 486 | 542 | IrType::Int(IntWidth::I32), |
| 487 | 543 | ); |
| 488 | - let x2 = push(&mut caller, InstKind::ConstInt(9, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 489 | - let step2 = push(&mut caller, InstKind::ConstInt(4, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 544 | + let x2 = push( | |
| 545 | + &mut caller, | |
| 546 | + InstKind::ConstInt(9, IntWidth::I32), | |
| 547 | + IrType::Int(IntWidth::I32), | |
| 548 | + ); | |
| 549 | + let step2 = push( | |
| 550 | + &mut caller, | |
| 551 | + InstKind::ConstInt(4, IntWidth::I32), | |
| 552 | + IrType::Int(IntWidth::I32), | |
| 553 | + ); | |
| 490 | 554 | let call = push( |
| 491 | 555 | &mut caller, |
| 492 | 556 | InstKind::Call(FuncRef::Internal(0), vec![x2, step2]), |
src/opt/const_fold.rsmodified@@ -27,7 +27,7 @@ | ||
| 27 | 27 | |
| 28 | 28 | use super::pass::Pass; |
| 29 | 29 | use crate::ir::inst::*; |
| 30 | -use crate::ir::types::{IrType, IntWidth, FloatWidth}; | |
| 30 | +use crate::ir::types::{FloatWidth, IntWidth, IrType}; | |
| 31 | 31 | use std::collections::HashMap; |
| 32 | 32 | |
| 33 | 33 | /// Compile-time constant value, normalized for folding. |
@@ -56,12 +56,8 @@ impl Const { | ||
| 56 | 56 | /// the producer-side guarantee from M-1. |
| 57 | 57 | fn from_inst(kind: &InstKind) -> Option<Self> { |
| 58 | 58 | match kind { |
| 59 | - InstKind::ConstInt(v, w) => { | |
| 60 | - Some(Const::Int(sext(*v, w.bits()), *w)) | |
| 61 | - } | |
| 62 | - InstKind::ConstFloat(v, w) => { | |
| 63 | - Some(Const::Float(round_for_width(*v, *w), *w)) | |
| 64 | - } | |
| 59 | + InstKind::ConstInt(v, w) => Some(Const::Int(sext(*v, w.bits()), *w)), | |
| 60 | + InstKind::ConstFloat(v, w) => Some(Const::Float(round_for_width(*v, *w), *w)), | |
| 65 | 61 | InstKind::ConstBool(b) => Some(Const::Bool(*b)), |
| 66 | 62 | _ => None, |
| 67 | 63 | } |
@@ -71,8 +67,9 @@ impl Const { | ||
| 71 | 67 | /// Sign-extend `v` from `bits` to a full i128. Used so that comparisons |
| 72 | 68 | /// and arithmetic on narrower integer types match hardware behavior. |
| 73 | 69 | fn sext(v: i128, bits: u32) -> i128 { |
| 74 | - if bits >= 128 { v } | |
| 75 | - else { | |
| 70 | + if bits >= 128 { | |
| 71 | + v | |
| 72 | + } else { | |
| 76 | 73 | let shift = 128 - bits; |
| 77 | 74 | (v << shift) >> shift |
| 78 | 75 | } |
@@ -81,8 +78,11 @@ fn sext(v: i128, bits: u32) -> i128 { | ||
| 81 | 78 | /// Mask `v` down to `bits` low bits. The IR stores integers as i128 but |
| 82 | 79 | /// arithmetic must wrap at the declared width. |
| 83 | 80 | fn mask(v: i128, bits: u32) -> i128 { |
| 84 | - if bits >= 128 { v } | |
| 85 | - else { v & ((1i128 << bits) - 1) } | |
| 81 | + if bits >= 128 { | |
| 82 | + v | |
| 83 | + } else { | |
| 84 | + v & ((1i128 << bits) - 1) | |
| 85 | + } | |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | 88 | /// Truncate-then-sign-extend a wide arithmetic result back to its |
@@ -149,8 +149,12 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 149 | 149 | InstKind::IDiv(a, b) => { |
| 150 | 150 | if let (Some(Const::Int(av, _)), Some(Const::Int(bv, _))) = (get(a), get(b)) { |
| 151 | 151 | if let IrType::Int(w) = ty { |
| 152 | - if bv == 0 { return None; } // leave divide-by-zero to runtime | |
| 153 | - if av == signed_min(*w) && bv == -1 { return None; } | |
| 152 | + if bv == 0 { | |
| 153 | + return None; | |
| 154 | + } // leave divide-by-zero to runtime | |
| 155 | + if av == signed_min(*w) && bv == -1 { | |
| 156 | + return None; | |
| 157 | + } | |
| 154 | 158 | return Some(InstKind::ConstInt(norm(av / bv, *w), *w)); |
| 155 | 159 | } |
| 156 | 160 | } |
@@ -159,8 +163,12 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 159 | 163 | InstKind::IMod(a, b) => { |
| 160 | 164 | if let (Some(Const::Int(av, _)), Some(Const::Int(bv, _))) = (get(a), get(b)) { |
| 161 | 165 | if let IrType::Int(w) = ty { |
| 162 | - if bv == 0 { return None; } | |
| 163 | - if av == signed_min(*w) && bv == -1 { return None; } | |
| 166 | + if bv == 0 { | |
| 167 | + return None; | |
| 168 | + } | |
| 169 | + if av == signed_min(*w) && bv == -1 { | |
| 170 | + return None; | |
| 171 | + } | |
| 164 | 172 | return Some(InstKind::ConstInt(norm(av % bv, *w), *w)); |
| 165 | 173 | } |
| 166 | 174 | } |
@@ -255,7 +263,7 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 255 | 263 | |
| 256 | 264 | // Bitwise --------------------------------------------------------- |
| 257 | 265 | InstKind::BitAnd(a, b) => fold_int_bin(get(a), get(b), ty, |x, y| x & y), |
| 258 | - InstKind::BitOr(a, b) => fold_int_bin(get(a), get(b), ty, |x, y| x | y), | |
| 266 | + InstKind::BitOr(a, b) => fold_int_bin(get(a), get(b), ty, |x, y| x | y), | |
| 259 | 267 | InstKind::BitXor(a, b) => fold_int_bin(get(a), get(b), ty, |x, y| x ^ y), |
| 260 | 268 | InstKind::BitNot(a) => { |
| 261 | 269 | if let Some(Const::Int(av, _)) = get(a) { |
@@ -278,10 +286,7 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 278 | 286 | if let IrType::Int(w) = ty { |
| 279 | 287 | let bits = w.bits() as i128; |
| 280 | 288 | if (0..bits).contains(&bv) { |
| 281 | - return Some(InstKind::ConstInt( | |
| 282 | - norm(av.wrapping_shl(bv as u32), *w), | |
| 283 | - *w, | |
| 284 | - )); | |
| 289 | + return Some(InstKind::ConstInt(norm(av.wrapping_shl(bv as u32), *w), *w)); | |
| 285 | 290 | } |
| 286 | 291 | return None; |
| 287 | 292 | } |
@@ -324,7 +329,10 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 324 | 329 | InstKind::PopCount(a) => { |
| 325 | 330 | if let (Some(Const::Int(av, src_w)), IrType::Int(out_w)) = (get(a), ty) { |
| 326 | 331 | let val = mask(av, src_w.bits()) as u128; |
| 327 | - return Some(InstKind::ConstInt(norm(val.count_ones() as i128, *out_w), *out_w)); | |
| 332 | + return Some(InstKind::ConstInt( | |
| 333 | + norm(val.count_ones() as i128, *out_w), | |
| 334 | + *out_w, | |
| 335 | + )); | |
| 328 | 336 | } |
| 329 | 337 | None |
| 330 | 338 | } |
@@ -345,7 +353,11 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 345 | 353 | if let (Some(Const::Int(av, src_w)), IrType::Int(out_w)) = (get(a), ty) { |
| 346 | 354 | let bits = src_w.bits(); |
| 347 | 355 | let val = mask(av, bits) as u128; |
| 348 | - let tz = if val == 0 { bits as i128 } else { val.trailing_zeros() as i128 }; | |
| 356 | + let tz = if val == 0 { | |
| 357 | + bits as i128 | |
| 358 | + } else { | |
| 359 | + val.trailing_zeros() as i128 | |
| 360 | + }; | |
| 349 | 361 | return Some(InstKind::ConstInt(norm(tz, *out_w), *out_w)); |
| 350 | 362 | } |
| 351 | 363 | None |
@@ -377,7 +389,9 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 377 | 389 | } |
| 378 | 390 | InstKind::FloatToInt(a, w) => { |
| 379 | 391 | if let Some(Const::Float(av, src_fw)) = get(a) { |
| 380 | - if av.is_nan() || av.is_infinite() { return None; } | |
| 392 | + if av.is_nan() || av.is_infinite() { | |
| 393 | + return None; | |
| 394 | + } | |
| 381 | 395 | // Audit B-3 (defense in depth): re-round at the |
| 382 | 396 | // source float width before truncating. The M-1 |
| 383 | 397 | // invariant says any f32-tagged Const::Float is |
@@ -388,20 +402,22 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 388 | 402 | // Fortran INT() truncates toward zero. Match. |
| 389 | 403 | let truncd = av.trunc(); |
| 390 | 404 | let lo = match w { |
| 391 | - IntWidth::I8 => i8::MIN as f64, | |
| 405 | + IntWidth::I8 => i8::MIN as f64, | |
| 392 | 406 | IntWidth::I16 => i16::MIN as f64, |
| 393 | 407 | IntWidth::I32 => i32::MIN as f64, |
| 394 | 408 | IntWidth::I64 => i64::MIN as f64, |
| 395 | 409 | IntWidth::I128 => return None, |
| 396 | 410 | }; |
| 397 | 411 | let hi = match w { |
| 398 | - IntWidth::I8 => i8::MAX as f64, | |
| 412 | + IntWidth::I8 => i8::MAX as f64, | |
| 399 | 413 | IntWidth::I16 => i16::MAX as f64, |
| 400 | 414 | IntWidth::I32 => i32::MAX as f64, |
| 401 | 415 | IntWidth::I64 => i64::MAX as f64, |
| 402 | 416 | IntWidth::I128 => return None, |
| 403 | 417 | }; |
| 404 | - if truncd < lo || truncd > hi { return None; } | |
| 418 | + if truncd < lo || truncd > hi { | |
| 419 | + return None; | |
| 420 | + } | |
| 405 | 421 | return Some(InstKind::ConstInt(norm(truncd as i128, *w), *w)); |
| 406 | 422 | } |
| 407 | 423 | None |
@@ -418,7 +434,11 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 418 | 434 | } |
| 419 | 435 | InstKind::IntExtend(a, w, signed) => { |
| 420 | 436 | if let Some(Const::Int(av, src_w)) = get(a) { |
| 421 | - let v = if *signed { sext(av, src_w.bits()) } else { mask(av, src_w.bits()) }; | |
| 437 | + let v = if *signed { | |
| 438 | + sext(av, src_w.bits()) | |
| 439 | + } else { | |
| 440 | + mask(av, src_w.bits()) | |
| 441 | + }; | |
| 422 | 442 | return Some(InstKind::ConstInt(norm(v, *w), *w)); |
| 423 | 443 | } |
| 424 | 444 | None |
@@ -467,9 +487,7 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 467 | 487 | (IrType::Float(FloatWidth::F64), Const::Float(v, _)) => { |
| 468 | 488 | Some(InstKind::ConstFloat(v, FloatWidth::F64)) |
| 469 | 489 | } |
| 470 | - (IrType::Bool, Const::Bool(b)) => { | |
| 471 | - Some(InstKind::ConstBool(b)) | |
| 472 | - } | |
| 490 | + (IrType::Bool, Const::Bool(b)) => Some(InstKind::ConstBool(b)), | |
| 473 | 491 | // Type categories disagree — bail rather than |
| 474 | 492 | // type-pun. The verifier doesn't catch the |
| 475 | 493 | // mismatch (it only checks `is_int`/`is_float` |
@@ -496,7 +514,8 @@ fn try_fold(kind: &InstKind, ty: &IrType, consts: &HashMap<ValueId, Const>) -> O | ||
| 496 | 514 | // f32 destinations because the op itself can produce a value |
| 497 | 515 | // outside f32's representable range. |
| 498 | 516 | fn fold_float_bin<F>(a: Option<Const>, b: Option<Const>, ty: &IrType, op: F) -> Option<InstKind> |
| 499 | -where F: FnOnce(f64, f64) -> f64 | |
| 517 | +where | |
| 518 | + F: FnOnce(f64, f64) -> f64, | |
| 500 | 519 | { |
| 501 | 520 | if let (Some(Const::Float(av, _)), Some(Const::Float(bv, _))) = (a, b) { |
| 502 | 521 | if let IrType::Float(w) = ty { |
@@ -509,7 +528,8 @@ where F: FnOnce(f64, f64) -> f64 | ||
| 509 | 528 | } |
| 510 | 529 | |
| 511 | 530 | fn fold_float_un<F>(a: Option<Const>, ty: &IrType, op: F) -> Option<InstKind> |
| 512 | -where F: FnOnce(f64) -> f64 | |
| 531 | +where | |
| 532 | + F: FnOnce(f64) -> f64, | |
| 513 | 533 | { |
| 514 | 534 | if let Some(Const::Float(av, _)) = a { |
| 515 | 535 | if let IrType::Float(w) = ty { |
@@ -522,7 +542,8 @@ where F: FnOnce(f64) -> f64 | ||
| 522 | 542 | } |
| 523 | 543 | |
| 524 | 544 | fn fold_int_bin<F>(a: Option<Const>, b: Option<Const>, ty: &IrType, op: F) -> Option<InstKind> |
| 525 | -where F: FnOnce(i128, i128) -> i128 | |
| 545 | +where | |
| 546 | + F: FnOnce(i128, i128) -> i128, | |
| 526 | 547 | { |
| 527 | 548 | if let (Some(Const::Int(av, _)), Some(Const::Int(bv, _))) = (a, b) { |
| 528 | 549 | if let IrType::Int(w) = ty { |
@@ -536,7 +557,9 @@ where F: FnOnce(i128, i128) -> i128 | ||
| 536 | 557 | pub struct ConstFold; |
| 537 | 558 | |
| 538 | 559 | impl Pass for ConstFold { |
| 539 | - fn name(&self) -> &'static str { "const-fold" } | |
| 560 | + fn name(&self) -> &'static str { | |
| 561 | + "const-fold" | |
| 562 | + } | |
| 540 | 563 | |
| 541 | 564 | fn run(&self, module: &mut Module) -> bool { |
| 542 | 565 | let mut changed = false; |
@@ -573,7 +596,9 @@ impl Pass for ConstFold { | ||
| 573 | 596 | inner_changed = false; |
| 574 | 597 | for block in &mut func.blocks { |
| 575 | 598 | for inst in &mut block.insts { |
| 576 | - if consts.contains_key(&inst.id) { continue; } | |
| 599 | + if consts.contains_key(&inst.id) { | |
| 600 | + continue; | |
| 601 | + } | |
| 577 | 602 | if let Some(new_kind) = try_fold(&inst.kind, &inst.ty, &consts) { |
| 578 | 603 | if let Some(c) = Const::from_inst(&new_kind) { |
| 579 | 604 | consts.insert(inst.id, c); |
@@ -597,7 +622,11 @@ mod tests { | ||
| 597 | 622 | |
| 598 | 623 | fn dummy_span() -> crate::lexer::Span { |
| 599 | 624 | let p = crate::lexer::Position { line: 1, col: 1 }; |
| 600 | - crate::lexer::Span { start: p, end: p, file_id: 0 } | |
| 625 | + crate::lexer::Span { | |
| 626 | + start: p, | |
| 627 | + end: p, | |
| 628 | + file_id: 0, | |
| 629 | + } | |
| 601 | 630 | } |
| 602 | 631 | |
| 603 | 632 | fn make_module_with(insts: Vec<(InstKind, IrType)>) -> (Module, Vec<ValueId>) { |
@@ -608,7 +637,12 @@ mod tests { | ||
| 608 | 637 | for (kind, ty) in insts { |
| 609 | 638 | let id = f.next_value_id(); |
| 610 | 639 | ids.push(id); |
| 611 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 640 | + f.block_mut(entry).insts.push(Inst { | |
| 641 | + id, | |
| 642 | + kind, | |
| 643 | + ty, | |
| 644 | + span: dummy_span(), | |
| 645 | + }); | |
| 612 | 646 | } |
| 613 | 647 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 614 | 648 | m.add_function(f); |
@@ -616,15 +650,28 @@ mod tests { | ||
| 616 | 650 | } |
| 617 | 651 | |
| 618 | 652 | fn first_block_kinds(m: &Module) -> Vec<InstKind> { |
| 619 | - m.functions[0].blocks[0].insts.iter().map(|i| i.kind.clone()).collect() | |
| 653 | + m.functions[0].blocks[0] | |
| 654 | + .insts | |
| 655 | + .iter() | |
| 656 | + .map(|i| i.kind.clone()) | |
| 657 | + .collect() | |
| 620 | 658 | } |
| 621 | 659 | |
| 622 | 660 | #[test] |
| 623 | 661 | fn folds_iadd_i32() { |
| 624 | 662 | let (mut m, ids) = make_module_with(vec![ |
| 625 | - (InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 626 | - (InstKind::ConstInt(4, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 627 | - (InstKind::IAdd(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I32)), | |
| 663 | + ( | |
| 664 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 665 | + IrType::Int(IntWidth::I32), | |
| 666 | + ), | |
| 667 | + ( | |
| 668 | + InstKind::ConstInt(4, IntWidth::I32), | |
| 669 | + IrType::Int(IntWidth::I32), | |
| 670 | + ), | |
| 671 | + ( | |
| 672 | + InstKind::IAdd(ValueId(0), ValueId(1)), | |
| 673 | + IrType::Int(IntWidth::I32), | |
| 674 | + ), | |
| 628 | 675 | ]); |
| 629 | 676 | let _ = ids; |
| 630 | 677 | assert!(ConstFold.run(&mut m)); |
@@ -636,9 +683,18 @@ mod tests { | ||
| 636 | 683 | fn integer_overflow_wraps_at_width() { |
| 637 | 684 | // i8: 100 + 50 = 150 → wraps to -106 |
| 638 | 685 | let (mut m, _) = make_module_with(vec![ |
| 639 | - (InstKind::ConstInt(100, IntWidth::I8), IrType::Int(IntWidth::I8)), | |
| 640 | - (InstKind::ConstInt(50, IntWidth::I8), IrType::Int(IntWidth::I8)), | |
| 641 | - (InstKind::IAdd(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I8)), | |
| 686 | + ( | |
| 687 | + InstKind::ConstInt(100, IntWidth::I8), | |
| 688 | + IrType::Int(IntWidth::I8), | |
| 689 | + ), | |
| 690 | + ( | |
| 691 | + InstKind::ConstInt(50, IntWidth::I8), | |
| 692 | + IrType::Int(IntWidth::I8), | |
| 693 | + ), | |
| 694 | + ( | |
| 695 | + InstKind::IAdd(ValueId(0), ValueId(1)), | |
| 696 | + IrType::Int(IntWidth::I8), | |
| 697 | + ), | |
| 642 | 698 | ]); |
| 643 | 699 | assert!(ConstFold.run(&mut m)); |
| 644 | 700 | let kinds = first_block_kinds(&m); |
@@ -651,9 +707,18 @@ mod tests { | ||
| 651 | 707 | #[test] |
| 652 | 708 | fn idiv_by_zero_left_alone() { |
| 653 | 709 | let (mut m, _) = make_module_with(vec![ |
| 654 | - (InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 655 | - (InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 656 | - (InstKind::IDiv(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I32)), | |
| 710 | + ( | |
| 711 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 712 | + IrType::Int(IntWidth::I32), | |
| 713 | + ), | |
| 714 | + ( | |
| 715 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 716 | + IrType::Int(IntWidth::I32), | |
| 717 | + ), | |
| 718 | + ( | |
| 719 | + InstKind::IDiv(ValueId(0), ValueId(1)), | |
| 720 | + IrType::Int(IntWidth::I32), | |
| 721 | + ), | |
| 657 | 722 | ]); |
| 658 | 723 | assert!(!ConstFold.run(&mut m)); |
| 659 | 724 | let kinds = first_block_kinds(&m); |
@@ -663,9 +728,18 @@ mod tests { | ||
| 663 | 728 | #[test] |
| 664 | 729 | fn fmul_f64() { |
| 665 | 730 | let (mut m, _) = make_module_with(vec![ |
| 666 | - (InstKind::ConstFloat(2.0, FloatWidth::F64), IrType::Float(FloatWidth::F64)), | |
| 667 | - (InstKind::ConstFloat(3.14, FloatWidth::F64), IrType::Float(FloatWidth::F64)), | |
| 668 | - (InstKind::FMul(ValueId(0), ValueId(1)), IrType::Float(FloatWidth::F64)), | |
| 731 | + ( | |
| 732 | + InstKind::ConstFloat(2.0, FloatWidth::F64), | |
| 733 | + IrType::Float(FloatWidth::F64), | |
| 734 | + ), | |
| 735 | + ( | |
| 736 | + InstKind::ConstFloat(3.14, FloatWidth::F64), | |
| 737 | + IrType::Float(FloatWidth::F64), | |
| 738 | + ), | |
| 739 | + ( | |
| 740 | + InstKind::FMul(ValueId(0), ValueId(1)), | |
| 741 | + IrType::Float(FloatWidth::F64), | |
| 742 | + ), | |
| 669 | 743 | ]); |
| 670 | 744 | assert!(ConstFold.run(&mut m)); |
| 671 | 745 | let kinds = first_block_kinds(&m); |
@@ -678,12 +752,24 @@ mod tests { | ||
| 678 | 752 | #[test] |
| 679 | 753 | fn icmp_eq_true() { |
| 680 | 754 | let (mut m, _) = make_module_with(vec![ |
| 681 | - (InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 682 | - (InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 683 | - (InstKind::ICmp(CmpOp::Eq, ValueId(0), ValueId(1)), IrType::Bool), | |
| 755 | + ( | |
| 756 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 757 | + IrType::Int(IntWidth::I32), | |
| 758 | + ), | |
| 759 | + ( | |
| 760 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 761 | + IrType::Int(IntWidth::I32), | |
| 762 | + ), | |
| 763 | + ( | |
| 764 | + InstKind::ICmp(CmpOp::Eq, ValueId(0), ValueId(1)), | |
| 765 | + IrType::Bool, | |
| 766 | + ), | |
| 684 | 767 | ]); |
| 685 | 768 | assert!(ConstFold.run(&mut m)); |
| 686 | - assert!(matches!(first_block_kinds(&m)[2], InstKind::ConstBool(true))); | |
| 769 | + assert!(matches!( | |
| 770 | + first_block_kinds(&m)[2], | |
| 771 | + InstKind::ConstBool(true) | |
| 772 | + )); | |
| 687 | 773 | } |
| 688 | 774 | |
| 689 | 775 | #[test] |
@@ -691,20 +777,41 @@ mod tests { | ||
| 691 | 777 | // i8 representation: -1 stored as 0xff (255). Without sign-extension, |
| 692 | 778 | // a naive (av < bv) would compare 255 < 1 → false; correct is true. |
| 693 | 779 | let (mut m, _) = make_module_with(vec![ |
| 694 | - (InstKind::ConstInt(-1, IntWidth::I8), IrType::Int(IntWidth::I8)), | |
| 695 | - (InstKind::ConstInt(1, IntWidth::I8), IrType::Int(IntWidth::I8)), | |
| 696 | - (InstKind::ICmp(CmpOp::Lt, ValueId(0), ValueId(1)), IrType::Bool), | |
| 780 | + ( | |
| 781 | + InstKind::ConstInt(-1, IntWidth::I8), | |
| 782 | + IrType::Int(IntWidth::I8), | |
| 783 | + ), | |
| 784 | + ( | |
| 785 | + InstKind::ConstInt(1, IntWidth::I8), | |
| 786 | + IrType::Int(IntWidth::I8), | |
| 787 | + ), | |
| 788 | + ( | |
| 789 | + InstKind::ICmp(CmpOp::Lt, ValueId(0), ValueId(1)), | |
| 790 | + IrType::Bool, | |
| 791 | + ), | |
| 697 | 792 | ]); |
| 698 | 793 | assert!(ConstFold.run(&mut m)); |
| 699 | - assert!(matches!(first_block_kinds(&m)[2], InstKind::ConstBool(true))); | |
| 794 | + assert!(matches!( | |
| 795 | + first_block_kinds(&m)[2], | |
| 796 | + InstKind::ConstBool(true) | |
| 797 | + )); | |
| 700 | 798 | } |
| 701 | 799 | |
| 702 | 800 | #[test] |
| 703 | 801 | fn shl_power_of_two() { |
| 704 | 802 | let (mut m, _) = make_module_with(vec![ |
| 705 | - (InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 706 | - (InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 707 | - (InstKind::Shl(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I32)), | |
| 803 | + ( | |
| 804 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 805 | + IrType::Int(IntWidth::I32), | |
| 806 | + ), | |
| 807 | + ( | |
| 808 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 809 | + IrType::Int(IntWidth::I32), | |
| 810 | + ), | |
| 811 | + ( | |
| 812 | + InstKind::Shl(ValueId(0), ValueId(1)), | |
| 813 | + IrType::Int(IntWidth::I32), | |
| 814 | + ), | |
| 708 | 815 | ]); |
| 709 | 816 | assert!(ConstFold.run(&mut m)); |
| 710 | 817 | match first_block_kinds(&m)[2] { |
@@ -717,8 +824,14 @@ mod tests { | ||
| 717 | 824 | fn int_to_float_chain() { |
| 718 | 825 | // const(42:i32) → int_to_float → const(42.0:f64) |
| 719 | 826 | let (mut m, _) = make_module_with(vec![ |
| 720 | - (InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 721 | - (InstKind::IntToFloat(ValueId(0), FloatWidth::F64), IrType::Float(FloatWidth::F64)), | |
| 827 | + ( | |
| 828 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 829 | + IrType::Int(IntWidth::I32), | |
| 830 | + ), | |
| 831 | + ( | |
| 832 | + InstKind::IntToFloat(ValueId(0), FloatWidth::F64), | |
| 833 | + IrType::Float(FloatWidth::F64), | |
| 834 | + ), | |
| 722 | 835 | ]); |
| 723 | 836 | assert!(ConstFold.run(&mut m)); |
| 724 | 837 | match first_block_kinds(&m)[1] { |
@@ -731,9 +844,18 @@ mod tests { | ||
| 731 | 844 | fn select_on_known_cond_picks_branch() { |
| 732 | 845 | let (mut m, _) = make_module_with(vec![ |
| 733 | 846 | (InstKind::ConstBool(true), IrType::Bool), |
| 734 | - (InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 735 | - (InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 736 | - (InstKind::Select(ValueId(0), ValueId(1), ValueId(2)), IrType::Int(IntWidth::I32)), | |
| 847 | + ( | |
| 848 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 849 | + IrType::Int(IntWidth::I32), | |
| 850 | + ), | |
| 851 | + ( | |
| 852 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 853 | + IrType::Int(IntWidth::I32), | |
| 854 | + ), | |
| 855 | + ( | |
| 856 | + InstKind::Select(ValueId(0), ValueId(1), ValueId(2)), | |
| 857 | + IrType::Int(IntWidth::I32), | |
| 858 | + ), | |
| 737 | 859 | ]); |
| 738 | 860 | assert!(ConstFold.run(&mut m)); |
| 739 | 861 | match first_block_kinds(&m)[3] { |
@@ -746,24 +868,45 @@ mod tests { | ||
| 746 | 868 | fn fixpoint_via_chained_constants() { |
| 747 | 869 | // (3 + 4) * 2 = 14, where the inner add must fold first. |
| 748 | 870 | let (mut m, _) = make_module_with(vec![ |
| 749 | - (InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 750 | - (InstKind::ConstInt(4, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 751 | - (InstKind::IAdd(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I32)), | |
| 752 | - (InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 753 | - (InstKind::IMul(ValueId(2), ValueId(3)), IrType::Int(IntWidth::I32)), | |
| 871 | + ( | |
| 872 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 873 | + IrType::Int(IntWidth::I32), | |
| 874 | + ), | |
| 875 | + ( | |
| 876 | + InstKind::ConstInt(4, IntWidth::I32), | |
| 877 | + IrType::Int(IntWidth::I32), | |
| 878 | + ), | |
| 879 | + ( | |
| 880 | + InstKind::IAdd(ValueId(0), ValueId(1)), | |
| 881 | + IrType::Int(IntWidth::I32), | |
| 882 | + ), | |
| 883 | + ( | |
| 884 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 885 | + IrType::Int(IntWidth::I32), | |
| 886 | + ), | |
| 887 | + ( | |
| 888 | + InstKind::IMul(ValueId(2), ValueId(3)), | |
| 889 | + IrType::Int(IntWidth::I32), | |
| 890 | + ), | |
| 754 | 891 | ]); |
| 755 | 892 | // A single pass already handles this because we walk in order. |
| 756 | 893 | assert!(ConstFold.run(&mut m)); |
| 757 | 894 | let kinds = first_block_kinds(&m); |
| 758 | - assert!(matches!(kinds[2], InstKind::ConstInt(7, IntWidth::I32))); | |
| 895 | + assert!(matches!(kinds[2], InstKind::ConstInt(7, IntWidth::I32))); | |
| 759 | 896 | assert!(matches!(kinds[4], InstKind::ConstInt(14, IntWidth::I32))); |
| 760 | 897 | } |
| 761 | 898 | |
| 762 | 899 | #[test] |
| 763 | 900 | fn nan_float_to_int_left_alone() { |
| 764 | 901 | let (mut m, _) = make_module_with(vec![ |
| 765 | - (InstKind::ConstFloat(f64::NAN, FloatWidth::F64), IrType::Float(FloatWidth::F64)), | |
| 766 | - (InstKind::FloatToInt(ValueId(0), IntWidth::I32), IrType::Int(IntWidth::I32)), | |
| 902 | + ( | |
| 903 | + InstKind::ConstFloat(f64::NAN, FloatWidth::F64), | |
| 904 | + IrType::Float(FloatWidth::F64), | |
| 905 | + ), | |
| 906 | + ( | |
| 907 | + InstKind::FloatToInt(ValueId(0), IntWidth::I32), | |
| 908 | + IrType::Int(IntWidth::I32), | |
| 909 | + ), | |
| 767 | 910 | ]); |
| 768 | 911 | assert!(!ConstFold.run(&mut m)); |
| 769 | 912 | assert!(matches!(first_block_kinds(&m)[1], InstKind::FloatToInt(..))); |
src/opt/const_prop.rsmodified@@ -88,7 +88,9 @@ fn collect_consts(func: &Function) -> HashMap<ValueId, Const> { | ||
| 88 | 88 | pub struct ConstProp; |
| 89 | 89 | |
| 90 | 90 | impl Pass for ConstProp { |
| 91 | - fn name(&self) -> &'static str { "const-prop" } | |
| 91 | + fn name(&self) -> &'static str { | |
| 92 | + "const-prop" | |
| 93 | + } | |
| 92 | 94 | |
| 93 | 95 | fn run(&self, module: &mut Module) -> bool { |
| 94 | 96 | let mut changed = false; |
@@ -96,7 +98,9 @@ impl Pass for ConstProp { | ||
| 96 | 98 | let consts = collect_consts(func); |
| 97 | 99 | let mut local_changed = false; |
| 98 | 100 | for block in &mut func.blocks { |
| 99 | - let Some(term) = block.terminator.take() else { continue }; | |
| 101 | + let Some(term) = block.terminator.take() else { | |
| 102 | + continue; | |
| 103 | + }; | |
| 100 | 104 | let new_term = simplify_terminator(term, &consts, &mut local_changed); |
| 101 | 105 | block.terminator = Some(new_term); |
| 102 | 106 | } |
@@ -118,7 +122,13 @@ fn simplify_terminator( | ||
| 118 | 122 | changed: &mut bool, |
| 119 | 123 | ) -> Terminator { |
| 120 | 124 | match term { |
| 121 | - Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args } => { | |
| 125 | + Terminator::CondBranch { | |
| 126 | + cond, | |
| 127 | + true_dest, | |
| 128 | + true_args, | |
| 129 | + false_dest, | |
| 130 | + false_args, | |
| 131 | + } => { | |
| 122 | 132 | if let Some(Const::Bool(c)) = consts.get(&cond).copied() { |
| 123 | 133 | *changed = true; |
| 124 | 134 | if c { |
@@ -127,10 +137,20 @@ fn simplify_terminator( | ||
| 127 | 137 | Terminator::Branch(false_dest, false_args) |
| 128 | 138 | } |
| 129 | 139 | } else { |
| 130 | - Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args } | |
| 140 | + Terminator::CondBranch { | |
| 141 | + cond, | |
| 142 | + true_dest, | |
| 143 | + true_args, | |
| 144 | + false_dest, | |
| 145 | + false_args, | |
| 146 | + } | |
| 131 | 147 | } |
| 132 | 148 | } |
| 133 | - Terminator::Switch { selector, cases, default } => { | |
| 149 | + Terminator::Switch { | |
| 150 | + selector, | |
| 151 | + cases, | |
| 152 | + default, | |
| 153 | + } => { | |
| 134 | 154 | if let Some(Const::Int(sv, w)) = consts.get(&selector).copied() { |
| 135 | 155 | // `sv` is already canonical (sign-extended at width) |
| 136 | 156 | // thanks to the M4-4 normalization in `Const::from_inst`. |
@@ -138,14 +158,20 @@ fn simplify_terminator( | ||
| 138 | 158 | // canonical, so we still normalize them before |
| 139 | 159 | // comparing. |
| 140 | 160 | *changed = true; |
| 141 | - let target = cases.iter().find(|(k, _)| sext(*k, w.bits()) == sv) | |
| 161 | + let target = cases | |
| 162 | + .iter() | |
| 163 | + .find(|(k, _)| sext(*k, w.bits()) == sv) | |
| 142 | 164 | .map(|(_, b)| *b) |
| 143 | 165 | .unwrap_or(default); |
| 144 | 166 | // Switch targets in our IR cannot have block params, so |
| 145 | 167 | // an empty arg vec is correct. |
| 146 | 168 | Terminator::Branch(target, vec![]) |
| 147 | 169 | } else { |
| 148 | - Terminator::Switch { selector, cases, default } | |
| 170 | + Terminator::Switch { | |
| 171 | + selector, | |
| 172 | + cases, | |
| 173 | + default, | |
| 174 | + } | |
| 149 | 175 | } |
| 150 | 176 | } |
| 151 | 177 | other => other, |
@@ -153,8 +179,9 @@ fn simplify_terminator( | ||
| 153 | 179 | } |
| 154 | 180 | |
| 155 | 181 | fn sext(v: i64, bits: u32) -> i64 { |
| 156 | - if bits >= 64 { v } | |
| 157 | - else { | |
| 182 | + if bits >= 64 { | |
| 183 | + v | |
| 184 | + } else { | |
| 158 | 185 | let shift = 64 - bits; |
| 159 | 186 | (v << shift) >> shift |
| 160 | 187 | } |
@@ -164,11 +191,15 @@ fn sext(v: i64, bits: u32) -> i64 { | ||
| 164 | 191 | mod tests { |
| 165 | 192 | use super::*; |
| 166 | 193 | use crate::ir::types::IrType; |
| 167 | - use crate::lexer::{Span, Position}; | |
| 194 | + use crate::lexer::{Position, Span}; | |
| 168 | 195 | |
| 169 | 196 | fn dummy_span() -> Span { |
| 170 | 197 | let p = Position { line: 1, col: 1 }; |
| 171 | - Span { start: p, end: p, file_id: 0 } | |
| 198 | + Span { | |
| 199 | + start: p, | |
| 200 | + end: p, | |
| 201 | + file_id: 0, | |
| 202 | + } | |
| 172 | 203 | } |
| 173 | 204 | |
| 174 | 205 | #[test] |
src/opt/cse.rsmodified@@ -63,13 +63,29 @@ struct Key { | ||
| 63 | 63 | /// instruction is impure or otherwise not a CSE candidate. |
| 64 | 64 | fn key_of(inst: &Inst) -> Option<Key> { |
| 65 | 65 | let mk = |tag: u32, ops: Vec<ValueId>, aux: i128| -> Option<Key> { |
| 66 | - Some(Key { tag, operands: ops, aux, name: None, ty: inst.ty.clone() }) | |
| 66 | + Some(Key { | |
| 67 | + tag, | |
| 68 | + operands: ops, | |
| 69 | + aux, | |
| 70 | + name: None, | |
| 71 | + ty: inst.ty.clone(), | |
| 72 | + }) | |
| 67 | 73 | }; |
| 68 | 74 | let mk_named = |tag: u32, name: String| -> Option<Key> { |
| 69 | - Some(Key { tag, operands: vec![], aux: 0, name: Some(name), ty: inst.ty.clone() }) | |
| 75 | + Some(Key { | |
| 76 | + tag, | |
| 77 | + operands: vec![], | |
| 78 | + aux: 0, | |
| 79 | + name: Some(name), | |
| 80 | + ty: inst.ty.clone(), | |
| 81 | + }) | |
| 70 | 82 | }; |
| 71 | 83 | let canon = |a: ValueId, b: ValueId| -> Vec<ValueId> { |
| 72 | - if a.0 <= b.0 { vec![a, b] } else { vec![b, a] } | |
| 84 | + if a.0 <= b.0 { | |
| 85 | + vec![a, b] | |
| 86 | + } else { | |
| 87 | + vec![b, a] | |
| 88 | + } | |
| 73 | 89 | }; |
| 74 | 90 | |
| 75 | 91 | match &inst.kind { |
@@ -87,7 +103,7 @@ fn key_of(inst: &Inst) -> Option<Key> { | ||
| 87 | 103 | // at different bit patterns dedupe. Example: `ConstInt(255, I8)` |
| 88 | 104 | // and `ConstInt(-1, I8)` both represent -1 in i8 — keying on |
| 89 | 105 | // the raw `*v` fails to dedupe them. |
| 90 | - InstKind::ConstInt(v, w) => { | |
| 106 | + InstKind::ConstInt(v, w) => { | |
| 91 | 107 | let bits = w.bits(); |
| 92 | 108 | // Sign-extend at width: low `bits` bits → i64 sign-extended. |
| 93 | 109 | let signed = if bits >= 128 { |
@@ -99,7 +115,7 @@ fn key_of(inst: &Inst) -> Option<Key> { | ||
| 99 | 115 | mk(1, vec![], signed) |
| 100 | 116 | } |
| 101 | 117 | InstKind::ConstFloat(v, _) => mk(2, vec![], v.to_bits() as i128), |
| 102 | - InstKind::ConstBool(b) => mk(3, vec![], if *b { 1 } else { 0 }), | |
| 118 | + InstKind::ConstBool(b) => mk(3, vec![], if *b { 1 } else { 0 }), | |
| 103 | 119 | |
| 104 | 120 | // Integer arithmetic -------------------------------------------- |
| 105 | 121 | InstKind::IAdd(a, b) => mk(10, canon(*a, *b), 0), |
@@ -107,58 +123,66 @@ fn key_of(inst: &Inst) -> Option<Key> { | ||
| 107 | 123 | InstKind::IMul(a, b) => mk(12, canon(*a, *b), 0), |
| 108 | 124 | InstKind::IDiv(a, b) => mk(13, vec![*a, *b], 0), |
| 109 | 125 | InstKind::IMod(a, b) => mk(14, vec![*a, *b], 0), |
| 110 | - InstKind::INeg(a) => mk(15, vec![*a], 0), | |
| 126 | + InstKind::INeg(a) => mk(15, vec![*a], 0), | |
| 111 | 127 | |
| 112 | 128 | // Float arithmetic ---------------------------------------------- |
| 113 | 129 | InstKind::FAdd(a, b) => mk(20, canon(*a, *b), 0), |
| 114 | 130 | InstKind::FSub(a, b) => mk(21, vec![*a, *b], 0), |
| 115 | 131 | InstKind::FMul(a, b) => mk(22, canon(*a, *b), 0), |
| 116 | 132 | InstKind::FDiv(a, b) => mk(23, vec![*a, *b], 0), |
| 117 | - InstKind::FNeg(a) => mk(24, vec![*a], 0), | |
| 118 | - InstKind::FAbs(a) => mk(25, vec![*a], 0), | |
| 119 | - InstKind::FSqrt(a) => mk(26, vec![*a], 0), | |
| 133 | + InstKind::FNeg(a) => mk(24, vec![*a], 0), | |
| 134 | + InstKind::FAbs(a) => mk(25, vec![*a], 0), | |
| 135 | + InstKind::FSqrt(a) => mk(26, vec![*a], 0), | |
| 120 | 136 | InstKind::FPow(a, b) => mk(27, vec![*a, *b], 0), |
| 121 | 137 | |
| 122 | 138 | // Comparisons --------------------------------------------------- |
| 123 | 139 | InstKind::ICmp(op, a, b) => { |
| 124 | 140 | let aux = *op as i128; |
| 125 | - let ops = match op { CmpOp::Eq | CmpOp::Ne => canon(*a, *b), _ => vec![*a, *b] }; | |
| 141 | + let ops = match op { | |
| 142 | + CmpOp::Eq | CmpOp::Ne => canon(*a, *b), | |
| 143 | + _ => vec![*a, *b], | |
| 144 | + }; | |
| 126 | 145 | mk(30, ops, aux) |
| 127 | 146 | } |
| 128 | 147 | InstKind::FCmp(op, a, b) => { |
| 129 | 148 | let aux = *op as i128; |
| 130 | - let ops = match op { CmpOp::Eq | CmpOp::Ne => canon(*a, *b), _ => vec![*a, *b] }; | |
| 149 | + let ops = match op { | |
| 150 | + CmpOp::Eq | CmpOp::Ne => canon(*a, *b), | |
| 151 | + _ => vec![*a, *b], | |
| 152 | + }; | |
| 131 | 153 | mk(31, ops, aux) |
| 132 | 154 | } |
| 133 | 155 | |
| 134 | 156 | // Logic --------------------------------------------------------- |
| 135 | 157 | InstKind::And(a, b) => mk(40, canon(*a, *b), 0), |
| 136 | - InstKind::Or(a, b) => mk(41, canon(*a, *b), 0), | |
| 137 | - InstKind::Not(a) => mk(42, vec![*a], 0), | |
| 158 | + InstKind::Or(a, b) => mk(41, canon(*a, *b), 0), | |
| 159 | + InstKind::Not(a) => mk(42, vec![*a], 0), | |
| 138 | 160 | |
| 139 | 161 | InstKind::Select(c, t, f) => mk(43, vec![*c, *t, *f], 0), |
| 140 | 162 | |
| 141 | 163 | // Bitwise ------------------------------------------------------- |
| 142 | 164 | InstKind::BitAnd(a, b) => mk(50, canon(*a, *b), 0), |
| 143 | - InstKind::BitOr(a, b) => mk(51, canon(*a, *b), 0), | |
| 165 | + InstKind::BitOr(a, b) => mk(51, canon(*a, *b), 0), | |
| 144 | 166 | InstKind::BitXor(a, b) => mk(52, canon(*a, *b), 0), |
| 145 | - InstKind::BitNot(a) => mk(53, vec![*a], 0), | |
| 146 | - InstKind::Shl(a, b) => mk(54, vec![*a, *b], 0), | |
| 147 | - InstKind::LShr(a, b) => mk(55, vec![*a, *b], 0), | |
| 148 | - InstKind::AShr(a, b) => mk(56, vec![*a, *b], 0), | |
| 149 | - InstKind::CountLeadingZeros(a) => mk(57, vec![*a], 0), | |
| 167 | + InstKind::BitNot(a) => mk(53, vec![*a], 0), | |
| 168 | + InstKind::Shl(a, b) => mk(54, vec![*a, *b], 0), | |
| 169 | + InstKind::LShr(a, b) => mk(55, vec![*a, *b], 0), | |
| 170 | + InstKind::AShr(a, b) => mk(56, vec![*a, *b], 0), | |
| 171 | + InstKind::CountLeadingZeros(a) => mk(57, vec![*a], 0), | |
| 150 | 172 | InstKind::CountTrailingZeros(a) => mk(58, vec![*a], 0), |
| 151 | - InstKind::PopCount(a) => mk(59, vec![*a], 0), | |
| 173 | + InstKind::PopCount(a) => mk(59, vec![*a], 0), | |
| 152 | 174 | |
| 153 | 175 | // Conversions --------------------------------------------------- |
| 154 | - InstKind::IntToFloat(v, fw) => mk(60, vec![*v], fw.bits() as i128), | |
| 155 | - InstKind::FloatToInt(v, w) => mk(61, vec![*v], w.bits() as i128), | |
| 156 | - InstKind::FloatExtend(v, fw) => mk(62, vec![*v], fw.bits() as i128), | |
| 157 | - InstKind::FloatTrunc(v, fw) => mk(63, vec![*v], fw.bits() as i128), | |
| 158 | - InstKind::IntExtend(v, w, sgn) => mk(64, vec![*v], (w.bits() as i128) | ((*sgn as i128) << 32)), | |
| 159 | - InstKind::IntTrunc(v, w) => mk(65, vec![*v], w.bits() as i128), | |
| 160 | - InstKind::PtrToInt(v) => mk(66, vec![*v], 0), | |
| 161 | - InstKind::IntToPtr(v, _) => mk(67, vec![*v], 0), | |
| 176 | + InstKind::IntToFloat(v, fw) => mk(60, vec![*v], fw.bits() as i128), | |
| 177 | + InstKind::FloatToInt(v, w) => mk(61, vec![*v], w.bits() as i128), | |
| 178 | + InstKind::FloatExtend(v, fw) => mk(62, vec![*v], fw.bits() as i128), | |
| 179 | + InstKind::FloatTrunc(v, fw) => mk(63, vec![*v], fw.bits() as i128), | |
| 180 | + InstKind::IntExtend(v, w, sgn) => { | |
| 181 | + mk(64, vec![*v], (w.bits() as i128) | ((*sgn as i128) << 32)) | |
| 182 | + } | |
| 183 | + InstKind::IntTrunc(v, w) => mk(65, vec![*v], w.bits() as i128), | |
| 184 | + InstKind::PtrToInt(v) => mk(66, vec![*v], 0), | |
| 185 | + InstKind::IntToPtr(v, _) => mk(67, vec![*v], 0), | |
| 162 | 186 | |
| 163 | 187 | // Address arithmetic -------------------------------------------- |
| 164 | 188 | InstKind::GetElementPtr(base, idxs) => { |
@@ -188,7 +212,9 @@ fn key_of(inst: &Inst) -> Option<Key> { | ||
| 188 | 212 | pub struct LocalCse; |
| 189 | 213 | |
| 190 | 214 | impl Pass for LocalCse { |
| 191 | - fn name(&self) -> &'static str { "local-cse" } | |
| 215 | + fn name(&self) -> &'static str { | |
| 216 | + "local-cse" | |
| 217 | + } | |
| 192 | 218 | |
| 193 | 219 | fn run(&self, module: &mut Module) -> bool { |
| 194 | 220 | let mut changed = false; |
@@ -211,7 +237,9 @@ impl Pass for LocalCse { | ||
| 211 | 237 | } |
| 212 | 238 | } |
| 213 | 239 | } |
| 214 | - if rewrite_map.is_empty() { continue; } | |
| 240 | + if rewrite_map.is_empty() { | |
| 241 | + continue; | |
| 242 | + } | |
| 215 | 243 | |
| 216 | 244 | // Audit B-7: in **local** CSE, every entry maps a later |
| 217 | 245 | // duplicate to its block's *first* occurrence — and that |
@@ -261,18 +289,27 @@ fn substitute_uses_batch(func: &mut Function, rewrites: &HashMap<ValueId, ValueI | ||
| 261 | 289 | #[cfg(test)] |
| 262 | 290 | mod tests { |
| 263 | 291 | use super::*; |
| 264 | - use crate::ir::types::{IrType, IntWidth, FloatWidth}; | |
| 265 | - use crate::lexer::{Span, Position}; | |
| 292 | + use crate::ir::types::{FloatWidth, IntWidth, IrType}; | |
| 293 | + use crate::lexer::{Position, Span}; | |
| 266 | 294 | |
| 267 | 295 | fn dummy_span() -> Span { |
| 268 | 296 | let p = Position { line: 1, col: 1 }; |
| 269 | - Span { start: p, end: p, file_id: 0 } | |
| 297 | + Span { | |
| 298 | + start: p, | |
| 299 | + end: p, | |
| 300 | + file_id: 0, | |
| 301 | + } | |
| 270 | 302 | } |
| 271 | 303 | |
| 272 | 304 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 273 | 305 | let id = f.next_value_id(); |
| 274 | 306 | let entry = f.entry; |
| 275 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 307 | + f.block_mut(entry).insts.push(Inst { | |
| 308 | + id, | |
| 309 | + kind, | |
| 310 | + ty, | |
| 311 | + span: dummy_span(), | |
| 312 | + }); | |
| 276 | 313 | id |
| 277 | 314 | } |
| 278 | 315 | |
@@ -285,8 +322,16 @@ mod tests { | ||
| 285 | 322 | // ret %3 → after CSE → ret %2 (and %3 is dead) |
| 286 | 323 | let mut m = Module::new("t".into()); |
| 287 | 324 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 288 | - let a = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 289 | - let b = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 325 | + let a = push( | |
| 326 | + &mut f, | |
| 327 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 328 | + IrType::Int(IntWidth::I32), | |
| 329 | + ); | |
| 330 | + let b = push( | |
| 331 | + &mut f, | |
| 332 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 333 | + IrType::Int(IntWidth::I32), | |
| 334 | + ); | |
| 290 | 335 | let c1 = push(&mut f, InstKind::IAdd(a, b), IrType::Int(IntWidth::I32)); |
| 291 | 336 | let c2 = push(&mut f, InstKind::IAdd(a, b), IrType::Int(IntWidth::I32)); |
| 292 | 337 | let entry = f.entry; |
@@ -308,8 +353,16 @@ mod tests { | ||
| 308 | 353 | // Should canonicalize to the same key. |
| 309 | 354 | let mut m = Module::new("t".into()); |
| 310 | 355 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 311 | - let a = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 312 | - let b = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 356 | + let a = push( | |
| 357 | + &mut f, | |
| 358 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 359 | + IrType::Int(IntWidth::I32), | |
| 360 | + ); | |
| 361 | + let b = push( | |
| 362 | + &mut f, | |
| 363 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 364 | + IrType::Int(IntWidth::I32), | |
| 365 | + ); | |
| 313 | 366 | let c1 = push(&mut f, InstKind::IAdd(a, b), IrType::Int(IntWidth::I32)); |
| 314 | 367 | let c2 = push(&mut f, InstKind::IAdd(b, a), IrType::Int(IntWidth::I32)); |
| 315 | 368 | let _ = c2; |
@@ -328,8 +381,16 @@ mod tests { | ||
| 328 | 381 | fn non_commutative_isub_does_not_dedupe_swapped() { |
| 329 | 382 | let mut m = Module::new("t".into()); |
| 330 | 383 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 331 | - let a = push(&mut f, InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 332 | - let b = push(&mut f, InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 384 | + let a = push( | |
| 385 | + &mut f, | |
| 386 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 387 | + IrType::Int(IntWidth::I32), | |
| 388 | + ); | |
| 389 | + let b = push( | |
| 390 | + &mut f, | |
| 391 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 392 | + IrType::Int(IntWidth::I32), | |
| 393 | + ); | |
| 333 | 394 | let _c1 = push(&mut f, InstKind::ISub(a, b), IrType::Int(IntWidth::I32)); |
| 334 | 395 | let c2 = push(&mut f, InstKind::ISub(b, a), IrType::Int(IntWidth::I32)); |
| 335 | 396 | let entry = f.entry; |
@@ -345,7 +406,8 @@ mod tests { | ||
| 345 | 406 | // Loads must NOT be deduplicated by local CSE. |
| 346 | 407 | let mut m = Module::new("t".into()); |
| 347 | 408 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 348 | - let addr = push(&mut f, | |
| 409 | + let addr = push( | |
| 410 | + &mut f, | |
| 349 | 411 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 350 | 412 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 351 | 413 | ); |
@@ -362,8 +424,16 @@ mod tests { | ||
| 362 | 424 | fn fmul_dedupes() { |
| 363 | 425 | let mut m = Module::new("t".into()); |
| 364 | 426 | let mut f = Function::new("f".into(), vec![], IrType::Float(FloatWidth::F64)); |
| 365 | - let a = push(&mut f, InstKind::ConstFloat(1.5, FloatWidth::F64), IrType::Float(FloatWidth::F64)); | |
| 366 | - let b = push(&mut f, InstKind::ConstFloat(2.5, FloatWidth::F64), IrType::Float(FloatWidth::F64)); | |
| 427 | + let a = push( | |
| 428 | + &mut f, | |
| 429 | + InstKind::ConstFloat(1.5, FloatWidth::F64), | |
| 430 | + IrType::Float(FloatWidth::F64), | |
| 431 | + ); | |
| 432 | + let b = push( | |
| 433 | + &mut f, | |
| 434 | + InstKind::ConstFloat(2.5, FloatWidth::F64), | |
| 435 | + IrType::Float(FloatWidth::F64), | |
| 436 | + ); | |
| 367 | 437 | let m1 = push(&mut f, InstKind::FMul(a, b), IrType::Float(FloatWidth::F64)); |
| 368 | 438 | let m2 = push(&mut f, InstKind::FMul(b, a), IrType::Float(FloatWidth::F64)); |
| 369 | 439 | let entry = f.entry; |
@@ -382,8 +452,16 @@ mod tests { | ||
| 382 | 452 | // Lt is not commutative — must not collapse. |
| 383 | 453 | let mut m = Module::new("t".into()); |
| 384 | 454 | let mut f = Function::new("f".into(), vec![], IrType::Bool); |
| 385 | - let a = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 386 | - let b = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 455 | + let a = push( | |
| 456 | + &mut f, | |
| 457 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 458 | + IrType::Int(IntWidth::I32), | |
| 459 | + ); | |
| 460 | + let b = push( | |
| 461 | + &mut f, | |
| 462 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 463 | + IrType::Int(IntWidth::I32), | |
| 464 | + ); | |
| 387 | 465 | let _c1 = push(&mut f, InstKind::ICmp(CmpOp::Lt, a, b), IrType::Bool); |
| 388 | 466 | let c2 = push(&mut f, InstKind::ICmp(CmpOp::Lt, b, a), IrType::Bool); |
| 389 | 467 | let entry = f.entry; |
src/opt/dce.rsmodified@@ -36,7 +36,7 @@ | ||
| 36 | 36 | //! refine this. |
| 37 | 37 | |
| 38 | 38 | use super::pass::Pass; |
| 39 | -use super::util::{inst_uses, terminator_uses, prune_unreachable}; | |
| 39 | +use super::util::{inst_uses, prune_unreachable, terminator_uses}; | |
| 40 | 40 | use crate::ir::inst::*; |
| 41 | 41 | use std::collections::{HashMap, HashSet}; |
| 42 | 42 | |
@@ -52,9 +52,10 @@ fn has_side_effect(kind: &InstKind, pure_internal_calls: &[bool]) -> bool { | ||
| 52 | 52 | // future store/call). Treat as side-effecting for safety. |
| 53 | 53 | | InstKind::Alloca(..) |
| 54 | 54 | ) || match kind { |
| 55 | - InstKind::Call(FuncRef::Internal(idx), _) => { | |
| 56 | - !pure_internal_calls.get(*idx as usize).copied().unwrap_or(false) | |
| 57 | - } | |
| 55 | + InstKind::Call(FuncRef::Internal(idx), _) => !pure_internal_calls | |
| 56 | + .get(*idx as usize) | |
| 57 | + .copied() | |
| 58 | + .unwrap_or(false), | |
| 58 | 59 | InstKind::Call(..) => true, |
| 59 | 60 | _ => false, |
| 60 | 61 | } |
@@ -109,8 +110,12 @@ fn dce_function(func: &mut Function, pure_internal_calls: &[bool]) -> bool { | ||
| 109 | 110 | for block in &mut func.blocks { |
| 110 | 111 | let before = block.insts.len(); |
| 111 | 112 | block.insts.retain(|inst| { |
| 112 | - if has_side_effect(&inst.kind, pure_internal_calls) { return true; } | |
| 113 | - if live.contains(&inst.id) { return true; } | |
| 113 | + if has_side_effect(&inst.kind, pure_internal_calls) { | |
| 114 | + return true; | |
| 115 | + } | |
| 116 | + if live.contains(&inst.id) { | |
| 117 | + return true; | |
| 118 | + } | |
| 114 | 119 | false |
| 115 | 120 | }); |
| 116 | 121 | if block.insts.len() != before { |
@@ -126,7 +131,9 @@ fn dce_function(func: &mut Function, pure_internal_calls: &[bool]) -> bool { | ||
| 126 | 131 | outer_changed = true; |
| 127 | 132 | } |
| 128 | 133 | } |
| 129 | - if prune_unreachable(func) { any_change = true; } | |
| 134 | + if prune_unreachable(func) { | |
| 135 | + any_change = true; | |
| 136 | + } | |
| 130 | 137 | any_change |
| 131 | 138 | } |
| 132 | 139 | |
@@ -146,19 +153,25 @@ fn remove_dead_block_params(func: &mut Function) -> bool { | ||
| 146 | 153 | |
| 147 | 154 | let mut by_block: HashMap<BlockId, Vec<usize>> = HashMap::new(); |
| 148 | 155 | for block in func.blocks.iter() { |
| 149 | - if block.id == func.entry { continue; } | |
| 156 | + if block.id == func.entry { | |
| 157 | + continue; | |
| 158 | + } | |
| 150 | 159 | for (idx, p) in block.params.iter().enumerate() { |
| 151 | 160 | if !live.contains(&p.id) { |
| 152 | 161 | by_block.entry(block.id).or_default().push(idx); |
| 153 | 162 | } |
| 154 | 163 | } |
| 155 | 164 | } |
| 156 | - if by_block.is_empty() { return false; } | |
| 165 | + if by_block.is_empty() { | |
| 166 | + return false; | |
| 167 | + } | |
| 157 | 168 | |
| 158 | 169 | // Map BlockId → vec index. LICM/DCE never structurally reorder |
| 159 | 170 | // `func.blocks`; only the inst vectors change. So this stays |
| 160 | 171 | // valid for the lifetime of this call. |
| 161 | - let block_index: HashMap<BlockId, usize> = func.blocks.iter() | |
| 172 | + let block_index: HashMap<BlockId, usize> = func | |
| 173 | + .blocks | |
| 174 | + .iter() | |
| 162 | 175 | .enumerate() |
| 163 | 176 | .map(|(i, b)| (b.id, i)) |
| 164 | 177 | .collect(); |
@@ -199,20 +212,38 @@ fn remove_dead_block_params(func: &mut Function) -> bool { | ||
| 199 | 212 | fn drop_branch_arg(term: &mut Terminator, target: BlockId, idx: usize) { |
| 200 | 213 | match term { |
| 201 | 214 | Terminator::Branch(d, args) if *d == target => { |
| 202 | - debug_assert!(idx < args.len(), | |
| 203 | - "drop_branch_arg: branch arg list out of sync with target params"); | |
| 204 | - if idx < args.len() { args.remove(idx); } | |
| 215 | + debug_assert!( | |
| 216 | + idx < args.len(), | |
| 217 | + "drop_branch_arg: branch arg list out of sync with target params" | |
| 218 | + ); | |
| 219 | + if idx < args.len() { | |
| 220 | + args.remove(idx); | |
| 221 | + } | |
| 205 | 222 | } |
| 206 | - Terminator::CondBranch { true_dest, true_args, false_dest, false_args, .. } => { | |
| 223 | + Terminator::CondBranch { | |
| 224 | + true_dest, | |
| 225 | + true_args, | |
| 226 | + false_dest, | |
| 227 | + false_args, | |
| 228 | + .. | |
| 229 | + } => { | |
| 207 | 230 | if *true_dest == target { |
| 208 | - debug_assert!(idx < true_args.len(), | |
| 209 | - "drop_branch_arg: cond_branch true_args out of sync"); | |
| 210 | - if idx < true_args.len() { true_args.remove(idx); } | |
| 231 | + debug_assert!( | |
| 232 | + idx < true_args.len(), | |
| 233 | + "drop_branch_arg: cond_branch true_args out of sync" | |
| 234 | + ); | |
| 235 | + if idx < true_args.len() { | |
| 236 | + true_args.remove(idx); | |
| 237 | + } | |
| 211 | 238 | } |
| 212 | 239 | if *false_dest == target { |
| 213 | - debug_assert!(idx < false_args.len(), | |
| 214 | - "drop_branch_arg: cond_branch false_args out of sync"); | |
| 215 | - if idx < false_args.len() { false_args.remove(idx); } | |
| 240 | + debug_assert!( | |
| 241 | + idx < false_args.len(), | |
| 242 | + "drop_branch_arg: cond_branch false_args out of sync" | |
| 243 | + ); | |
| 244 | + if idx < false_args.len() { | |
| 245 | + false_args.remove(idx); | |
| 246 | + } | |
| 216 | 247 | } |
| 217 | 248 | } |
| 218 | 249 | Terminator::Switch { cases, default, .. } => { |
@@ -221,11 +252,12 @@ fn drop_branch_arg(term: &mut Terminator, target: BlockId, idx: usize) { | ||
| 221 | 252 | // M-6: if we ever see a Switch that branches into a |
| 222 | 253 | // block we're stripping params from, that's a real IR |
| 223 | 254 | // validity bug. Assert instead of silently skipping. |
| 224 | - let touches_target = cases.iter().any(|(_, b)| *b == target) | |
| 225 | - || *default == target; | |
| 226 | - debug_assert!(!touches_target, | |
| 255 | + let touches_target = cases.iter().any(|(_, b)| *b == target) || *default == target; | |
| 256 | + debug_assert!( | |
| 257 | + !touches_target, | |
| 227 | 258 | "drop_branch_arg: Switch terminator branches to a block with params \ |
| 228 | - — verifier should reject this construction"); | |
| 259 | + — verifier should reject this construction" | |
| 260 | + ); | |
| 229 | 261 | } |
| 230 | 262 | _ => {} |
| 231 | 263 | } |
@@ -234,12 +266,17 @@ fn drop_branch_arg(term: &mut Terminator, target: BlockId, idx: usize) { | ||
| 234 | 266 | pub struct Dce; |
| 235 | 267 | |
| 236 | 268 | impl Pass for Dce { |
| 237 | - fn name(&self) -> &'static str { "dce" } | |
| 269 | + fn name(&self) -> &'static str { | |
| 270 | + "dce" | |
| 271 | + } | |
| 238 | 272 | fn run(&self, module: &mut Module) -> bool { |
| 239 | - let pure_internal_calls: Vec<bool> = module.functions.iter().map(|func| func.is_pure).collect(); | |
| 273 | + let pure_internal_calls: Vec<bool> = | |
| 274 | + module.functions.iter().map(|func| func.is_pure).collect(); | |
| 240 | 275 | let mut changed = false; |
| 241 | 276 | for func in &mut module.functions { |
| 242 | - if dce_function(func, &pure_internal_calls) { changed = true; } | |
| 277 | + if dce_function(func, &pure_internal_calls) { | |
| 278 | + changed = true; | |
| 279 | + } | |
| 243 | 280 | } |
| 244 | 281 | changed |
| 245 | 282 | } |
@@ -248,18 +285,27 @@ impl Pass for Dce { | ||
| 248 | 285 | #[cfg(test)] |
| 249 | 286 | mod tests { |
| 250 | 287 | use super::*; |
| 251 | - use crate::ir::types::{IrType, IntWidth, FloatWidth}; | |
| 252 | - use crate::lexer::{Span, Position}; | |
| 288 | + use crate::ir::types::{FloatWidth, IntWidth, IrType}; | |
| 289 | + use crate::lexer::{Position, Span}; | |
| 253 | 290 | |
| 254 | 291 | fn dummy_span() -> Span { |
| 255 | 292 | let p = Position { line: 1, col: 1 }; |
| 256 | - Span { start: p, end: p, file_id: 0 } | |
| 293 | + Span { | |
| 294 | + start: p, | |
| 295 | + end: p, | |
| 296 | + file_id: 0, | |
| 297 | + } | |
| 257 | 298 | } |
| 258 | 299 | |
| 259 | 300 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 260 | 301 | let id = f.next_value_id(); |
| 261 | 302 | let entry = f.entry; |
| 262 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 303 | + f.block_mut(entry).insts.push(Inst { | |
| 304 | + id, | |
| 305 | + kind, | |
| 306 | + ty, | |
| 307 | + span: dummy_span(), | |
| 308 | + }); | |
| 263 | 309 | id |
| 264 | 310 | } |
| 265 | 311 | |
@@ -267,7 +313,11 @@ mod tests { | ||
| 267 | 313 | fn removes_unused_const() { |
| 268 | 314 | let mut m = Module::new("t".into()); |
| 269 | 315 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 270 | - push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 316 | + push( | |
| 317 | + &mut f, | |
| 318 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 319 | + IrType::Int(IntWidth::I32), | |
| 320 | + ); | |
| 271 | 321 | let entry = f.entry; |
| 272 | 322 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 273 | 323 | m.add_function(f); |
@@ -280,7 +330,11 @@ mod tests { | ||
| 280 | 330 | fn keeps_const_used_by_return() { |
| 281 | 331 | let mut m = Module::new("t".into()); |
| 282 | 332 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 283 | - let v = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 333 | + let v = push( | |
| 334 | + &mut f, | |
| 335 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 336 | + IrType::Int(IntWidth::I32), | |
| 337 | + ); | |
| 284 | 338 | let entry = f.entry; |
| 285 | 339 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(v))); |
| 286 | 340 | m.add_function(f); |
@@ -294,11 +348,16 @@ mod tests { | ||
| 294 | 348 | // Alloca + Store should both stay even though no one loads. |
| 295 | 349 | let mut m = Module::new("t".into()); |
| 296 | 350 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 297 | - let addr = push(&mut f, | |
| 351 | + let addr = push( | |
| 352 | + &mut f, | |
| 298 | 353 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 299 | 354 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 300 | 355 | ); |
| 301 | - let v = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 356 | + let v = push( | |
| 357 | + &mut f, | |
| 358 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 359 | + IrType::Int(IntWidth::I32), | |
| 360 | + ); | |
| 302 | 361 | push(&mut f, InstKind::Store(v, addr), IrType::Void); |
| 303 | 362 | let entry = f.entry; |
| 304 | 363 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
@@ -317,8 +376,16 @@ mod tests { | ||
| 317 | 376 | // After DCE: only the terminator remains. |
| 318 | 377 | let mut m = Module::new("t".into()); |
| 319 | 378 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 320 | - let a = push(&mut f, InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 321 | - let b = push(&mut f, InstKind::ConstInt(4, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 379 | + let a = push( | |
| 380 | + &mut f, | |
| 381 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 382 | + IrType::Int(IntWidth::I32), | |
| 383 | + ); | |
| 384 | + let b = push( | |
| 385 | + &mut f, | |
| 386 | + InstKind::ConstInt(4, IntWidth::I32), | |
| 387 | + IrType::Int(IntWidth::I32), | |
| 388 | + ); | |
| 322 | 389 | let c = push(&mut f, InstKind::IAdd(a, b), IrType::Int(IntWidth::I32)); |
| 323 | 390 | let _d = push(&mut f, InstKind::INeg(c), IrType::Int(IntWidth::I32)); |
| 324 | 391 | let entry = f.entry; |
@@ -333,7 +400,8 @@ mod tests { | ||
| 333 | 400 | fn keeps_call_even_if_result_unused() { |
| 334 | 401 | let mut m = Module::new("t".into()); |
| 335 | 402 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 336 | - push(&mut f, | |
| 403 | + push( | |
| 404 | + &mut f, | |
| 337 | 405 | InstKind::RuntimeCall(RuntimeFunc::PrintNewline, vec![]), |
| 338 | 406 | IrType::Void, |
| 339 | 407 | ); |
@@ -351,7 +419,11 @@ mod tests { | ||
| 351 | 419 | |
| 352 | 420 | let mut callee = Function::new("pure_fn".into(), vec![], IrType::Int(IntWidth::I32)); |
| 353 | 421 | callee.is_pure = true; |
| 354 | - let c = push(&mut callee, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 422 | + let c = push( | |
| 423 | + &mut callee, | |
| 424 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 425 | + IrType::Int(IntWidth::I32), | |
| 426 | + ); | |
| 355 | 427 | let callee_entry = callee.entry; |
| 356 | 428 | callee.block_mut(callee_entry).terminator = Some(Terminator::Return(Some(c))); |
| 357 | 429 | m.add_function(callee); |
@@ -367,15 +439,26 @@ mod tests { | ||
| 367 | 439 | m.add_function(caller); |
| 368 | 440 | |
| 369 | 441 | assert!(Dce.run(&mut m)); |
| 370 | - assert!(m.functions[1].blocks[0].insts.is_empty(), "unused PURE call should be removed"); | |
| 442 | + assert!( | |
| 443 | + m.functions[1].blocks[0].insts.is_empty(), | |
| 444 | + "unused PURE call should be removed" | |
| 445 | + ); | |
| 371 | 446 | } |
| 372 | 447 | |
| 373 | 448 | #[test] |
| 374 | 449 | fn float_chain_is_dead() { |
| 375 | 450 | let mut m = Module::new("t".into()); |
| 376 | 451 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 377 | - let a = push(&mut f, InstKind::ConstFloat(1.5, FloatWidth::F64), IrType::Float(FloatWidth::F64)); | |
| 378 | - let b = push(&mut f, InstKind::ConstFloat(2.5, FloatWidth::F64), IrType::Float(FloatWidth::F64)); | |
| 452 | + let a = push( | |
| 453 | + &mut f, | |
| 454 | + InstKind::ConstFloat(1.5, FloatWidth::F64), | |
| 455 | + IrType::Float(FloatWidth::F64), | |
| 456 | + ); | |
| 457 | + let b = push( | |
| 458 | + &mut f, | |
| 459 | + InstKind::ConstFloat(2.5, FloatWidth::F64), | |
| 460 | + IrType::Float(FloatWidth::F64), | |
| 461 | + ); | |
| 379 | 462 | let _ = push(&mut f, InstKind::FAdd(a, b), IrType::Float(FloatWidth::F64)); |
| 380 | 463 | let entry = f.entry; |
| 381 | 464 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
src/opt/dead_arg.rsmodified@@ -10,7 +10,9 @@ use super::pass::Pass; | ||
| 10 | 10 | pub struct DeadArgElim; |
| 11 | 11 | |
| 12 | 12 | impl Pass for DeadArgElim { |
| 13 | - fn name(&self) -> &'static str { "dead-arg-elim" } | |
| 13 | + fn name(&self) -> &'static str { | |
| 14 | + "dead-arg-elim" | |
| 15 | + } | |
| 14 | 16 | |
| 15 | 17 | fn run(&self, module: &mut Module) -> bool { |
| 16 | 18 | let mut live_masks: Vec<Vec<bool>> = module |
@@ -196,9 +198,12 @@ fn block_terminator_uses_param(term: &Option<Terminator>, param_id: ValueId) -> | ||
| 196 | 198 | match term { |
| 197 | 199 | Some(Terminator::Return(Some(v))) => *v == param_id, |
| 198 | 200 | Some(Terminator::Branch(_, args)) => args.contains(¶m_id), |
| 199 | - Some(Terminator::CondBranch { cond, true_args, false_args, .. }) => { | |
| 200 | - *cond == param_id || true_args.contains(¶m_id) || false_args.contains(¶m_id) | |
| 201 | - } | |
| 201 | + Some(Terminator::CondBranch { | |
| 202 | + cond, | |
| 203 | + true_args, | |
| 204 | + false_args, | |
| 205 | + .. | |
| 206 | + }) => *cond == param_id || true_args.contains(¶m_id) || false_args.contains(¶m_id), | |
| 202 | 207 | Some(Terminator::Switch { selector, .. }) => *selector == param_id, |
| 203 | 208 | _ => false, |
| 204 | 209 | } |
@@ -207,18 +212,27 @@ fn block_terminator_uses_param(term: &Option<Terminator>, param_id: ValueId) -> | ||
| 207 | 212 | #[cfg(test)] |
| 208 | 213 | mod tests { |
| 209 | 214 | use super::*; |
| 210 | - use crate::ir::types::{IrType, IntWidth}; | |
| 215 | + use crate::ir::types::{IntWidth, IrType}; | |
| 211 | 216 | use crate::lexer::{Position, Span}; |
| 212 | 217 | |
| 213 | 218 | fn span() -> Span { |
| 214 | 219 | let pos = Position { line: 0, col: 0 }; |
| 215 | - Span { file_id: 0, start: pos, end: pos } | |
| 220 | + Span { | |
| 221 | + file_id: 0, | |
| 222 | + start: pos, | |
| 223 | + end: pos, | |
| 224 | + } | |
| 216 | 225 | } |
| 217 | 226 | |
| 218 | 227 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 219 | 228 | let id = f.next_value_id(); |
| 220 | 229 | let entry = f.entry; |
| 221 | - f.block_mut(entry).insts.push(Inst { id, kind, ty: ty.clone(), span: span() }); | |
| 230 | + f.block_mut(entry).insts.push(Inst { | |
| 231 | + id, | |
| 232 | + kind, | |
| 233 | + ty: ty.clone(), | |
| 234 | + span: span(), | |
| 235 | + }); | |
| 222 | 236 | f.register_type(id, ty); |
| 223 | 237 | id |
| 224 | 238 | } |
@@ -248,8 +262,16 @@ mod tests { | ||
| 248 | 262 | module.add_function(callee); |
| 249 | 263 | |
| 250 | 264 | let mut caller = Function::new("main".into(), vec![], IrType::Int(IntWidth::I32)); |
| 251 | - let a = push(&mut caller, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 252 | - let b = push(&mut caller, InstKind::ConstInt(9, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 265 | + let a = push( | |
| 266 | + &mut caller, | |
| 267 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 268 | + IrType::Int(IntWidth::I32), | |
| 269 | + ); | |
| 270 | + let b = push( | |
| 271 | + &mut caller, | |
| 272 | + InstKind::ConstInt(9, IntWidth::I32), | |
| 273 | + IrType::Int(IntWidth::I32), | |
| 274 | + ); | |
| 253 | 275 | let call = push( |
| 254 | 276 | &mut caller, |
| 255 | 277 | InstKind::Call(FuncRef::Internal(0), vec![a, b]), |
src/opt/dead_func.rsmodified@@ -10,18 +10,22 @@ | ||
| 10 | 10 | //! (runtime, external linkage) are also kept since the call might come |
| 11 | 11 | //! from outside. |
| 12 | 12 | |
| 13 | -use std::collections::HashSet; | |
| 14 | -use crate::ir::inst::*; | |
| 15 | 13 | use super::pass::Pass; |
| 14 | +use crate::ir::inst::*; | |
| 15 | +use std::collections::HashSet; | |
| 16 | 16 | |
| 17 | 17 | pub struct DeadFuncElim; |
| 18 | 18 | |
| 19 | 19 | impl Pass for DeadFuncElim { |
| 20 | - fn name(&self) -> &'static str { "dead-func-elim" } | |
| 20 | + fn name(&self) -> &'static str { | |
| 21 | + "dead-func-elim" | |
| 22 | + } | |
| 21 | 23 | |
| 22 | 24 | fn run(&self, module: &mut Module) -> bool { |
| 23 | 25 | let n = module.functions.len(); |
| 24 | - if n <= 1 { return false; } | |
| 26 | + if n <= 1 { | |
| 27 | + return false; | |
| 28 | + } | |
| 25 | 29 | |
| 26 | 30 | // Collect all function indices that are referenced by Internal calls. |
| 27 | 31 | let mut referenced: HashSet<u32> = HashSet::new(); |
@@ -78,7 +82,9 @@ impl Pass for DeadFuncElim { | ||
| 78 | 82 | .filter(|i| !referenced.contains(&(*i as u32))) |
| 79 | 83 | .collect(); |
| 80 | 84 | |
| 81 | - if dead.is_empty() { return false; } | |
| 85 | + if dead.is_empty() { | |
| 86 | + return false; | |
| 87 | + } | |
| 82 | 88 | |
| 83 | 89 | // Remove in reverse order so indices stay valid. |
| 84 | 90 | for &idx in dead.iter().rev() { |
@@ -117,13 +123,17 @@ impl Pass for DeadFuncElim { | ||
| 117 | 123 | #[cfg(test)] |
| 118 | 124 | mod tests { |
| 119 | 125 | use super::*; |
| 120 | - use crate::ir::types::{IrType, IntWidth}; | |
| 126 | + use crate::ir::types::{IntWidth, IrType}; | |
| 127 | + use crate::lexer::{Position, Span}; | |
| 121 | 128 | use crate::opt::pass::Pass; |
| 122 | - use crate::lexer::{Span, Position}; | |
| 123 | 129 | |
| 124 | 130 | fn span() -> Span { |
| 125 | 131 | let pos = Position { line: 0, col: 0 }; |
| 126 | - Span { file_id: 0, start: pos, end: pos } | |
| 132 | + Span { | |
| 133 | + file_id: 0, | |
| 134 | + start: pos, | |
| 135 | + end: pos, | |
| 136 | + } | |
| 127 | 137 | } |
| 128 | 138 | |
| 129 | 139 | #[test] |
@@ -155,7 +165,9 @@ mod tests { | ||
| 155 | 165 | let cid = caller.next_value_id(); |
| 156 | 166 | caller.register_type(cid, IrType::Void); |
| 157 | 167 | caller.block_mut(caller.entry).insts.push(Inst { |
| 158 | - id: cid, ty: IrType::Void, span: span(), | |
| 168 | + id: cid, | |
| 169 | + ty: IrType::Void, | |
| 170 | + span: span(), | |
| 159 | 171 | kind: InstKind::Call(FuncRef::Internal(1), vec![]), |
| 160 | 172 | }); |
| 161 | 173 | caller.block_mut(caller.entry).terminator = Some(Terminator::Return(None)); |
@@ -212,7 +224,10 @@ mod tests { | ||
| 212 | 224 | |
| 213 | 225 | let pass = DeadFuncElim; |
| 214 | 226 | let changed = pass.run(&mut m); |
| 215 | - assert!(changed, "dead helper should be removed while keeping program body"); | |
| 227 | + assert!( | |
| 228 | + changed, | |
| 229 | + "dead helper should be removed while keeping program body" | |
| 230 | + ); | |
| 216 | 231 | assert_eq!(m.functions.len(), 1); |
| 217 | 232 | assert_eq!(m.functions[0].name, "__prog_entry"); |
| 218 | 233 | } |
src/opt/dep_analysis.rsmodified@@ -11,8 +11,8 @@ | ||
| 11 | 11 | //! - Column-major strides are compile-time constants for fixed-shape arrays. |
| 12 | 12 | //! - INTENT(IN) arguments cannot alias INTENT(OUT) arguments. |
| 13 | 13 | |
| 14 | -use std::collections::HashSet; | |
| 15 | 14 | use crate::ir::inst::*; |
| 15 | +use std::collections::HashSet; | |
| 16 | 16 | |
| 17 | 17 | // --------------------------------------------------------------------------- |
| 18 | 18 | // Data structures |
@@ -23,16 +23,29 @@ use crate::ir::inst::*; | ||
| 23 | 23 | #[derive(Debug, Clone)] |
| 24 | 24 | pub struct AffineExpr { |
| 25 | 25 | pub constant: i64, |
| 26 | - pub terms: Vec<(i64, ValueId)>, // (coefficient, iv) | |
| 26 | + pub terms: Vec<(i64, ValueId)>, // (coefficient, iv) | |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | impl AffineExpr { |
| 30 | - fn zero() -> Self { Self { constant: 0, terms: Vec::new() } } | |
| 30 | + fn zero() -> Self { | |
| 31 | + Self { | |
| 32 | + constant: 0, | |
| 33 | + terms: Vec::new(), | |
| 34 | + } | |
| 35 | + } | |
| 31 | 36 | |
| 32 | - fn from_const(c: i64) -> Self { Self { constant: c, terms: Vec::new() } } | |
| 37 | + fn from_const(c: i64) -> Self { | |
| 38 | + Self { | |
| 39 | + constant: c, | |
| 40 | + terms: Vec::new(), | |
| 41 | + } | |
| 42 | + } | |
| 33 | 43 | |
| 34 | 44 | fn from_iv(iv: ValueId) -> Self { |
| 35 | - Self { constant: 0, terms: vec![(1, iv)] } | |
| 45 | + Self { | |
| 46 | + constant: 0, | |
| 47 | + terms: vec![(1, iv)], | |
| 48 | + } | |
| 36 | 49 | } |
| 37 | 50 | |
| 38 | 51 | fn add(&self, other: &Self) -> Self { |
@@ -89,11 +102,7 @@ pub struct DepResult { | ||
| 89 | 102 | /// Extract an affine expression from a GEP index by walking backwards |
| 90 | 103 | /// through arithmetic instructions. `ivs` is the set of known induction |
| 91 | 104 | /// variables for the enclosing loop nest. |
| 92 | -pub fn extract_affine( | |
| 93 | - func: &Function, | |
| 94 | - val: ValueId, | |
| 95 | - ivs: &HashSet<ValueId>, | |
| 96 | -) -> Option<AffineExpr> { | |
| 105 | +pub fn extract_affine(func: &Function, val: ValueId, ivs: &HashSet<ValueId>) -> Option<AffineExpr> { | |
| 97 | 106 | // Is this an IV? |
| 98 | 107 | if ivs.contains(&val) { |
| 99 | 108 | return Some(AffineExpr::from_iv(val)); |
@@ -142,13 +151,19 @@ pub fn extract_affine( | ||
| 142 | 151 | |
| 143 | 152 | fn resolve_const(func: &Function, vid: ValueId) -> Option<i64> { |
| 144 | 153 | let inst = find_inst(func, vid)?; |
| 145 | - if let InstKind::ConstInt(c, _) = &inst.kind { i64::try_from(*c).ok() } else { None } | |
| 154 | + if let InstKind::ConstInt(c, _) = &inst.kind { | |
| 155 | + i64::try_from(*c).ok() | |
| 156 | + } else { | |
| 157 | + None | |
| 158 | + } | |
| 146 | 159 | } |
| 147 | 160 | |
| 148 | 161 | fn find_inst(func: &Function, vid: ValueId) -> Option<&Inst> { |
| 149 | 162 | for block in &func.blocks { |
| 150 | 163 | for inst in &block.insts { |
| 151 | - if inst.id == vid { return Some(inst); } | |
| 164 | + if inst.id == vid { | |
| 165 | + return Some(inst); | |
| 166 | + } | |
| 152 | 167 | } |
| 153 | 168 | } |
| 154 | 169 | None |
@@ -203,7 +218,12 @@ fn extract_mem_ref( | ||
| 203 | 218 | // We work with the flat offset (single index for 1D GEP). |
| 204 | 219 | let idx = indices.first()?; |
| 205 | 220 | let subscript = extract_affine(func, *idx, ivs)?; |
| 206 | - Some(MemRef { inst_id, base, subscript, is_write }) | |
| 221 | + Some(MemRef { | |
| 222 | + inst_id, | |
| 223 | + base, | |
| 224 | + subscript, | |
| 225 | + is_write, | |
| 226 | + }) | |
| 207 | 227 | } |
| 208 | 228 | |
| 209 | 229 | // --------------------------------------------------------------------------- |
@@ -229,17 +249,23 @@ pub fn test_dependence(ref_a: &MemRef, ref_b: &MemRef) -> DepResult { | ||
| 229 | 249 | // If the constant is 0, they access the same element (same iteration = ok |
| 230 | 250 | // unless both are writes). If non-zero, they access different elements. |
| 231 | 251 | if diff.terms.is_empty() { |
| 232 | - return DepResult { dependent: diff.constant == 0 }; | |
| 252 | + return DepResult { | |
| 253 | + dependent: diff.constant == 0, | |
| 254 | + }; | |
| 233 | 255 | } |
| 234 | 256 | |
| 235 | 257 | // GCD of all IV coefficients in the difference. |
| 236 | - let g = diff.terms.iter() | |
| 258 | + let g = diff | |
| 259 | + .terms | |
| 260 | + .iter() | |
| 237 | 261 | .map(|(c, _)| c.unsigned_abs()) |
| 238 | 262 | .fold(0u64, gcd); |
| 239 | 263 | |
| 240 | 264 | if g == 0 { |
| 241 | 265 | // All coefficients are zero — same as fixed-distance case. |
| 242 | - return DepResult { dependent: diff.constant == 0 }; | |
| 266 | + return DepResult { | |
| 267 | + dependent: diff.constant == 0, | |
| 268 | + }; | |
| 243 | 269 | } |
| 244 | 270 | |
| 245 | 271 | // GCD test: if gcd does not divide the constant difference, |
@@ -249,7 +275,11 @@ pub fn test_dependence(ref_a: &MemRef, ref_b: &MemRef) -> DepResult { | ||
| 249 | 275 | } |
| 250 | 276 | |
| 251 | 277 | fn gcd(a: u64, b: u64) -> u64 { |
| 252 | - if b == 0 { a } else { gcd(b, a % b) } | |
| 278 | + if b == 0 { | |
| 279 | + a | |
| 280 | + } else { | |
| 281 | + gcd(b, a % b) | |
| 282 | + } | |
| 253 | 283 | } |
| 254 | 284 | |
| 255 | 285 | // --------------------------------------------------------------------------- |
@@ -287,17 +317,26 @@ pub fn fusion_legal( | ||
| 287 | 317 | let refs_b_raw = collect_mem_refs(func, body_b, &ivs_b); |
| 288 | 318 | |
| 289 | 319 | // Remap iv_b → iv_a in B's subscripts so the comparison works. |
| 290 | - let refs_b: Vec<MemRef> = refs_b_raw.into_iter().map(|mut r| { | |
| 291 | - for term in &mut r.subscript.terms { | |
| 292 | - if term.1 == iv_b { term.1 = iv_a; } | |
| 293 | - } | |
| 294 | - r | |
| 295 | - }).collect(); | |
| 320 | + let refs_b: Vec<MemRef> = refs_b_raw | |
| 321 | + .into_iter() | |
| 322 | + .map(|mut r| { | |
| 323 | + for term in &mut r.subscript.terms { | |
| 324 | + if term.1 == iv_b { | |
| 325 | + term.1 = iv_a; | |
| 326 | + } | |
| 327 | + } | |
| 328 | + r | |
| 329 | + }) | |
| 330 | + .collect(); | |
| 296 | 331 | |
| 297 | 332 | for ra in &refs_a { |
| 298 | 333 | for rb in &refs_b { |
| 299 | - if !ra.is_write && !rb.is_write { continue; } | |
| 300 | - if ra.base != rb.base { continue; } | |
| 334 | + if !ra.is_write && !rb.is_write { | |
| 335 | + continue; | |
| 336 | + } | |
| 337 | + if ra.base != rb.base { | |
| 338 | + continue; | |
| 339 | + } | |
| 301 | 340 | |
| 302 | 341 | let diff = rb.subscript.sub(&ra.subscript); |
| 303 | 342 | |
@@ -330,9 +369,13 @@ pub fn interchange_legal( | ||
| 330 | 369 | |
| 331 | 370 | // For each pair of refs where at least one is a write: |
| 332 | 371 | for i in 0..refs.len() { |
| 333 | - for j in (i+1)..refs.len() { | |
| 334 | - if !refs[i].is_write && !refs[j].is_write { continue; } | |
| 335 | - if refs[i].base != refs[j].base { continue; } | |
| 372 | + for j in (i + 1)..refs.len() { | |
| 373 | + if !refs[i].is_write && !refs[j].is_write { | |
| 374 | + continue; | |
| 375 | + } | |
| 376 | + if refs[i].base != refs[j].base { | |
| 377 | + continue; | |
| 378 | + } | |
| 336 | 379 | |
| 337 | 380 | // Both refs share a base and at least one is a write. |
| 338 | 381 | // Check if the subscript difference has non-zero |
@@ -370,8 +413,14 @@ mod tests { | ||
| 370 | 413 | #[test] |
| 371 | 414 | fn affine_add_sub() { |
| 372 | 415 | let iv = ValueId(100); |
| 373 | - let a = AffineExpr { constant: 3, terms: vec![(2, iv)] }; | |
| 374 | - let b = AffineExpr { constant: 1, terms: vec![(1, iv)] }; | |
| 416 | + let a = AffineExpr { | |
| 417 | + constant: 3, | |
| 418 | + terms: vec![(2, iv)], | |
| 419 | + }; | |
| 420 | + let b = AffineExpr { | |
| 421 | + constant: 1, | |
| 422 | + terms: vec![(1, iv)], | |
| 423 | + }; | |
| 375 | 424 | let sum = a.add(&b); |
| 376 | 425 | assert_eq!(sum.constant, 4); |
| 377 | 426 | assert_eq!(sum.terms.len(), 1); |
@@ -386,7 +435,10 @@ mod tests { | ||
| 386 | 435 | #[test] |
| 387 | 436 | fn affine_scale() { |
| 388 | 437 | let iv = ValueId(100); |
| 389 | - let a = AffineExpr { constant: 2, terms: vec![(3, iv)] }; | |
| 438 | + let a = AffineExpr { | |
| 439 | + constant: 2, | |
| 440 | + terms: vec![(3, iv)], | |
| 441 | + }; | |
| 390 | 442 | let scaled = a.scale(4); |
| 391 | 443 | assert_eq!(scaled.constant, 8); |
| 392 | 444 | assert_eq!(scaled.terms[0], (12, iv)); |
@@ -399,13 +451,21 @@ mod tests { | ||
| 399 | 451 | // constant = 1 ≠ 0 → independent. |
| 400 | 452 | let iv = ValueId(100); |
| 401 | 453 | let ref_a = MemRef { |
| 402 | - inst_id: ValueId(0), base: ValueId(50), | |
| 403 | - subscript: AffineExpr { constant: 1, terms: vec![(2, iv)] }, | |
| 454 | + inst_id: ValueId(0), | |
| 455 | + base: ValueId(50), | |
| 456 | + subscript: AffineExpr { | |
| 457 | + constant: 1, | |
| 458 | + terms: vec![(2, iv)], | |
| 459 | + }, | |
| 404 | 460 | is_write: true, |
| 405 | 461 | }; |
| 406 | 462 | let ref_b = MemRef { |
| 407 | - inst_id: ValueId(1), base: ValueId(50), | |
| 408 | - subscript: AffineExpr { constant: 2, terms: vec![(2, iv)] }, | |
| 463 | + inst_id: ValueId(1), | |
| 464 | + base: ValueId(50), | |
| 465 | + subscript: AffineExpr { | |
| 466 | + constant: 2, | |
| 467 | + terms: vec![(2, iv)], | |
| 468 | + }, | |
| 409 | 469 | is_write: false, |
| 410 | 470 | }; |
| 411 | 471 | let dep = test_dependence(&ref_a, &ref_b); |
@@ -417,13 +477,21 @@ mod tests { | ||
| 417 | 477 | // a(i) vs a(i): same subscript → always dependent. |
| 418 | 478 | let iv = ValueId(100); |
| 419 | 479 | let ref_a = MemRef { |
| 420 | - inst_id: ValueId(0), base: ValueId(50), | |
| 421 | - subscript: AffineExpr { constant: 0, terms: vec![(1, iv)] }, | |
| 480 | + inst_id: ValueId(0), | |
| 481 | + base: ValueId(50), | |
| 482 | + subscript: AffineExpr { | |
| 483 | + constant: 0, | |
| 484 | + terms: vec![(1, iv)], | |
| 485 | + }, | |
| 422 | 486 | is_write: true, |
| 423 | 487 | }; |
| 424 | 488 | let ref_b = MemRef { |
| 425 | - inst_id: ValueId(1), base: ValueId(50), | |
| 426 | - subscript: AffineExpr { constant: 0, terms: vec![(1, iv)] }, | |
| 489 | + inst_id: ValueId(1), | |
| 490 | + base: ValueId(50), | |
| 491 | + subscript: AffineExpr { | |
| 492 | + constant: 0, | |
| 493 | + terms: vec![(1, iv)], | |
| 494 | + }, | |
| 427 | 495 | is_write: false, |
| 428 | 496 | }; |
| 429 | 497 | let dep = test_dependence(&ref_a, &ref_b); |
@@ -436,33 +504,55 @@ mod tests { | ||
| 436 | 504 | // (they cancel: 3-3=0). So diff = constant 1, no terms → independent. |
| 437 | 505 | let iv = ValueId(100); |
| 438 | 506 | let ref_a = MemRef { |
| 439 | - inst_id: ValueId(0), base: ValueId(50), | |
| 440 | - subscript: AffineExpr { constant: 0, terms: vec![(3, iv)] }, | |
| 507 | + inst_id: ValueId(0), | |
| 508 | + base: ValueId(50), | |
| 509 | + subscript: AffineExpr { | |
| 510 | + constant: 0, | |
| 511 | + terms: vec![(3, iv)], | |
| 512 | + }, | |
| 441 | 513 | is_write: true, |
| 442 | 514 | }; |
| 443 | 515 | let ref_b = MemRef { |
| 444 | - inst_id: ValueId(1), base: ValueId(50), | |
| 445 | - subscript: AffineExpr { constant: 1, terms: vec![(3, iv)] }, | |
| 516 | + inst_id: ValueId(1), | |
| 517 | + base: ValueId(50), | |
| 518 | + subscript: AffineExpr { | |
| 519 | + constant: 1, | |
| 520 | + terms: vec![(3, iv)], | |
| 521 | + }, | |
| 446 | 522 | is_write: false, |
| 447 | 523 | }; |
| 448 | 524 | let dep = test_dependence(&ref_a, &ref_b); |
| 449 | - assert!(!dep.dependent, "a(3i) and a(3i+1) should be independent by GCD test"); | |
| 525 | + assert!( | |
| 526 | + !dep.dependent, | |
| 527 | + "a(3i) and a(3i+1) should be independent by GCD test" | |
| 528 | + ); | |
| 450 | 529 | } |
| 451 | 530 | |
| 452 | 531 | #[test] |
| 453 | 532 | fn distinct_bases_independent() { |
| 454 | 533 | let iv = ValueId(100); |
| 455 | 534 | let ref_a = MemRef { |
| 456 | - inst_id: ValueId(0), base: ValueId(50), | |
| 457 | - subscript: AffineExpr { constant: 0, terms: vec![(1, iv)] }, | |
| 535 | + inst_id: ValueId(0), | |
| 536 | + base: ValueId(50), | |
| 537 | + subscript: AffineExpr { | |
| 538 | + constant: 0, | |
| 539 | + terms: vec![(1, iv)], | |
| 540 | + }, | |
| 458 | 541 | is_write: true, |
| 459 | 542 | }; |
| 460 | 543 | let ref_b = MemRef { |
| 461 | - inst_id: ValueId(1), base: ValueId(60), // different base | |
| 462 | - subscript: AffineExpr { constant: 0, terms: vec![(1, iv)] }, | |
| 544 | + inst_id: ValueId(1), | |
| 545 | + base: ValueId(60), // different base | |
| 546 | + subscript: AffineExpr { | |
| 547 | + constant: 0, | |
| 548 | + terms: vec![(1, iv)], | |
| 549 | + }, | |
| 463 | 550 | is_write: false, |
| 464 | 551 | }; |
| 465 | 552 | let dep = test_dependence(&ref_a, &ref_b); |
| 466 | - assert!(!dep.dependent, "distinct bases should be independent (Fortran no-alias)"); | |
| 553 | + assert!( | |
| 554 | + !dep.dependent, | |
| 555 | + "distinct bases should be independent (Fortran no-alias)" | |
| 556 | + ); | |
| 467 | 557 | } |
| 468 | 558 | } |
src/opt/dse.rsmodified@@ -25,8 +25,8 @@ | ||
| 25 | 25 | //! initialization sequences where a variable is zeroed and then |
| 26 | 26 | //! immediately written with the real value. |
| 27 | 27 | |
| 28 | -use super::pass::Pass; | |
| 29 | 28 | use super::alias::{self, AliasResult}; |
| 29 | +use super::pass::Pass; | |
| 30 | 30 | use crate::ir::inst::*; |
| 31 | 31 | use crate::ir::types::IrType; |
| 32 | 32 | use std::collections::HashSet; |
@@ -58,7 +58,10 @@ fn dse_block(func: &mut Function, block_idx: usize) -> bool { | ||
| 58 | 58 | } |
| 59 | 59 | } |
| 60 | 60 | } |
| 61 | - next_pending.push(PendingStore { ptr: *ptr, inst_idx: i }); | |
| 61 | + next_pending.push(PendingStore { | |
| 62 | + ptr: *ptr, | |
| 63 | + inst_idx: i, | |
| 64 | + }); | |
| 62 | 65 | pending = next_pending; |
| 63 | 66 | } |
| 64 | 67 | |
@@ -82,18 +85,13 @@ fn dse_block(func: &mut Function, block_idx: usize) -> bool { | ||
| 82 | 85 | let pointer_args: Vec<ValueId> = args |
| 83 | 86 | .iter() |
| 84 | 87 | .copied() |
| 85 | - .filter(|arg| { | |
| 86 | - matches!( | |
| 87 | - func.value_type(*arg), | |
| 88 | - Some(IrType::Ptr(_)) | |
| 89 | - ) | |
| 90 | - }) | |
| 88 | + .filter(|arg| matches!(func.value_type(*arg), Some(IrType::Ptr(_)))) | |
| 91 | 89 | .collect(); |
| 92 | 90 | if !pointer_args.is_empty() { |
| 93 | 91 | pending.retain(|entry| { |
| 94 | - pointer_args.iter().all(|arg| { | |
| 95 | - !alias::may_reach_through_call_arg(func, entry.ptr, *arg) | |
| 96 | - }) | |
| 92 | + pointer_args | |
| 93 | + .iter() | |
| 94 | + .all(|arg| !alias::may_reach_through_call_arg(func, entry.ptr, *arg)) | |
| 97 | 95 | }); |
| 98 | 96 | } |
| 99 | 97 | } |
@@ -136,11 +134,15 @@ fn dse_function(func: &mut Function) -> bool { | ||
| 136 | 134 | pub struct Dse; |
| 137 | 135 | |
| 138 | 136 | impl Pass for Dse { |
| 139 | - fn name(&self) -> &'static str { "dse" } | |
| 137 | + fn name(&self) -> &'static str { | |
| 138 | + "dse" | |
| 139 | + } | |
| 140 | 140 | fn run(&self, module: &mut Module) -> bool { |
| 141 | 141 | let mut changed = false; |
| 142 | 142 | for func in &mut module.functions { |
| 143 | - if dse_function(func) { changed = true; } | |
| 143 | + if dse_function(func) { | |
| 144 | + changed = true; | |
| 145 | + } | |
| 144 | 146 | } |
| 145 | 147 | changed |
| 146 | 148 | } |
@@ -149,24 +151,39 @@ impl Pass for Dse { | ||
| 149 | 151 | #[cfg(test)] |
| 150 | 152 | mod tests { |
| 151 | 153 | use super::*; |
| 152 | - use crate::ir::types::{IrType, IntWidth}; | |
| 153 | - use crate::lexer::{Span, Position}; | |
| 154 | + use crate::ir::types::{IntWidth, IrType}; | |
| 155 | + use crate::lexer::{Position, Span}; | |
| 154 | 156 | |
| 155 | 157 | fn dummy_span() -> Span { |
| 156 | 158 | let p = Position { line: 1, col: 1 }; |
| 157 | - Span { start: p, end: p, file_id: 0 } | |
| 159 | + Span { | |
| 160 | + start: p, | |
| 161 | + end: p, | |
| 162 | + file_id: 0, | |
| 163 | + } | |
| 158 | 164 | } |
| 159 | 165 | |
| 160 | 166 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 161 | 167 | let id = f.next_value_id(); |
| 162 | 168 | let entry = f.entry; |
| 163 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 169 | + f.block_mut(entry).insts.push(Inst { | |
| 170 | + id, | |
| 171 | + kind, | |
| 172 | + ty, | |
| 173 | + span: dummy_span(), | |
| 174 | + }); | |
| 164 | 175 | id |
| 165 | 176 | } |
| 166 | 177 | |
| 167 | - fn ptr_ty() -> IrType { IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))) } | |
| 168 | - fn alloca_ty() -> IrType { IrType::Int(IntWidth::I32) } | |
| 169 | - fn i32_ty() -> IrType { IrType::Int(IntWidth::I32) } | |
| 178 | + fn ptr_ty() -> IrType { | |
| 179 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))) | |
| 180 | + } | |
| 181 | + fn alloca_ty() -> IrType { | |
| 182 | + IrType::Int(IntWidth::I32) | |
| 183 | + } | |
| 184 | + fn i32_ty() -> IrType { | |
| 185 | + IrType::Int(IntWidth::I32) | |
| 186 | + } | |
| 170 | 187 | |
| 171 | 188 | /// %ptr = alloca i32 |
| 172 | 189 | /// store 1, %ptr |
@@ -177,10 +194,10 @@ mod tests { | ||
| 177 | 194 | let mut m = Module::new("t".into()); |
| 178 | 195 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 179 | 196 | let ptr = push(&mut f, InstKind::Alloca(alloca_ty()), ptr_ty()); |
| 180 | - let v1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), i32_ty()); | |
| 181 | - let v2 = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), i32_ty()); | |
| 182 | - push(&mut f, InstKind::Store(v1, ptr), IrType::Void); // dead | |
| 183 | - push(&mut f, InstKind::Store(v2, ptr), IrType::Void); // live | |
| 197 | + let v1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), i32_ty()); | |
| 198 | + let v2 = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), i32_ty()); | |
| 199 | + push(&mut f, InstKind::Store(v1, ptr), IrType::Void); // dead | |
| 200 | + push(&mut f, InstKind::Store(v2, ptr), IrType::Void); // live | |
| 184 | 201 | let _loaded = push(&mut f, InstKind::Load(ptr), i32_ty()); |
| 185 | 202 | let entry = f.entry; |
| 186 | 203 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
@@ -191,7 +208,10 @@ mod tests { | ||
| 191 | 208 | let insts = &m.functions[0].blocks[0].insts; |
| 192 | 209 | assert_eq!(insts.len(), 5, "first store should be eliminated"); |
| 193 | 210 | // The surviving Store should use v2. |
| 194 | - let stores: Vec<_> = insts.iter().filter(|i| matches!(i.kind, InstKind::Store(..))).collect(); | |
| 211 | + let stores: Vec<_> = insts | |
| 212 | + .iter() | |
| 213 | + .filter(|i| matches!(i.kind, InstKind::Store(..))) | |
| 214 | + .collect(); | |
| 195 | 215 | assert_eq!(stores.len(), 1); |
| 196 | 216 | assert!(matches!(stores[0].kind, InstKind::Store(v, _) if v == v2)); |
| 197 | 217 | } |
@@ -202,7 +222,7 @@ mod tests { | ||
| 202 | 222 | let mut m = Module::new("t".into()); |
| 203 | 223 | let mut f = Function::new("f".into(), vec![], i32_ty()); |
| 204 | 224 | let ptr = push(&mut f, InstKind::Alloca(alloca_ty()), ptr_ty()); |
| 205 | - let v1 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), i32_ty()); | |
| 225 | + let v1 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), i32_ty()); | |
| 206 | 226 | push(&mut f, InstKind::Store(v1, ptr), IrType::Void); |
| 207 | 227 | let loaded = push(&mut f, InstKind::Load(ptr), i32_ty()); |
| 208 | 228 | let entry = f.entry; |
@@ -223,23 +243,35 @@ mod tests { | ||
| 223 | 243 | let ptr = push(&mut f, InstKind::Alloca(arr_ty), arr_ptr_ty); |
| 224 | 244 | let off0 = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), i32_ty()); |
| 225 | 245 | let off1 = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), i32_ty()); |
| 226 | - let gep0 = push(&mut f, InstKind::GetElementPtr(ptr, vec![off0]), | |
| 227 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); | |
| 228 | - let gep1 = push(&mut f, InstKind::GetElementPtr(ptr, vec![off1]), | |
| 229 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); | |
| 246 | + let gep0 = push( | |
| 247 | + &mut f, | |
| 248 | + InstKind::GetElementPtr(ptr, vec![off0]), | |
| 249 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), | |
| 250 | + ); | |
| 251 | + let gep1 = push( | |
| 252 | + &mut f, | |
| 253 | + InstKind::GetElementPtr(ptr, vec![off1]), | |
| 254 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), | |
| 255 | + ); | |
| 230 | 256 | let v1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), i32_ty()); |
| 231 | 257 | let v2 = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), i32_ty()); |
| 232 | - push(&mut f, InstKind::Store(v1, gep0), IrType::Void); // dead | |
| 233 | - push(&mut f, InstKind::Store(v2, gep1), IrType::Void); // live | |
| 258 | + push(&mut f, InstKind::Store(v1, gep0), IrType::Void); // dead | |
| 259 | + push(&mut f, InstKind::Store(v2, gep1), IrType::Void); // live | |
| 234 | 260 | let entry = f.entry; |
| 235 | 261 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 236 | 262 | m.add_function(f); |
| 237 | 263 | |
| 238 | 264 | assert!(Dse.run(&mut m)); |
| 239 | - let stores: Vec<_> = m.functions[0].blocks[0].insts.iter() | |
| 265 | + let stores: Vec<_> = m.functions[0].blocks[0] | |
| 266 | + .insts | |
| 267 | + .iter() | |
| 240 | 268 | .filter(|i| matches!(i.kind, InstKind::Store(..))) |
| 241 | 269 | .collect(); |
| 242 | - assert_eq!(stores.len(), 1, "must-alias GEP overwrite should kill the first store"); | |
| 270 | + assert_eq!( | |
| 271 | + stores.len(), | |
| 272 | + 1, | |
| 273 | + "must-alias GEP overwrite should kill the first store" | |
| 274 | + ); | |
| 243 | 275 | assert!(matches!(stores[0].kind, InstKind::Store(v, _) if v == v2)); |
| 244 | 276 | } |
| 245 | 277 | |
@@ -249,12 +281,12 @@ mod tests { | ||
| 249 | 281 | let mut m = Module::new("t".into()); |
| 250 | 282 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 251 | 283 | let ptr = push(&mut f, InstKind::Alloca(alloca_ty()), ptr_ty()); |
| 252 | - let v1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), i32_ty()); | |
| 253 | - let v2 = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), i32_ty()); | |
| 254 | - let v3 = push(&mut f, InstKind::ConstInt(3, IntWidth::I32), i32_ty()); | |
| 255 | - push(&mut f, InstKind::Store(v1, ptr), IrType::Void); // dead | |
| 256 | - push(&mut f, InstKind::Store(v2, ptr), IrType::Void); // dead | |
| 257 | - push(&mut f, InstKind::Store(v3, ptr), IrType::Void); // live (block exit) | |
| 284 | + let v1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), i32_ty()); | |
| 285 | + let v2 = push(&mut f, InstKind::ConstInt(2, IntWidth::I32), i32_ty()); | |
| 286 | + let v3 = push(&mut f, InstKind::ConstInt(3, IntWidth::I32), i32_ty()); | |
| 287 | + push(&mut f, InstKind::Store(v1, ptr), IrType::Void); // dead | |
| 288 | + push(&mut f, InstKind::Store(v2, ptr), IrType::Void); // dead | |
| 289 | + push(&mut f, InstKind::Store(v3, ptr), IrType::Void); // live (block exit) | |
| 258 | 290 | let entry = f.entry; |
| 259 | 291 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 260 | 292 | m.add_function(f); |
@@ -263,7 +295,9 @@ mod tests { | ||
| 263 | 295 | // (stores of v1 and v2 are dead; store of v3 remains because it |
| 264 | 296 | // might be read in a successor — we don't do cross-block analysis) |
| 265 | 297 | assert!(Dse.run(&mut m)); |
| 266 | - let stores: Vec<_> = m.functions[0].blocks[0].insts.iter() | |
| 298 | + let stores: Vec<_> = m.functions[0].blocks[0] | |
| 299 | + .insts | |
| 300 | + .iter() | |
| 267 | 301 | .filter(|i| matches!(i.kind, InstKind::Store(..))) |
| 268 | 302 | .collect(); |
| 269 | 303 | // Two dead stores removed, one survives. |
src/opt/fast_math.rsmodified@@ -24,7 +24,9 @@ use super::util::substitute_uses; | ||
| 24 | 24 | pub struct FastMathReassoc; |
| 25 | 25 | |
| 26 | 26 | impl Pass for FastMathReassoc { |
| 27 | - fn name(&self) -> &'static str { "fast-math-reassoc" } | |
| 27 | + fn name(&self) -> &'static str { | |
| 28 | + "fast-math-reassoc" | |
| 29 | + } | |
| 28 | 30 | |
| 29 | 31 | fn run(&self, module: &mut Module) -> bool { |
| 30 | 32 | let mut changed = false; |
@@ -83,7 +85,10 @@ struct SignedFloatConst { | ||
| 83 | 85 | |
| 84 | 86 | impl SignedFloatConst { |
| 85 | 87 | fn positive(constant: FloatConst) -> Self { |
| 86 | - Self { constant, negative: false } | |
| 88 | + Self { | |
| 89 | + constant, | |
| 90 | + negative: false, | |
| 91 | + } | |
| 87 | 92 | } |
| 88 | 93 | |
| 89 | 94 | fn negated(self) -> Self { |
@@ -221,13 +226,13 @@ fn collect_chain( | ||
| 221 | 226 | }); |
| 222 | 227 | } |
| 223 | 228 | |
| 224 | - let Some(inst) = defs.get(&value) else { | |
| 225 | - return Some(AddChain { | |
| 226 | - base: Some(value), | |
| 227 | - terms: Vec::new(), | |
| 228 | - additive_nodes: 0, | |
| 229 | - }); | |
| 230 | - }; | |
| 229 | + let Some(inst) = defs.get(&value) else { | |
| 230 | + return Some(AddChain { | |
| 231 | + base: Some(value), | |
| 232 | + terms: Vec::new(), | |
| 233 | + additive_nodes: 0, | |
| 234 | + }); | |
| 235 | + }; | |
| 231 | 236 | match inst.kind { |
| 232 | 237 | InstKind::FAdd(lhs, rhs) if inst.ty == IrType::Float(width) => { |
| 233 | 238 | let lhs_chain = collect_chain(defs, lhs, width)?; |
@@ -278,7 +283,9 @@ fn const_float_of( | ||
| 278 | 283 | ) -> Option<FloatConst> { |
| 279 | 284 | let inst = defs.get(&value)?; |
| 280 | 285 | match inst.kind { |
| 281 | - InstKind::ConstFloat(value, inst_width) if inst_width == width => FloatConst::from_value(value, width), | |
| 286 | + InstKind::ConstFloat(value, inst_width) if inst_width == width => { | |
| 287 | + FloatConst::from_value(value, width) | |
| 288 | + } | |
| 282 | 289 | _ => None, |
| 283 | 290 | } |
| 284 | 291 | } |
@@ -359,13 +366,22 @@ mod tests { | ||
| 359 | 366 | |
| 360 | 367 | fn span() -> Span { |
| 361 | 368 | let pos = Position { line: 0, col: 0 }; |
| 362 | - Span { file_id: 0, start: pos, end: pos } | |
| 369 | + Span { | |
| 370 | + file_id: 0, | |
| 371 | + start: pos, | |
| 372 | + end: pos, | |
| 373 | + } | |
| 363 | 374 | } |
| 364 | 375 | |
| 365 | 376 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 366 | 377 | let id = f.next_value_id(); |
| 367 | 378 | let entry = f.entry; |
| 368 | - f.block_mut(entry).insts.push(Inst { id, kind, ty: ty.clone(), span: span() }); | |
| 379 | + f.block_mut(entry).insts.push(Inst { | |
| 380 | + id, | |
| 381 | + kind, | |
| 382 | + ty: ty.clone(), | |
| 383 | + span: span(), | |
| 384 | + }); | |
| 369 | 385 | f.register_type(id, ty); |
| 370 | 386 | id |
| 371 | 387 | } |
@@ -408,7 +424,9 @@ mod tests { | ||
| 408 | 424 | let func = &module.functions[0]; |
| 409 | 425 | let insts = &func.blocks[0].insts; |
| 410 | 426 | assert!( |
| 411 | - insts.iter().any(|inst| matches!(inst.kind, InstKind::ConstFloat(v, FloatWidth::F64) if v == 5.0)), | |
| 427 | + insts.iter().any( | |
| 428 | + |inst| matches!(inst.kind, InstKind::ConstFloat(v, FloatWidth::F64) if v == 5.0) | |
| 429 | + ), | |
| 412 | 430 | "reassociated chain should materialize a combined constant:\n{:?}", |
| 413 | 431 | insts |
| 414 | 432 | ); |
@@ -416,7 +434,10 @@ mod tests { | ||
| 416 | 434 | matches!(func.blocks[0].terminator, Some(Terminator::Return(Some(v))) if v == add2), |
| 417 | 435 | "outer value should stay the return root" |
| 418 | 436 | ); |
| 419 | - let outer = insts.iter().find(|inst| inst.id == add2).expect("outer add should remain"); | |
| 437 | + let outer = insts | |
| 438 | + .iter() | |
| 439 | + .find(|inst| inst.id == add2) | |
| 440 | + .expect("outer add should remain"); | |
| 420 | 441 | assert!( |
| 421 | 442 | matches!(outer.kind, InstKind::FAdd(ValueId(0), _)), |
| 422 | 443 | "outer add should become x + const, got {:?}", |
src/opt/fission.rsmodified@@ -10,24 +10,28 @@ | ||
| 10 | 10 | //! |
| 11 | 11 | //! This avoids SSA domination bugs from selective instruction removal. |
| 12 | 12 | |
| 13 | -use std::collections::HashSet; | |
| 14 | -use crate::ir::inst::*; | |
| 15 | -use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 16 | -use super::loop_utils::{find_preheader, clone_loop}; | |
| 17 | 13 | use super::dep_analysis::{collect_mem_refs, test_dependence}; |
| 14 | +use super::loop_utils::{clone_loop, find_preheader}; | |
| 18 | 15 | use super::pass::Pass; |
| 16 | +use crate::ir::inst::*; | |
| 17 | +use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 18 | +use std::collections::HashSet; | |
| 19 | 19 | |
| 20 | 20 | const FISSION_MIN_BODY: usize = 4; |
| 21 | 21 | |
| 22 | 22 | pub struct LoopFission; |
| 23 | 23 | |
| 24 | 24 | impl Pass for LoopFission { |
| 25 | - fn name(&self) -> &'static str { "loop-fission" } | |
| 25 | + fn name(&self) -> &'static str { | |
| 26 | + "loop-fission" | |
| 27 | + } | |
| 26 | 28 | |
| 27 | 29 | fn run(&self, module: &mut Module) -> bool { |
| 28 | 30 | let mut changed = false; |
| 29 | 31 | for func in &mut module.functions { |
| 30 | - if fission_in_function(func) { changed = true; } | |
| 32 | + if fission_in_function(func) { | |
| 33 | + changed = true; | |
| 34 | + } | |
| 31 | 35 | } |
| 32 | 36 | changed |
| 33 | 37 | } |
@@ -38,12 +42,18 @@ fn fission_in_function(func: &mut Function) -> bool { | ||
| 38 | 42 | let preds = predecessors(func); |
| 39 | 43 | |
| 40 | 44 | for lp in &loops { |
| 41 | - let Some(_ph_id) = find_preheader(func, lp, &preds) else { continue }; | |
| 42 | - if lp.latches.len() != 1 { continue; } | |
| 45 | + let Some(_ph_id) = find_preheader(func, lp, &preds) else { | |
| 46 | + continue; | |
| 47 | + }; | |
| 48 | + if lp.latches.len() != 1 { | |
| 49 | + continue; | |
| 50 | + } | |
| 43 | 51 | let latch_id = lp.latches[0]; |
| 44 | 52 | |
| 45 | 53 | let hdr = func.block(lp.header); |
| 46 | - if hdr.params.len() != 1 { continue; } | |
| 54 | + if hdr.params.len() != 1 { | |
| 55 | + continue; | |
| 56 | + } | |
| 47 | 57 | let iv = hdr.params[0].id; |
| 48 | 58 | |
| 49 | 59 | // Find the single computation body block. |
@@ -51,34 +61,56 @@ fn fission_in_function(func: &mut Function) -> bool { | ||
| 51 | 61 | let Some(body_bid) = body_block else { continue }; |
| 52 | 62 | |
| 53 | 63 | let block = func.block(body_bid); |
| 54 | - if block.insts.len() < FISSION_MIN_BODY { continue; } | |
| 64 | + if block.insts.len() < FISSION_MIN_BODY { | |
| 65 | + continue; | |
| 66 | + } | |
| 55 | 67 | |
| 56 | 68 | // Need exactly 2 stores to different arrays. |
| 57 | - let stores: Vec<(usize, ValueId)> = block.insts.iter().enumerate() | |
| 69 | + let stores: Vec<(usize, ValueId)> = block | |
| 70 | + .insts | |
| 71 | + .iter() | |
| 72 | + .enumerate() | |
| 58 | 73 | .filter(|(_, i)| matches!(i.kind, InstKind::Store(..))) |
| 59 | 74 | .map(|(idx, i)| (idx, i.id)) |
| 60 | 75 | .collect(); |
| 61 | - if stores.len() != 2 { continue; } | |
| 76 | + if stores.len() != 2 { | |
| 77 | + continue; | |
| 78 | + } | |
| 62 | 79 | |
| 63 | 80 | // Check independence via dep analysis. |
| 64 | 81 | let mut ivs = HashSet::new(); |
| 65 | 82 | ivs.insert(iv); |
| 66 | 83 | let mem_refs = collect_mem_refs(func, &lp.body, &ivs); |
| 67 | 84 | let writes: Vec<_> = mem_refs.iter().filter(|r| r.is_write).collect(); |
| 68 | - if writes.len() != 2 { continue; } | |
| 69 | - if writes[0].base == writes[1].base { continue; } | |
| 85 | + if writes.len() != 2 { | |
| 86 | + continue; | |
| 87 | + } | |
| 88 | + if writes[0].base == writes[1].base { | |
| 89 | + continue; | |
| 90 | + } | |
| 70 | 91 | |
| 71 | 92 | let mut has_cross_dep = false; |
| 72 | 93 | for i in 0..mem_refs.len() { |
| 73 | - for j in (i+1)..mem_refs.len() { | |
| 74 | - if !mem_refs[i].is_write && !mem_refs[j].is_write { continue; } | |
| 75 | - if mem_refs[i].base == mem_refs[j].base { continue; } | |
| 94 | + for j in (i + 1)..mem_refs.len() { | |
| 95 | + if !mem_refs[i].is_write && !mem_refs[j].is_write { | |
| 96 | + continue; | |
| 97 | + } | |
| 98 | + if mem_refs[i].base == mem_refs[j].base { | |
| 99 | + continue; | |
| 100 | + } | |
| 76 | 101 | let dep = test_dependence(&mem_refs[i], &mem_refs[j]); |
| 77 | - if dep.dependent { has_cross_dep = true; break; } | |
| 102 | + if dep.dependent { | |
| 103 | + has_cross_dep = true; | |
| 104 | + break; | |
| 105 | + } | |
| 106 | + } | |
| 107 | + if has_cross_dep { | |
| 108 | + break; | |
| 78 | 109 | } |
| 79 | - if has_cross_dep { break; } | |
| 80 | 110 | } |
| 81 | - if has_cross_dep { continue; } | |
| 111 | + if has_cross_dep { | |
| 112 | + continue; | |
| 113 | + } | |
| 82 | 114 | |
| 83 | 115 | // Find the exit block. |
| 84 | 116 | let exit_id = find_loop_exit(func, lp); |
@@ -104,8 +136,7 @@ fn fission_in_function(func: &mut Function) -> bool { | ||
| 104 | 136 | }; |
| 105 | 137 | |
| 106 | 138 | let bridge = func.create_block("fission_bridge"); |
| 107 | - func.block_mut(bridge).terminator = | |
| 108 | - Some(Terminator::Branch(clone_header, vec![init_val])); | |
| 139 | + func.block_mut(bridge).terminator = Some(Terminator::Branch(clone_header, vec![init_val])); | |
| 109 | 140 | |
| 110 | 141 | // Redirect original cmp's exit → bridge. |
| 111 | 142 | for &bid in &lp.body { |
@@ -130,7 +161,9 @@ fn fission_in_function(func: &mut Function) -> bool { | ||
| 130 | 161 | /// making it a dead store that DSE/DCE can clean up. |
| 131 | 162 | fn neutralize_store(func: &mut Function, block_id: BlockId, store_idx: usize) { |
| 132 | 163 | let block = func.block_mut(block_id); |
| 133 | - if store_idx >= block.insts.len() { return; } | |
| 164 | + if store_idx >= block.insts.len() { | |
| 165 | + return; | |
| 166 | + } | |
| 134 | 167 | if let InstKind::Store(_, _) = block.insts[store_idx].kind { |
| 135 | 168 | // Replace with a store of undef — the store is now dead. |
| 136 | 169 | // We keep the store instruction (not remove it) to preserve |
@@ -152,21 +185,35 @@ fn find_computation_block( | ||
| 152 | 185 | ) -> Option<BlockId> { |
| 153 | 186 | let mut comp = None; |
| 154 | 187 | for &bid in &lp.body { |
| 155 | - if bid == lp.header || bid == latch_id { continue; } | |
| 188 | + if bid == lp.header || bid == latch_id { | |
| 189 | + continue; | |
| 190 | + } | |
| 156 | 191 | let block = func.block(bid); |
| 157 | - if block.insts.iter().any(|i| matches!(i.kind, InstKind::Store(..))) { | |
| 158 | - if comp.is_some() { return None; } | |
| 192 | + if block | |
| 193 | + .insts | |
| 194 | + .iter() | |
| 195 | + .any(|i| matches!(i.kind, InstKind::Store(..))) | |
| 196 | + { | |
| 197 | + if comp.is_some() { | |
| 198 | + return None; | |
| 199 | + } | |
| 159 | 200 | comp = Some(bid); |
| 160 | 201 | } |
| 161 | 202 | } |
| 162 | 203 | comp |
| 163 | 204 | } |
| 164 | 205 | |
| 165 | -fn backward_slice(func: &Function, root: ValueId, loop_defs: &HashSet<ValueId>) -> HashSet<ValueId> { | |
| 206 | +fn backward_slice( | |
| 207 | + func: &Function, | |
| 208 | + root: ValueId, | |
| 209 | + loop_defs: &HashSet<ValueId>, | |
| 210 | +) -> HashSet<ValueId> { | |
| 166 | 211 | let mut slice = HashSet::new(); |
| 167 | 212 | let mut worklist = vec![root]; |
| 168 | 213 | while let Some(vid) = worklist.pop() { |
| 169 | - if !slice.insert(vid) { continue; } | |
| 214 | + if !slice.insert(vid) { | |
| 215 | + continue; | |
| 216 | + } | |
| 170 | 217 | for block in &func.blocks { |
| 171 | 218 | for inst in &block.insts { |
| 172 | 219 | if inst.id == vid { |
@@ -186,7 +233,9 @@ fn find_loop_exit(func: &Function, lp: &crate::ir::walk::NaturalLoop) -> Option< | ||
| 186 | 233 | for &bid in &lp.body { |
| 187 | 234 | let block = func.block(bid); |
| 188 | 235 | if let Some(Terminator::CondBranch { false_dest, .. }) = &block.terminator { |
| 189 | - if !lp.body.contains(false_dest) { return Some(*false_dest); } | |
| 236 | + if !lp.body.contains(false_dest) { | |
| 237 | + return Some(*false_dest); | |
| 238 | + } | |
| 190 | 239 | } |
| 191 | 240 | } |
| 192 | 241 | None |
src/opt/fusion.rsmodified@@ -9,23 +9,27 @@ | ||
| 9 | 9 | //! |
| 10 | 10 | //! This avoids splicing instructions between blocks entirely. |
| 11 | 11 | |
| 12 | -use std::collections::HashSet; | |
| 13 | -use crate::ir::inst::*; | |
| 14 | -use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 12 | +use super::dep_analysis; | |
| 15 | 13 | use super::loop_utils::remap_inst_kind; |
| 16 | 14 | use super::loop_utils::{find_preheader, resolve_const_int}; |
| 17 | -use super::dep_analysis; | |
| 18 | 15 | use super::pass::Pass; |
| 16 | +use crate::ir::inst::*; | |
| 17 | +use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 18 | +use std::collections::HashSet; | |
| 19 | 19 | |
| 20 | 20 | pub struct LoopFusion; |
| 21 | 21 | |
| 22 | 22 | impl Pass for LoopFusion { |
| 23 | - fn name(&self) -> &'static str { "loop-fusion" } | |
| 23 | + fn name(&self) -> &'static str { | |
| 24 | + "loop-fusion" | |
| 25 | + } | |
| 24 | 26 | |
| 25 | 27 | fn run(&self, module: &mut Module) -> bool { |
| 26 | 28 | let mut changed = false; |
| 27 | 29 | for func in &mut module.functions { |
| 28 | - if fusion_in_function(func) { changed = true; } | |
| 30 | + if fusion_in_function(func) { | |
| 31 | + changed = true; | |
| 32 | + } | |
| 29 | 33 | } |
| 30 | 34 | changed |
| 31 | 35 | } |
@@ -33,63 +37,99 @@ impl Pass for LoopFusion { | ||
| 33 | 37 | |
| 34 | 38 | fn fusion_in_function(func: &mut Function) -> bool { |
| 35 | 39 | let loops = find_natural_loops(func); |
| 36 | - if loops.len() < 2 { return false; } | |
| 40 | + if loops.len() < 2 { | |
| 41 | + return false; | |
| 42 | + } | |
| 37 | 43 | let preds = predecessors(func); |
| 38 | 44 | |
| 39 | 45 | for i in 0..loops.len() { |
| 40 | - for j in (i+1)..loops.len() { | |
| 46 | + for j in (i + 1)..loops.len() { | |
| 41 | 47 | let lp_a = &loops[i]; |
| 42 | 48 | let lp_b = &loops[j]; |
| 43 | 49 | |
| 44 | 50 | // Both need preheaders and single latches. |
| 45 | - let Some(_ph_a) = find_preheader(func, lp_a, &preds) else { continue }; | |
| 46 | - let Some(ph_b) = find_preheader(func, lp_b, &preds) else { continue }; | |
| 47 | - if lp_a.latches.len() != 1 || lp_b.latches.len() != 1 { continue; } | |
| 51 | + let Some(_ph_a) = find_preheader(func, lp_a, &preds) else { | |
| 52 | + continue; | |
| 53 | + }; | |
| 54 | + let Some(ph_b) = find_preheader(func, lp_b, &preds) else { | |
| 55 | + continue; | |
| 56 | + }; | |
| 57 | + if lp_a.latches.len() != 1 || lp_b.latches.len() != 1 { | |
| 58 | + continue; | |
| 59 | + } | |
| 48 | 60 | let latch_a = lp_a.latches[0]; |
| 49 | 61 | let latch_b = lp_b.latches[0]; |
| 50 | 62 | |
| 51 | 63 | // Both headers must have exactly 1 block param (IV). |
| 52 | 64 | let hdr_a = func.block(lp_a.header); |
| 53 | 65 | let hdr_b = func.block(lp_b.header); |
| 54 | - if hdr_a.params.len() != 1 || hdr_b.params.len() != 1 { continue; } | |
| 66 | + if hdr_a.params.len() != 1 || hdr_b.params.len() != 1 { | |
| 67 | + continue; | |
| 68 | + } | |
| 55 | 69 | let iv_a = hdr_a.params[0].id; |
| 56 | 70 | let iv_b = hdr_b.params[0].id; |
| 57 | 71 | |
| 58 | 72 | // Neither loop should be nested inside the other. |
| 59 | - if lp_a.body.iter().any(|b| lp_b.body.contains(b)) { continue; } | |
| 60 | - if lp_b.body.iter().any(|b| lp_a.body.contains(b)) { continue; } | |
| 73 | + if lp_a.body.iter().any(|b| lp_b.body.contains(b)) { | |
| 74 | + continue; | |
| 75 | + } | |
| 76 | + if lp_b.body.iter().any(|b| lp_a.body.contains(b)) { | |
| 77 | + continue; | |
| 78 | + } | |
| 61 | 79 | |
| 62 | 80 | // Neither loop should contain inner loops. Fusing loops |
| 63 | 81 | // with nested inner loops requires more complex handling |
| 64 | 82 | // (inner loop blocks need reparenting). V1: simple only. |
| 65 | - let has_inner_a = loops.iter().any(|other| | |
| 66 | - other.header != lp_a.header && lp_a.body.is_superset(&other.body)); | |
| 67 | - let has_inner_b = loops.iter().any(|other| | |
| 68 | - other.header != lp_b.header && lp_b.body.is_superset(&other.body)); | |
| 69 | - if has_inner_a || has_inner_b { continue; } | |
| 83 | + let has_inner_a = loops | |
| 84 | + .iter() | |
| 85 | + .any(|other| other.header != lp_a.header && lp_a.body.is_superset(&other.body)); | |
| 86 | + let has_inner_b = loops | |
| 87 | + .iter() | |
| 88 | + .any(|other| other.header != lp_b.header && lp_b.body.is_superset(&other.body)); | |
| 89 | + if has_inner_a || has_inner_b { | |
| 90 | + continue; | |
| 91 | + } | |
| 70 | 92 | |
| 71 | 93 | // Skip loops produced by fission in the same pipeline |
| 72 | 94 | // iteration — they have clone/bridge blocks that fusion |
| 73 | 95 | // can't handle safely. |
| 74 | - let has_clone_a = lp_a.body.iter().any(|&b| | |
| 75 | - func.block(b).name.contains("clone") || func.block(b).name.contains("fission")); | |
| 76 | - let has_clone_b = lp_b.body.iter().any(|&b| | |
| 77 | - func.block(b).name.contains("clone") || func.block(b).name.contains("fission")); | |
| 78 | - if has_clone_a || has_clone_b { continue; } | |
| 96 | + let has_clone_a = lp_a.body.iter().any(|&b| { | |
| 97 | + func.block(b).name.contains("clone") || func.block(b).name.contains("fission") | |
| 98 | + }); | |
| 99 | + let has_clone_b = lp_b.body.iter().any(|&b| { | |
| 100 | + func.block(b).name.contains("clone") || func.block(b).name.contains("fission") | |
| 101 | + }); | |
| 102 | + if has_clone_a || has_clone_b { | |
| 103 | + continue; | |
| 104 | + } | |
| 79 | 105 | |
| 80 | 106 | // Loop A's exit must be (or flow to) loop B's preheader. |
| 81 | 107 | let exit_a = find_loop_exit(func, lp_a); |
| 82 | 108 | let Some(exit_a) = exit_a else { continue }; |
| 83 | - if exit_a != ph_b && !flows_to(func, exit_a, ph_b) { continue; } | |
| 109 | + if exit_a != ph_b && !flows_to(func, exit_a, ph_b) { | |
| 110 | + continue; | |
| 111 | + } | |
| 84 | 112 | |
| 85 | 113 | // Matching iteration spaces. |
| 86 | - let Some(init_a) = get_init_const(func, lp_a, &preds) else { continue }; | |
| 87 | - let Some(init_b) = get_init_const(func, lp_b, &preds) else { continue }; | |
| 88 | - if init_a != init_b { continue; } | |
| 114 | + let Some(init_a) = get_init_const(func, lp_a, &preds) else { | |
| 115 | + continue; | |
| 116 | + }; | |
| 117 | + let Some(init_b) = get_init_const(func, lp_b, &preds) else { | |
| 118 | + continue; | |
| 119 | + }; | |
| 120 | + if init_a != init_b { | |
| 121 | + continue; | |
| 122 | + } | |
| 89 | 123 | |
| 90 | - let Some(bound_a) = find_bound_const(func, lp_a, iv_a) else { continue }; | |
| 91 | - let Some(bound_b) = find_bound_const(func, lp_b, iv_b) else { continue }; | |
| 92 | - if bound_a != bound_b { continue; } | |
| 124 | + let Some(bound_a) = find_bound_const(func, lp_a, iv_a) else { | |
| 125 | + continue; | |
| 126 | + }; | |
| 127 | + let Some(bound_b) = find_bound_const(func, lp_b, iv_b) else { | |
| 128 | + continue; | |
| 129 | + }; | |
| 130 | + if bound_a != bound_b { | |
| 131 | + continue; | |
| 132 | + } | |
| 93 | 133 | |
| 94 | 134 | // Dep analysis: fusion legal? |
| 95 | 135 | if !dep_analysis::fusion_legal(func, &lp_a.body, &lp_b.body, iv_a, iv_b) { |
@@ -123,11 +163,7 @@ fn fusion_in_function(func: &mut Function) -> bool { | ||
| 123 | 163 | |
| 124 | 164 | // ---- Perform fusion via latch redirect ---- |
| 125 | 165 | do_fusion_latch_redirect( |
| 126 | - func, lp_a, lp_b, | |
| 127 | - latch_a, latch_b, | |
| 128 | - iv_a, iv_b, | |
| 129 | - body_b_id, cmp_b_id, | |
| 130 | - exit_a, exit_b, | |
| 166 | + func, lp_a, lp_b, latch_a, latch_b, iv_a, iv_b, body_b_id, cmp_b_id, exit_a, exit_b, | |
| 131 | 167 | ); |
| 132 | 168 | return true; |
| 133 | 169 | } |
@@ -158,10 +194,16 @@ fn do_fusion_latch_redirect( | ||
| 158 | 194 | sub_map.insert(iv_b, iv_a); |
| 159 | 195 | for &bid in &lp_b.body { |
| 160 | 196 | let old_insts: Vec<Inst> = func.block(bid).insts.clone(); |
| 161 | - let new_insts: Vec<Inst> = old_insts.into_iter().map(|inst| { | |
| 162 | - let new_kind = remap_inst_kind(&inst.kind, &sub_map); | |
| 163 | - Inst { kind: new_kind, ..inst } | |
| 164 | - }).collect(); | |
| 197 | + let new_insts: Vec<Inst> = old_insts | |
| 198 | + .into_iter() | |
| 199 | + .map(|inst| { | |
| 200 | + let new_kind = remap_inst_kind(&inst.kind, &sub_map); | |
| 201 | + Inst { | |
| 202 | + kind: new_kind, | |
| 203 | + ..inst | |
| 204 | + } | |
| 205 | + }) | |
| 206 | + .collect(); | |
| 165 | 207 | func.block_mut(bid).insts = new_insts; |
| 166 | 208 | // Also remap terminator operands. |
| 167 | 209 | let old_term = func.block(bid).terminator.clone(); |
@@ -210,7 +252,9 @@ fn do_fusion_latch_redirect( | ||
| 210 | 252 | |
| 211 | 253 | // Find the block that branches to latch_a (A's body exit). |
| 212 | 254 | let a_body_exit = find_branch_to(func, lp_a, latch_a); |
| 213 | - let Some(a_body_exit_id) = a_body_exit else { return }; | |
| 255 | + let Some(a_body_exit_id) = a_body_exit else { | |
| 256 | + return; | |
| 257 | + }; | |
| 214 | 258 | |
| 215 | 259 | // Redirect A's body exit → B's body. |
| 216 | 260 | redirect_branch(func, a_body_exit_id, latch_a, body_b_id); |
@@ -220,8 +264,7 @@ fn do_fusion_latch_redirect( | ||
| 220 | 264 | // But B's latch's iv_next_b should be discarded — A's latch will |
| 221 | 265 | // compute iv_next_a. So B's latch should just branch to A's latch |
| 222 | 266 | // with no args. |
| 223 | - func.block_mut(latch_b).terminator = | |
| 224 | - Some(Terminator::Branch(latch_a, vec![])); | |
| 267 | + func.block_mut(latch_b).terminator = Some(Terminator::Branch(latch_a, vec![])); | |
| 225 | 268 | |
| 226 | 269 | // Step 3: A's cmp exit (exit_a) should now go to B's exit (exit_b) |
| 227 | 270 | // since B's header/cmp are bypassed. Copy B's exit block content |
@@ -235,7 +278,9 @@ fn do_fusion_latch_redirect( | ||
| 235 | 278 | func.block_mut(lp_b.header).terminator = Some(Terminator::Unreachable); |
| 236 | 279 | // Mark B's cmp blocks too. |
| 237 | 280 | for &bid in &lp_b.body { |
| 238 | - if bid == body_b_id || bid == latch_b { continue; } | |
| 281 | + if bid == body_b_id || bid == latch_b { | |
| 282 | + continue; | |
| 283 | + } | |
| 239 | 284 | func.block_mut(bid).terminator = Some(Terminator::Unreachable); |
| 240 | 285 | } |
| 241 | 286 | |
@@ -248,7 +293,9 @@ fn find_branch_to( | ||
| 248 | 293 | target: BlockId, |
| 249 | 294 | ) -> Option<BlockId> { |
| 250 | 295 | for &bid in &lp.body { |
| 251 | - if bid == target { continue; } | |
| 296 | + if bid == target { | |
| 297 | + continue; | |
| 298 | + } | |
| 252 | 299 | let block = func.block(bid); |
| 253 | 300 | match &block.terminator { |
| 254 | 301 | Some(Terminator::Branch(dest, _)) if *dest == target => return Some(bid), |
@@ -272,7 +319,9 @@ fn find_loop_exit(func: &Function, lp: &crate::ir::walk::NaturalLoop) -> Option< | ||
| 272 | 319 | for &bid in &lp.body { |
| 273 | 320 | let block = func.block(bid); |
| 274 | 321 | if let Some(Terminator::CondBranch { false_dest, .. }) = &block.terminator { |
| 275 | - if !lp.body.contains(false_dest) { return Some(*false_dest); } | |
| 322 | + if !lp.body.contains(false_dest) { | |
| 323 | + return Some(*false_dest); | |
| 324 | + } | |
| 276 | 325 | } |
| 277 | 326 | } |
| 278 | 327 | None |
@@ -282,7 +331,9 @@ fn flows_to(func: &Function, from: BlockId, to: BlockId) -> bool { | ||
| 282 | 331 | let block = func.block(from); |
| 283 | 332 | match &block.terminator { |
| 284 | 333 | Some(Terminator::Branch(dest, _)) => { |
| 285 | - if *dest == to { return true; } | |
| 334 | + if *dest == to { | |
| 335 | + return true; | |
| 336 | + } | |
| 286 | 337 | let mid = func.block(*dest); |
| 287 | 338 | if let Some(Terminator::Branch(dest2, _)) = &mid.terminator { |
| 288 | 339 | return *dest2 == to; |
@@ -306,12 +357,22 @@ fn get_init_const( | ||
| 306 | 357 | resolve_const_int(func, init_val) |
| 307 | 358 | } |
| 308 | 359 | |
| 309 | -fn find_bound_const(func: &Function, lp: &crate::ir::walk::NaturalLoop, iv: ValueId) -> Option<i64> { | |
| 360 | +fn find_bound_const( | |
| 361 | + func: &Function, | |
| 362 | + lp: &crate::ir::walk::NaturalLoop, | |
| 363 | + iv: ValueId, | |
| 364 | +) -> Option<i64> { | |
| 310 | 365 | for &bid in &lp.body { |
| 311 | 366 | let block = func.block(bid); |
| 312 | 367 | for inst in &block.insts { |
| 313 | 368 | if let InstKind::ICmp(_, a, b) = &inst.kind { |
| 314 | - let bound_val = if *a == iv { *b } else if *b == iv { *a } else { continue }; | |
| 369 | + let bound_val = if *a == iv { | |
| 370 | + *b | |
| 371 | + } else if *b == iv { | |
| 372 | + *a | |
| 373 | + } else { | |
| 374 | + continue; | |
| 375 | + }; | |
| 315 | 376 | return resolve_const_int(func, bound_val); |
| 316 | 377 | } |
| 317 | 378 | } |
@@ -325,9 +386,15 @@ fn find_body_block( | ||
| 325 | 386 | latch_id: BlockId, |
| 326 | 387 | ) -> Option<BlockId> { |
| 327 | 388 | for &bid in &lp.body { |
| 328 | - if bid == lp.header || bid == latch_id { continue; } | |
| 389 | + if bid == lp.header || bid == latch_id { | |
| 390 | + continue; | |
| 391 | + } | |
| 329 | 392 | let block = func.block(bid); |
| 330 | - if block.insts.iter().any(|i| matches!(i.kind, InstKind::Store(..))) { | |
| 393 | + if block | |
| 394 | + .insts | |
| 395 | + .iter() | |
| 396 | + .any(|i| matches!(i.kind, InstKind::Store(..))) | |
| 397 | + { | |
| 331 | 398 | return Some(bid); |
| 332 | 399 | } |
| 333 | 400 | } |
@@ -337,7 +404,11 @@ fn find_body_block( | ||
| 337 | 404 | fn find_cmp_block(func: &Function, lp: &crate::ir::walk::NaturalLoop) -> Option<BlockId> { |
| 338 | 405 | for &bid in &lp.body { |
| 339 | 406 | let block = func.block(bid); |
| 340 | - if block.insts.iter().any(|i| matches!(i.kind, InstKind::ICmp(..))) { | |
| 407 | + if block | |
| 408 | + .insts | |
| 409 | + .iter() | |
| 410 | + .any(|i| matches!(i.kind, InstKind::ICmp(..))) | |
| 411 | + { | |
| 341 | 412 | return Some(bid); |
| 342 | 413 | } |
| 343 | 414 | } |
src/opt/global_lsf.rsmodified@@ -12,22 +12,26 @@ | ||
| 12 | 12 | //! %r = load %ptr → %r = %val (forwarded) |
| 13 | 13 | //! ``` |
| 14 | 14 | |
| 15 | -use std::collections::{HashMap, HashSet, VecDeque}; | |
| 15 | +use super::alias::{self, AliasResult}; | |
| 16 | +use super::pass::Pass; | |
| 16 | 17 | use crate::ir::inst::*; |
| 17 | 18 | use crate::ir::types::IrType; |
| 18 | 19 | use crate::ir::walk::{compute_immediate_dominators, predecessors, terminator_targets}; |
| 19 | -use super::alias::{self, AliasResult}; | |
| 20 | -use super::pass::Pass; | |
| 20 | +use std::collections::{HashMap, HashSet, VecDeque}; | |
| 21 | 21 | |
| 22 | 22 | pub struct GlobalLsf; |
| 23 | 23 | |
| 24 | 24 | impl Pass for GlobalLsf { |
| 25 | - fn name(&self) -> &'static str { "global-lsf" } | |
| 25 | + fn name(&self) -> &'static str { | |
| 26 | + "global-lsf" | |
| 27 | + } | |
| 26 | 28 | |
| 27 | 29 | fn run(&self, module: &mut Module) -> bool { |
| 28 | 30 | let mut changed = false; |
| 29 | 31 | for func in &mut module.functions { |
| 30 | - if global_lsf_function(func) { changed = true; } | |
| 32 | + if global_lsf_function(func) { | |
| 33 | + changed = true; | |
| 34 | + } | |
| 31 | 35 | } |
| 32 | 36 | changed |
| 33 | 37 | } |
@@ -51,7 +55,9 @@ fn global_lsf_function(func: &mut Function) -> bool { | ||
| 51 | 55 | } |
| 52 | 56 | } |
| 53 | 57 | |
| 54 | - if replacements.is_empty() { return false; } | |
| 58 | + if replacements.is_empty() { | |
| 59 | + return false; | |
| 60 | + } | |
| 55 | 61 | |
| 56 | 62 | // Apply replacements. |
| 57 | 63 | for block in &mut func.blocks { |
@@ -59,11 +65,8 @@ fn global_lsf_function(func: &mut Function) -> bool { | ||
| 59 | 65 | inst.kind = super::loop_utils::remap_inst_kind(&inst.kind, &replacements); |
| 60 | 66 | } |
| 61 | 67 | if let Some(ref mut term) = block.terminator { |
| 62 | - let new_term = super::loop_utils::remap_terminator( | |
| 63 | - term, | |
| 64 | - &HashMap::new(), | |
| 65 | - &replacements, | |
| 66 | - ); | |
| 68 | + let new_term = | |
| 69 | + super::loop_utils::remap_terminator(term, &HashMap::new(), &replacements); | |
| 67 | 70 | *term = new_term; |
| 68 | 71 | } |
| 69 | 72 | } |
@@ -95,7 +98,9 @@ fn find_reaching_store( | ||
| 95 | 98 | |
| 96 | 99 | let mut current = load_block; |
| 97 | 100 | while let Some(&idom) = idoms.get(¤t) { |
| 98 | - if idom == current { break; } // entry | |
| 101 | + if idom == current { | |
| 102 | + break; | |
| 103 | + } // entry | |
| 99 | 104 | current = idom; |
| 100 | 105 | |
| 101 | 106 | if !paths_to_load_are_clean(func, preds, current, load_block, load_ptr) { |
@@ -186,9 +191,10 @@ fn update_memory_state( | ||
| 186 | 191 | .copied() |
| 187 | 192 | .filter(|arg| value_is_pointer(func, *arg)) |
| 188 | 193 | .collect(); |
| 189 | - if pointer_args.iter().any(|arg| { | |
| 190 | - alias::may_reach_through_call_arg(func, load_ptr, *arg) | |
| 191 | - }) { | |
| 194 | + if pointer_args | |
| 195 | + .iter() | |
| 196 | + .any(|arg| alias::may_reach_through_call_arg(func, load_ptr, *arg)) | |
| 197 | + { | |
| 192 | 198 | *last_store = None; |
| 193 | 199 | *clobbered = true; |
| 194 | 200 | } |
@@ -201,10 +207,17 @@ fn value_is_pointer(func: &Function, value: ValueId) -> bool { | ||
| 201 | 207 | if matches!(func.value_type(value), Some(IrType::Ptr(_))) { |
| 202 | 208 | return true; |
| 203 | 209 | } |
| 204 | - if func.params.iter().any(|param| param.id == value && matches!(param.ty, IrType::Ptr(_))) { | |
| 210 | + if func | |
| 211 | + .params | |
| 212 | + .iter() | |
| 213 | + .any(|param| param.id == value && matches!(param.ty, IrType::Ptr(_))) | |
| 214 | + { | |
| 205 | 215 | return true; |
| 206 | 216 | } |
| 207 | - func.blocks.iter().flat_map(|block| block.insts.iter()).find(|inst| inst.id == value) | |
| 217 | + func.blocks | |
| 218 | + .iter() | |
| 219 | + .flat_map(|block| block.insts.iter()) | |
| 220 | + .find(|inst| inst.id == value) | |
| 208 | 221 | .map(|inst| matches!(inst.ty, IrType::Ptr(_))) |
| 209 | 222 | .unwrap_or(false) |
| 210 | 223 | } |
@@ -316,7 +329,7 @@ fn reverse_reachable_blocks( | ||
| 316 | 329 | #[cfg(test)] |
| 317 | 330 | mod tests { |
| 318 | 331 | use super::*; |
| 319 | - use crate::ir::types::{IrType, IntWidth}; | |
| 332 | + use crate::ir::types::{IntWidth, IrType}; | |
| 320 | 333 | use crate::opt::pass::Pass; |
| 321 | 334 | |
| 322 | 335 | #[test] |
@@ -379,7 +392,10 @@ mod tests { | ||
| 379 | 392 | m.add_function(f); |
| 380 | 393 | |
| 381 | 394 | let pass = GlobalLsf; |
| 382 | - assert!(pass.run(&mut m), "straight-line dominated load should be forwarded"); | |
| 395 | + assert!( | |
| 396 | + pass.run(&mut m), | |
| 397 | + "straight-line dominated load should be forwarded" | |
| 398 | + ); | |
| 383 | 399 | let term = m.functions[0].block(load_block).terminator.clone().unwrap(); |
| 384 | 400 | assert!( |
| 385 | 401 | matches!(term, Terminator::Return(Some(v)) if v == value), |
src/opt/gvn.rsmodified@@ -8,16 +8,18 @@ | ||
| 8 | 8 | //! |
| 9 | 9 | //! Uses the same canonical Key structure as local CSE (cse.rs). |
| 10 | 10 | |
| 11 | -use std::collections::HashMap; | |
| 11 | +use super::pass::Pass; | |
| 12 | 12 | use crate::ir::inst::*; |
| 13 | 13 | use crate::ir::types::IrType; |
| 14 | 14 | use crate::ir::walk::{compute_immediate_dominators, dominator_tree_children}; |
| 15 | -use super::pass::Pass; | |
| 15 | +use std::collections::HashMap; | |
| 16 | 16 | |
| 17 | 17 | pub struct Gvn; |
| 18 | 18 | |
| 19 | 19 | impl Pass for Gvn { |
| 20 | - fn name(&self) -> &'static str { "gvn" } | |
| 20 | + fn name(&self) -> &'static str { | |
| 21 | + "gvn" | |
| 22 | + } | |
| 21 | 23 | |
| 22 | 24 | fn run(&self, module: &mut Module) -> bool { |
| 23 | 25 | let pure_calls: Vec<PureCallPolicy> = module |
@@ -27,7 +29,9 @@ impl Pass for Gvn { | ||
| 27 | 29 | .collect(); |
| 28 | 30 | let mut changed = false; |
| 29 | 31 | for func in &mut module.functions { |
| 30 | - if gvn_function(func, &pure_calls) { changed = true; } | |
| 32 | + if gvn_function(func, &pure_calls) { | |
| 33 | + changed = true; | |
| 34 | + } | |
| 31 | 35 | } |
| 32 | 36 | changed |
| 33 | 37 | } |
@@ -58,11 +62,17 @@ struct PureCallPolicy { | ||
| 58 | 62 | |
| 59 | 63 | impl PureCallPolicy { |
| 60 | 64 | fn for_function(func: &Function) -> Self { |
| 61 | - let arg_policies = func.params.iter().map(|param| classify_pure_arg(func, param)).collect::<Vec<_>>(); | |
| 65 | + let arg_policies = func | |
| 66 | + .params | |
| 67 | + .iter() | |
| 68 | + .map(|param| classify_pure_arg(func, param)) | |
| 69 | + .collect::<Vec<_>>(); | |
| 62 | 70 | let reusable = func.is_pure |
| 63 | 71 | && !matches!(func.return_type, IrType::Void) |
| 64 | 72 | && !func.return_type.is_ptr() |
| 65 | - && arg_policies.iter().all(|policy| *policy != PureArgPolicy::Unsupported) | |
| 73 | + && arg_policies | |
| 74 | + .iter() | |
| 75 | + .all(|policy| *policy != PureArgPolicy::Unsupported) | |
| 66 | 76 | && !reads_non_argument_memory(func); |
| 67 | 77 | Self { |
| 68 | 78 | reusable, |
@@ -255,10 +265,7 @@ fn wrapper_alloca_values( | ||
| 255 | 265 | } |
| 256 | 266 | let valid_arg = args.iter().enumerate().any(|(arg_idx, arg)| { |
| 257 | 267 | *arg == alloca_id |
| 258 | - && policy | |
| 259 | - .arg_policies | |
| 260 | - .get(arg_idx) | |
| 261 | - .copied() | |
| 268 | + && policy.arg_policies.get(arg_idx).copied() | |
| 262 | 269 | == Some(PureArgPolicy::ReadOnlyWrapperPtr) |
| 263 | 270 | }); |
| 264 | 271 | if !valid_arg { |
@@ -300,32 +307,48 @@ fn key_of( | ||
| 300 | 307 | wrapper_values: &HashMap<ValueId, ValueId>, |
| 301 | 308 | ) -> Option<Key> { |
| 302 | 309 | let mk = |tag: u32, ops: Vec<ValueId>, aux: i128| -> Option<Key> { |
| 303 | - Some(Key { tag, operands: ops, aux, name: None, ty: inst.ty.clone() }) | |
| 310 | + Some(Key { | |
| 311 | + tag, | |
| 312 | + operands: ops, | |
| 313 | + aux, | |
| 314 | + name: None, | |
| 315 | + ty: inst.ty.clone(), | |
| 316 | + }) | |
| 304 | 317 | }; |
| 305 | 318 | let mk_named = |tag: u32, name: String| -> Option<Key> { |
| 306 | - Some(Key { tag, operands: vec![], aux: 0, name: Some(name), ty: inst.ty.clone() }) | |
| 319 | + Some(Key { | |
| 320 | + tag, | |
| 321 | + operands: vec![], | |
| 322 | + aux: 0, | |
| 323 | + name: Some(name), | |
| 324 | + ty: inst.ty.clone(), | |
| 325 | + }) | |
| 307 | 326 | }; |
| 308 | 327 | let remap = |v: ValueId| resolve_value(replacements, v); |
| 309 | 328 | fn canon(a: ValueId, b: ValueId) -> Vec<ValueId> { |
| 310 | - if a.0 <= b.0 { vec![a, b] } else { vec![b, a] } | |
| 329 | + if a.0 <= b.0 { | |
| 330 | + vec![a, b] | |
| 331 | + } else { | |
| 332 | + vec![b, a] | |
| 333 | + } | |
| 311 | 334 | } |
| 312 | 335 | |
| 313 | 336 | match &inst.kind { |
| 314 | 337 | // Pure arithmetic — commutative ops get canonicalized operand order. |
| 315 | - InstKind::IAdd(a, b) => mk(1, canon(remap(*a), remap(*b)), 0), | |
| 316 | - InstKind::ISub(a, b) => mk(2, vec![remap(*a), remap(*b)], 0), | |
| 317 | - InstKind::IMul(a, b) => mk(3, canon(remap(*a), remap(*b)), 0), | |
| 318 | - InstKind::IDiv(a, b) => mk(4, vec![remap(*a), remap(*b)], 0), | |
| 319 | - InstKind::IMod(a, b) => mk(5, vec![remap(*a), remap(*b)], 0), | |
| 320 | - InstKind::INeg(a) => mk(6, vec![remap(*a)], 0), | |
| 321 | - InstKind::FAdd(a, b) => mk(10, canon(remap(*a), remap(*b)), 0), | |
| 322 | - InstKind::FSub(a, b) => mk(11, vec![remap(*a), remap(*b)], 0), | |
| 323 | - InstKind::FMul(a, b) => mk(12, canon(remap(*a), remap(*b)), 0), | |
| 324 | - InstKind::FDiv(a, b) => mk(13, vec![remap(*a), remap(*b)], 0), | |
| 325 | - InstKind::FNeg(a) => mk(14, vec![remap(*a)], 0), | |
| 326 | - InstKind::FAbs(a) => mk(15, vec![remap(*a)], 0), | |
| 327 | - InstKind::FSqrt(a) => mk(16, vec![remap(*a)], 0), | |
| 328 | - InstKind::FPow(a, b) => mk(17, vec![remap(*a), remap(*b)], 0), | |
| 338 | + InstKind::IAdd(a, b) => mk(1, canon(remap(*a), remap(*b)), 0), | |
| 339 | + InstKind::ISub(a, b) => mk(2, vec![remap(*a), remap(*b)], 0), | |
| 340 | + InstKind::IMul(a, b) => mk(3, canon(remap(*a), remap(*b)), 0), | |
| 341 | + InstKind::IDiv(a, b) => mk(4, vec![remap(*a), remap(*b)], 0), | |
| 342 | + InstKind::IMod(a, b) => mk(5, vec![remap(*a), remap(*b)], 0), | |
| 343 | + InstKind::INeg(a) => mk(6, vec![remap(*a)], 0), | |
| 344 | + InstKind::FAdd(a, b) => mk(10, canon(remap(*a), remap(*b)), 0), | |
| 345 | + InstKind::FSub(a, b) => mk(11, vec![remap(*a), remap(*b)], 0), | |
| 346 | + InstKind::FMul(a, b) => mk(12, canon(remap(*a), remap(*b)), 0), | |
| 347 | + InstKind::FDiv(a, b) => mk(13, vec![remap(*a), remap(*b)], 0), | |
| 348 | + InstKind::FNeg(a) => mk(14, vec![remap(*a)], 0), | |
| 349 | + InstKind::FAbs(a) => mk(15, vec![remap(*a)], 0), | |
| 350 | + InstKind::FSqrt(a) => mk(16, vec![remap(*a)], 0), | |
| 351 | + InstKind::FPow(a, b) => mk(17, vec![remap(*a), remap(*b)], 0), | |
| 329 | 352 | InstKind::ICmp(op, a, b) => { |
| 330 | 353 | let op_val = *op as i128; |
| 331 | 354 | match op { |
@@ -335,30 +358,34 @@ fn key_of( | ||
| 335 | 358 | } |
| 336 | 359 | InstKind::FCmp(op, a, b) => mk(21, vec![remap(*a), remap(*b)], *op as i128), |
| 337 | 360 | InstKind::And(a, b) => mk(30, canon(remap(*a), remap(*b)), 0), |
| 338 | - InstKind::Or(a, b) => mk(31, canon(remap(*a), remap(*b)), 0), | |
| 339 | - InstKind::Not(a) => mk(32, vec![remap(*a)], 0), | |
| 361 | + InstKind::Or(a, b) => mk(31, canon(remap(*a), remap(*b)), 0), | |
| 362 | + InstKind::Not(a) => mk(32, vec![remap(*a)], 0), | |
| 340 | 363 | InstKind::Select(c, t, f) => mk(33, vec![remap(*c), remap(*t), remap(*f)], 0), |
| 341 | - InstKind::BitAnd(a, b) => mk(40, canon(remap(*a), remap(*b)), 0), | |
| 342 | - InstKind::BitOr(a, b) => mk(41, canon(remap(*a), remap(*b)), 0), | |
| 343 | - InstKind::BitXor(a, b) => mk(42, canon(remap(*a), remap(*b)), 0), | |
| 344 | - InstKind::BitNot(a) => mk(43, vec![remap(*a)], 0), | |
| 345 | - InstKind::Shl(a, b) => mk(44, vec![remap(*a), remap(*b)], 0), | |
| 346 | - InstKind::LShr(a, b) => mk(45, vec![remap(*a), remap(*b)], 0), | |
| 347 | - InstKind::AShr(a, b) => mk(46, vec![remap(*a), remap(*b)], 0), | |
| 348 | - InstKind::CountLeadingZeros(a) => mk(47, vec![remap(*a)], 0), | |
| 364 | + InstKind::BitAnd(a, b) => mk(40, canon(remap(*a), remap(*b)), 0), | |
| 365 | + InstKind::BitOr(a, b) => mk(41, canon(remap(*a), remap(*b)), 0), | |
| 366 | + InstKind::BitXor(a, b) => mk(42, canon(remap(*a), remap(*b)), 0), | |
| 367 | + InstKind::BitNot(a) => mk(43, vec![remap(*a)], 0), | |
| 368 | + InstKind::Shl(a, b) => mk(44, vec![remap(*a), remap(*b)], 0), | |
| 369 | + InstKind::LShr(a, b) => mk(45, vec![remap(*a), remap(*b)], 0), | |
| 370 | + InstKind::AShr(a, b) => mk(46, vec![remap(*a), remap(*b)], 0), | |
| 371 | + InstKind::CountLeadingZeros(a) => mk(47, vec![remap(*a)], 0), | |
| 349 | 372 | InstKind::CountTrailingZeros(a) => mk(48, vec![remap(*a)], 0), |
| 350 | - InstKind::PopCount(a) => mk(49, vec![remap(*a)], 0), | |
| 373 | + InstKind::PopCount(a) => mk(49, vec![remap(*a)], 0), | |
| 351 | 374 | // Conversions. |
| 352 | - InstKind::IntToFloat(a, w) => mk(50, vec![remap(*a)], w.bits() as i128), | |
| 353 | - InstKind::FloatToInt(a, w) => mk(51, vec![remap(*a)], w.bits() as i128), | |
| 354 | - InstKind::FloatExtend(a, w) => mk(52, vec![remap(*a)], w.bits() as i128), | |
| 355 | - InstKind::FloatTrunc(a, w) => mk(53, vec![remap(*a)], w.bits() as i128), | |
| 356 | - InstKind::IntExtend(a, w, s) => mk(54, vec![remap(*a)], (w.bits() as i128) * if *s { 1 } else { -1 }), | |
| 357 | - InstKind::IntTrunc(a, w) => mk(55, vec![remap(*a)], w.bits() as i128), | |
| 358 | - InstKind::PtrToInt(a) => mk(56, vec![remap(*a)], 0), | |
| 359 | - InstKind::IntToPtr(a, _) => mk(57, vec![remap(*a)], 0), | |
| 375 | + InstKind::IntToFloat(a, w) => mk(50, vec![remap(*a)], w.bits() as i128), | |
| 376 | + InstKind::FloatToInt(a, w) => mk(51, vec![remap(*a)], w.bits() as i128), | |
| 377 | + InstKind::FloatExtend(a, w) => mk(52, vec![remap(*a)], w.bits() as i128), | |
| 378 | + InstKind::FloatTrunc(a, w) => mk(53, vec![remap(*a)], w.bits() as i128), | |
| 379 | + InstKind::IntExtend(a, w, s) => mk( | |
| 380 | + 54, | |
| 381 | + vec![remap(*a)], | |
| 382 | + (w.bits() as i128) * if *s { 1 } else { -1 }, | |
| 383 | + ), | |
| 384 | + InstKind::IntTrunc(a, w) => mk(55, vec![remap(*a)], w.bits() as i128), | |
| 385 | + InstKind::PtrToInt(a) => mk(56, vec![remap(*a)], 0), | |
| 386 | + InstKind::IntToPtr(a, _) => mk(57, vec![remap(*a)], 0), | |
| 360 | 387 | // Constants. |
| 361 | - InstKind::ConstInt(v, w) => { | |
| 388 | + InstKind::ConstInt(v, w) => { | |
| 362 | 389 | let bits = w.bits(); |
| 363 | 390 | let signed = if bits >= 128 { |
| 364 | 391 | *v |
@@ -369,7 +396,7 @@ fn key_of( | ||
| 369 | 396 | mk(60, vec![], signed) |
| 370 | 397 | } |
| 371 | 398 | InstKind::ConstFloat(v, w) => mk(61, vec![], ((*v).to_bits() as i128) ^ (w.bits() as i128)), |
| 372 | - InstKind::ConstBool(v) => mk(62, vec![], *v as i128), | |
| 399 | + InstKind::ConstBool(v) => mk(62, vec![], *v as i128), | |
| 373 | 400 | // GlobalAddr. |
| 374 | 401 | InstKind::GlobalAddr(name) => mk_named(70, name.clone()), |
| 375 | 402 | // GEP. |
@@ -403,10 +430,15 @@ fn key_of( | ||
| 403 | 430 | mk(90, ops, *idx as i128) |
| 404 | 431 | } |
| 405 | 432 | // Impure: loads, stores, runtime calls, external calls, alloca — not GVN candidates. |
| 406 | - InstKind::Load(..) | InstKind::Store(..) | InstKind::Alloca(..) | |
| 407 | - | InstKind::Call(..) | InstKind::RuntimeCall(..) | |
| 408 | - | InstKind::ConstString(..) | InstKind::Undef(..) | |
| 409 | - | InstKind::ExtractField(..) | InstKind::InsertField(..) => None, | |
| 433 | + InstKind::Load(..) | |
| 434 | + | InstKind::Store(..) | |
| 435 | + | InstKind::Alloca(..) | |
| 436 | + | InstKind::Call(..) | |
| 437 | + | InstKind::RuntimeCall(..) | |
| 438 | + | InstKind::ConstString(..) | |
| 439 | + | InstKind::Undef(..) | |
| 440 | + | InstKind::ExtractField(..) | |
| 441 | + | InstKind::InsertField(..) => None, | |
| 410 | 442 | } |
| 411 | 443 | } |
| 412 | 444 | |
@@ -460,7 +492,9 @@ fn gvn_function(func: &mut Function, pure_calls: &[PureCallPolicy]) -> bool { | ||
| 460 | 492 | } |
| 461 | 493 | } |
| 462 | 494 | |
| 463 | - if replacements.is_empty() { return false; } | |
| 495 | + if replacements.is_empty() { | |
| 496 | + return false; | |
| 497 | + } | |
| 464 | 498 | |
| 465 | 499 | // Apply replacements: substitute all uses of redundant values, then |
| 466 | 500 | // drop the now-dead duplicate instructions directly. This matters |
@@ -470,7 +504,9 @@ fn gvn_function(func: &mut Function, pure_calls: &[PureCallPolicy]) -> bool { | ||
| 470 | 504 | for inst in &mut block.insts { |
| 471 | 505 | inst.kind = remap_operands(&inst.kind, &replacements); |
| 472 | 506 | } |
| 473 | - block.insts.retain(|inst| !replacements.contains_key(&inst.id)); | |
| 507 | + block | |
| 508 | + .insts | |
| 509 | + .retain(|inst| !replacements.contains_key(&inst.id)); | |
| 474 | 510 | if let Some(ref mut term) = block.terminator { |
| 475 | 511 | remap_terminator_operands(term, &replacements); |
| 476 | 512 | } |
@@ -490,12 +526,23 @@ fn remap_terminator_operands(term: &mut Terminator, map: &HashMap<ValueId, Value | ||
| 490 | 526 | match term { |
| 491 | 527 | Terminator::Return(Some(v)) => *v = r(v), |
| 492 | 528 | Terminator::Branch(_, args) => { |
| 493 | - for a in args.iter_mut() { *a = r(a); } | |
| 529 | + for a in args.iter_mut() { | |
| 530 | + *a = r(a); | |
| 531 | + } | |
| 494 | 532 | } |
| 495 | - Terminator::CondBranch { cond, true_args, false_args, .. } => { | |
| 533 | + Terminator::CondBranch { | |
| 534 | + cond, | |
| 535 | + true_args, | |
| 536 | + false_args, | |
| 537 | + .. | |
| 538 | + } => { | |
| 496 | 539 | *cond = r(cond); |
| 497 | - for a in true_args.iter_mut() { *a = r(a); } | |
| 498 | - for a in false_args.iter_mut() { *a = r(a); } | |
| 540 | + for a in true_args.iter_mut() { | |
| 541 | + *a = r(a); | |
| 542 | + } | |
| 543 | + for a in false_args.iter_mut() { | |
| 544 | + *a = r(a); | |
| 545 | + } | |
| 499 | 546 | } |
| 500 | 547 | Terminator::Switch { selector, .. } => { |
| 501 | 548 | *selector = r(selector); |
@@ -507,36 +554,45 @@ fn remap_terminator_operands(term: &mut Terminator, map: &HashMap<ValueId, Value | ||
| 507 | 554 | #[cfg(test)] |
| 508 | 555 | mod tests { |
| 509 | 556 | use super::*; |
| 510 | - use std::fs; | |
| 511 | - use std::path::PathBuf; | |
| 512 | - use crate::ir::types::{IrType, IntWidth}; | |
| 557 | + use crate::ir::lower; | |
| 558 | + use crate::ir::types::{IntWidth, IrType}; | |
| 559 | + use crate::lexer::{tokenize, SourceForm}; | |
| 560 | + use crate::lexer::{Position, Span}; | |
| 513 | 561 | use crate::opt::pass::Pass; |
| 514 | 562 | use crate::opt::pass::PassManager; |
| 563 | + use crate::opt::pipeline::OptLevel; | |
| 515 | 564 | use crate::opt::{ |
| 516 | 565 | bce::Bce, call_resolve::CallResolve, const_fold::ConstFold, const_prop::ConstProp, |
| 517 | - dead_func::DeadFuncElim, dse::Dse, fusion::LoopFusion, global_lsf::GlobalLsf, | |
| 518 | - interchange::LoopInterchange, inline::Inline, licm::Licm, lsf::LocalLsf, | |
| 519 | - mem2reg::Mem2Reg, peel::LoopPeel, preheader::PreheaderInsert, simplify_cfg::SimplifyCfg, | |
| 520 | - sroa::Sroa, strength_reduce::StrengthReduce, unroll::LoopUnroll, unswitch::LoopUnswitch, | |
| 521 | - fission::LoopFission, | |
| 566 | + dead_func::DeadFuncElim, dse::Dse, fission::LoopFission, fusion::LoopFusion, | |
| 567 | + global_lsf::GlobalLsf, inline::Inline, interchange::LoopInterchange, licm::Licm, | |
| 568 | + lsf::LocalLsf, mem2reg::Mem2Reg, peel::LoopPeel, preheader::PreheaderInsert, | |
| 569 | + simplify_cfg::SimplifyCfg, sroa::Sroa, strength_reduce::StrengthReduce, unroll::LoopUnroll, | |
| 570 | + unswitch::LoopUnswitch, | |
| 522 | 571 | }; |
| 523 | - use crate::opt::pipeline::OptLevel; | |
| 524 | - use crate::lexer::{Position, Span}; | |
| 525 | - use crate::lexer::{SourceForm, tokenize}; | |
| 526 | 572 | use crate::parser::Parser; |
| 527 | 573 | use crate::preprocess::PreprocConfig; |
| 528 | 574 | use crate::sema::{resolve, validate}; |
| 529 | - use crate::ir::lower; | |
| 575 | + use std::fs; | |
| 576 | + use std::path::PathBuf; | |
| 530 | 577 | |
| 531 | 578 | fn span() -> Span { |
| 532 | 579 | let pos = Position { line: 0, col: 0 }; |
| 533 | - Span { file_id: 0, start: pos, end: pos } | |
| 580 | + Span { | |
| 581 | + file_id: 0, | |
| 582 | + start: pos, | |
| 583 | + end: pos, | |
| 584 | + } | |
| 534 | 585 | } |
| 535 | 586 | |
| 536 | 587 | fn push_inst(func: &mut Function, block: BlockId, kind: InstKind, ty: IrType) -> ValueId { |
| 537 | 588 | let id = func.next_value_id(); |
| 538 | 589 | func.register_type(id, ty.clone()); |
| 539 | - func.block_mut(block).insts.push(Inst { id, kind, ty, span: span() }); | |
| 590 | + func.block_mut(block).insts.push(Inst { | |
| 591 | + id, | |
| 592 | + kind, | |
| 593 | + ty, | |
| 594 | + span: span(), | |
| 595 | + }); | |
| 540 | 596 | id |
| 541 | 597 | } |
| 542 | 598 | |
@@ -555,14 +611,26 @@ mod tests { | ||
| 555 | 611 | let tokens = tokenize(&pp.text, 0, SourceForm::FreeForm).expect("tokenize fixture"); |
| 556 | 612 | let mut parser = Parser::new(&tokens); |
| 557 | 613 | let units = parser.parse_file().expect("parse fixture"); |
| 558 | - let (st, type_layouts) = { let rr = resolve::resolve_file(&units, &[]).expect("resolve fixture"); (rr.st, rr.type_layouts) }; | |
| 614 | + let (st, type_layouts) = { | |
| 615 | + let rr = resolve::resolve_file(&units, &[]).expect("resolve fixture"); | |
| 616 | + (rr.st, rr.type_layouts) | |
| 617 | + }; | |
| 559 | 618 | let diags = validate::validate_file(&units, &st); |
| 560 | 619 | assert!( |
| 561 | - !diags.iter().any(|diag| diag.kind == validate::DiagKind::Error), | |
| 620 | + !diags | |
| 621 | + .iter() | |
| 622 | + .any(|diag| diag.kind == validate::DiagKind::Error), | |
| 562 | 623 | "fixture should lower cleanly: {:?}", |
| 563 | 624 | diags |
| 564 | 625 | ); |
| 565 | - lower::lower_file(&units, &st, &type_layouts, std::collections::HashMap::new(), std::collections::HashMap::new()).0 | |
| 626 | + lower::lower_file( | |
| 627 | + &units, | |
| 628 | + &st, | |
| 629 | + &type_layouts, | |
| 630 | + std::collections::HashMap::new(), | |
| 631 | + std::collections::HashMap::new(), | |
| 632 | + ) | |
| 633 | + .0 | |
| 566 | 634 | } |
| 567 | 635 | |
| 568 | 636 | fn build_pre_gvn_o2_pipeline() -> PassManager { |
@@ -758,9 +826,16 @@ mod tests { | ||
| 758 | 826 | .iter() |
| 759 | 827 | .filter(|inst| matches!(inst.kind, InstKind::Call(..))) |
| 760 | 828 | .count(); |
| 761 | - assert_eq!(call_count, 1, "PURE call should reuse equivalent dominating args"); | |
| 829 | + assert_eq!( | |
| 830 | + call_count, 1, | |
| 831 | + "PURE call should reuse equivalent dominating args" | |
| 832 | + ); | |
| 762 | 833 | |
| 763 | - match caller.blocks[0].terminator.as_ref().expect("return terminator") { | |
| 834 | + match caller.blocks[0] | |
| 835 | + .terminator | |
| 836 | + .as_ref() | |
| 837 | + .expect("return terminator") | |
| 838 | + { | |
| 764 | 839 | Terminator::Return(Some(v)) => assert_eq!(*v, call1), |
| 765 | 840 | other => panic!("unexpected terminator: {:?}", other), |
| 766 | 841 | } |
@@ -774,7 +849,8 @@ mod tests { | ||
| 774 | 849 | id: ValueId(0), |
| 775 | 850 | fortran_noalias: false, |
| 776 | 851 | }; |
| 777 | - let mut callee = Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 852 | + let mut callee = | |
| 853 | + Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 778 | 854 | callee.is_pure = true; |
| 779 | 855 | let entry = callee.entry; |
| 780 | 856 | |
@@ -784,7 +860,12 @@ mod tests { | ||
| 784 | 860 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 785 | 861 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 786 | 862 | ); |
| 787 | - push_inst(&mut callee, entry, InstKind::Store(ValueId(0), slot), IrType::Void); | |
| 863 | + push_inst( | |
| 864 | + &mut callee, | |
| 865 | + entry, | |
| 866 | + InstKind::Store(ValueId(0), slot), | |
| 867 | + IrType::Void, | |
| 868 | + ); | |
| 788 | 869 | let arg_ptr = push_inst( |
| 789 | 870 | &mut callee, |
| 790 | 871 | entry, |
@@ -814,7 +895,8 @@ mod tests { | ||
| 814 | 895 | id: ValueId(0), |
| 815 | 896 | fortran_noalias: false, |
| 816 | 897 | }; |
| 817 | - let mut callee = Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 898 | + let mut callee = | |
| 899 | + Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 818 | 900 | callee.is_pure = true; |
| 819 | 901 | let callee_entry = callee.entry; |
| 820 | 902 | let slot = push_inst( |
@@ -823,7 +905,12 @@ mod tests { | ||
| 823 | 905 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 824 | 906 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 825 | 907 | ); |
| 826 | - push_inst(&mut callee, callee_entry, InstKind::Store(ValueId(0), slot), IrType::Void); | |
| 908 | + push_inst( | |
| 909 | + &mut callee, | |
| 910 | + callee_entry, | |
| 911 | + InstKind::Store(ValueId(0), slot), | |
| 912 | + IrType::Void, | |
| 913 | + ); | |
| 827 | 914 | let arg_ptr = push_inst( |
| 828 | 915 | &mut callee, |
| 829 | 916 | callee_entry, |
@@ -855,7 +942,12 @@ mod tests { | ||
| 855 | 942 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 856 | 943 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 857 | 944 | ); |
| 858 | - push_inst(&mut caller, entry, InstKind::Store(c, wrapper), IrType::Void); | |
| 945 | + push_inst( | |
| 946 | + &mut caller, | |
| 947 | + entry, | |
| 948 | + InstKind::Store(c, wrapper), | |
| 949 | + IrType::Void, | |
| 950 | + ); | |
| 859 | 951 | let call = push_inst( |
| 860 | 952 | &mut caller, |
| 861 | 953 | entry, |
@@ -878,7 +970,8 @@ mod tests { | ||
| 878 | 970 | id: ValueId(0), |
| 879 | 971 | fortran_noalias: false, |
| 880 | 972 | }; |
| 881 | - let mut callee = Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 973 | + let mut callee = | |
| 974 | + Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 882 | 975 | callee.is_pure = true; |
| 883 | 976 | let callee_entry = callee.entry; |
| 884 | 977 | let shadow = push_inst( |
@@ -887,7 +980,12 @@ mod tests { | ||
| 887 | 980 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 888 | 981 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 889 | 982 | ); |
| 890 | - push_inst(&mut callee, callee_entry, InstKind::Store(ValueId(0), shadow), IrType::Void); | |
| 983 | + push_inst( | |
| 984 | + &mut callee, | |
| 985 | + callee_entry, | |
| 986 | + InstKind::Store(ValueId(0), shadow), | |
| 987 | + IrType::Void, | |
| 988 | + ); | |
| 891 | 989 | let arg_ptr = push_inst( |
| 892 | 990 | &mut callee, |
| 893 | 991 | callee_entry, |
@@ -942,7 +1040,12 @@ mod tests { | ||
| 942 | 1040 | InstKind::Call(FuncRef::Internal(0), vec![wrap1]), |
| 943 | 1041 | IrType::Int(IntWidth::I32), |
| 944 | 1042 | ); |
| 945 | - push_inst(&mut caller, entry, InstKind::Store(call1, first), IrType::Void); | |
| 1043 | + push_inst( | |
| 1044 | + &mut caller, | |
| 1045 | + entry, | |
| 1046 | + InstKind::Store(call1, first), | |
| 1047 | + IrType::Void, | |
| 1048 | + ); | |
| 946 | 1049 | let c3 = push_inst( |
| 947 | 1050 | &mut caller, |
| 948 | 1051 | entry, |
@@ -968,9 +1071,24 @@ mod tests { | ||
| 968 | 1071 | InstKind::Call(FuncRef::Internal(0), vec![wrap2]), |
| 969 | 1072 | IrType::Int(IntWidth::I32), |
| 970 | 1073 | ); |
| 971 | - push_inst(&mut caller, entry, InstKind::Store(call2, second), IrType::Void); | |
| 972 | - let left = push_inst(&mut caller, entry, InstKind::Load(first), IrType::Int(IntWidth::I32)); | |
| 973 | - let right = push_inst(&mut caller, entry, InstKind::Load(second), IrType::Int(IntWidth::I32)); | |
| 1074 | + push_inst( | |
| 1075 | + &mut caller, | |
| 1076 | + entry, | |
| 1077 | + InstKind::Store(call2, second), | |
| 1078 | + IrType::Void, | |
| 1079 | + ); | |
| 1080 | + let left = push_inst( | |
| 1081 | + &mut caller, | |
| 1082 | + entry, | |
| 1083 | + InstKind::Load(first), | |
| 1084 | + IrType::Int(IntWidth::I32), | |
| 1085 | + ); | |
| 1086 | + let right = push_inst( | |
| 1087 | + &mut caller, | |
| 1088 | + entry, | |
| 1089 | + InstKind::Load(second), | |
| 1090 | + IrType::Int(IntWidth::I32), | |
| 1091 | + ); | |
| 974 | 1092 | let sum = push_inst( |
| 975 | 1093 | &mut caller, |
| 976 | 1094 | entry, |
@@ -980,9 +1098,16 @@ mod tests { | ||
| 980 | 1098 | caller.block_mut(entry).terminator = Some(Terminator::Return(Some(sum))); |
| 981 | 1099 | m.add_function(caller); |
| 982 | 1100 | |
| 983 | - let pure_calls: Vec<PureCallPolicy> = m.functions.iter().map(PureCallPolicy::for_function).collect(); | |
| 1101 | + let pure_calls: Vec<PureCallPolicy> = m | |
| 1102 | + .functions | |
| 1103 | + .iter() | |
| 1104 | + .map(PureCallPolicy::for_function) | |
| 1105 | + .collect(); | |
| 984 | 1106 | assert!(pure_calls[0].reusable); |
| 985 | - assert_eq!(pure_calls[0].arg_policies, vec![PureArgPolicy::ReadOnlyWrapperPtr]); | |
| 1107 | + assert_eq!( | |
| 1108 | + pure_calls[0].arg_policies, | |
| 1109 | + vec![PureArgPolicy::ReadOnlyWrapperPtr] | |
| 1110 | + ); | |
| 986 | 1111 | let wrappers = wrapper_alloca_values(&m.functions[1], &pure_calls); |
| 987 | 1112 | assert_eq!(wrappers.get(&wrap1), Some(&c2)); |
| 988 | 1113 | assert_eq!(wrappers.get(&wrap2), Some(&c4)); |
@@ -1002,9 +1127,14 @@ mod tests { | ||
| 1002 | 1127 | .iter() |
| 1003 | 1128 | .find(|inst| inst.id == call2) |
| 1004 | 1129 | .expect("call2 inst"); |
| 1005 | - let key1 = key_of(call1_inst, &replacements, &pure_calls, &wrappers).expect("call1 should key"); | |
| 1006 | - let key2 = key_of(call2_inst, &replacements, &pure_calls, &wrappers).expect("call2 should key"); | |
| 1007 | - assert_eq!(key1, key2, "wrapper-based call keys should line up before the pass runs"); | |
| 1130 | + let key1 = | |
| 1131 | + key_of(call1_inst, &replacements, &pure_calls, &wrappers).expect("call1 should key"); | |
| 1132 | + let key2 = | |
| 1133 | + key_of(call2_inst, &replacements, &pure_calls, &wrappers).expect("call2 should key"); | |
| 1134 | + assert_eq!( | |
| 1135 | + key1, key2, | |
| 1136 | + "wrapper-based call keys should line up before the pass runs" | |
| 1137 | + ); | |
| 1008 | 1138 | |
| 1009 | 1139 | let pass = Gvn; |
| 1010 | 1140 | assert!(pass.run(&mut m)); |
@@ -1028,7 +1158,8 @@ mod tests { | ||
| 1028 | 1158 | id: ValueId(0), |
| 1029 | 1159 | fortran_noalias: false, |
| 1030 | 1160 | }; |
| 1031 | - let mut callee = Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 1161 | + let mut callee = | |
| 1162 | + Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 1032 | 1163 | callee.is_pure = true; |
| 1033 | 1164 | let entry = callee.entry; |
| 1034 | 1165 | let if_end = callee.create_block("if_end"); |
@@ -1041,7 +1172,12 @@ mod tests { | ||
| 1041 | 1172 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 1042 | 1173 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 1043 | 1174 | ); |
| 1044 | - push_inst(&mut callee, entry, InstKind::Store(ValueId(0), shadow), IrType::Void); | |
| 1175 | + push_inst( | |
| 1176 | + &mut callee, | |
| 1177 | + entry, | |
| 1178 | + InstKind::Store(ValueId(0), shadow), | |
| 1179 | + IrType::Void, | |
| 1180 | + ); | |
| 1045 | 1181 | let result = push_inst( |
| 1046 | 1182 | &mut callee, |
| 1047 | 1183 | entry, |
@@ -1054,7 +1190,12 @@ mod tests { | ||
| 1054 | 1190 | InstKind::Load(shadow), |
| 1055 | 1191 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1056 | 1192 | ); |
| 1057 | - let n1 = push_inst(&mut callee, entry, InstKind::Load(p1), IrType::Int(IntWidth::I32)); | |
| 1193 | + let n1 = push_inst( | |
| 1194 | + &mut callee, | |
| 1195 | + entry, | |
| 1196 | + InstKind::Load(p1), | |
| 1197 | + IrType::Int(IntWidth::I32), | |
| 1198 | + ); | |
| 1058 | 1199 | let one = push_inst( |
| 1059 | 1200 | &mut callee, |
| 1060 | 1201 | entry, |
@@ -1081,7 +1222,12 @@ mod tests { | ||
| 1081 | 1222 | InstKind::ConstInt(1, IntWidth::I32), |
| 1082 | 1223 | IrType::Int(IntWidth::I32), |
| 1083 | 1224 | ); |
| 1084 | - push_inst(&mut callee, if_then, InstKind::Store(then_one, result), IrType::Void); | |
| 1225 | + push_inst( | |
| 1226 | + &mut callee, | |
| 1227 | + if_then, | |
| 1228 | + InstKind::Store(then_one, result), | |
| 1229 | + IrType::Void, | |
| 1230 | + ); | |
| 1085 | 1231 | callee.block_mut(if_then).terminator = Some(Terminator::Branch(if_end, vec![])); |
| 1086 | 1232 | |
| 1087 | 1233 | let p2 = push_inst( |
@@ -1090,14 +1236,24 @@ mod tests { | ||
| 1090 | 1236 | InstKind::Load(shadow), |
| 1091 | 1237 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1092 | 1238 | ); |
| 1093 | - let n2 = push_inst(&mut callee, if_else, InstKind::Load(p2), IrType::Int(IntWidth::I32)); | |
| 1239 | + let n2 = push_inst( | |
| 1240 | + &mut callee, | |
| 1241 | + if_else, | |
| 1242 | + InstKind::Load(p2), | |
| 1243 | + IrType::Int(IntWidth::I32), | |
| 1244 | + ); | |
| 1094 | 1245 | let p3 = push_inst( |
| 1095 | 1246 | &mut callee, |
| 1096 | 1247 | if_else, |
| 1097 | 1248 | InstKind::Load(shadow), |
| 1098 | 1249 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1099 | 1250 | ); |
| 1100 | - let n3 = push_inst(&mut callee, if_else, InstKind::Load(p3), IrType::Int(IntWidth::I32)); | |
| 1251 | + let n3 = push_inst( | |
| 1252 | + &mut callee, | |
| 1253 | + if_else, | |
| 1254 | + InstKind::Load(p3), | |
| 1255 | + IrType::Int(IntWidth::I32), | |
| 1256 | + ); | |
| 1101 | 1257 | let one2 = push_inst( |
| 1102 | 1258 | &mut callee, |
| 1103 | 1259 | if_else, |
@@ -1116,7 +1272,12 @@ mod tests { | ||
| 1116 | 1272 | InstKind::Load(shadow), |
| 1117 | 1273 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1118 | 1274 | ); |
| 1119 | - let n4 = push_inst(&mut callee, if_else, InstKind::Load(p4), IrType::Int(IntWidth::I32)); | |
| 1275 | + let n4 = push_inst( | |
| 1276 | + &mut callee, | |
| 1277 | + if_else, | |
| 1278 | + InstKind::Load(p4), | |
| 1279 | + IrType::Int(IntWidth::I32), | |
| 1280 | + ); | |
| 1120 | 1281 | let one3 = push_inst( |
| 1121 | 1282 | &mut callee, |
| 1122 | 1283 | if_else, |
@@ -1135,7 +1296,12 @@ mod tests { | ||
| 1135 | 1296 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1136 | 1297 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1137 | 1298 | ); |
| 1138 | - push_inst(&mut callee, if_else, InstKind::Store(dec2, wrap), IrType::Void); | |
| 1299 | + push_inst( | |
| 1300 | + &mut callee, | |
| 1301 | + if_else, | |
| 1302 | + InstKind::Store(dec2, wrap), | |
| 1303 | + IrType::Void, | |
| 1304 | + ); | |
| 1139 | 1305 | let rec = push_inst( |
| 1140 | 1306 | &mut callee, |
| 1141 | 1307 | if_else, |
@@ -1148,10 +1314,20 @@ mod tests { | ||
| 1148 | 1314 | InstKind::IMul(n2, rec), |
| 1149 | 1315 | IrType::Int(IntWidth::I32), |
| 1150 | 1316 | ); |
| 1151 | - push_inst(&mut callee, if_else, InstKind::Store(prod, result), IrType::Void); | |
| 1317 | + push_inst( | |
| 1318 | + &mut callee, | |
| 1319 | + if_else, | |
| 1320 | + InstKind::Store(prod, result), | |
| 1321 | + IrType::Void, | |
| 1322 | + ); | |
| 1152 | 1323 | callee.block_mut(if_else).terminator = Some(Terminator::Branch(if_end, vec![])); |
| 1153 | 1324 | |
| 1154 | - let final_val = push_inst(&mut callee, if_end, InstKind::Load(result), IrType::Int(IntWidth::I32)); | |
| 1325 | + let final_val = push_inst( | |
| 1326 | + &mut callee, | |
| 1327 | + if_end, | |
| 1328 | + InstKind::Load(result), | |
| 1329 | + IrType::Int(IntWidth::I32), | |
| 1330 | + ); | |
| 1155 | 1331 | callee.block_mut(if_end).terminator = Some(Terminator::Return(Some(final_val))); |
| 1156 | 1332 | m.add_function(callee); |
| 1157 | 1333 | |
@@ -1175,7 +1351,12 @@ mod tests { | ||
| 1175 | 1351 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1176 | 1352 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1177 | 1353 | ); |
| 1178 | - push_inst(&mut caller, caller_entry, InstKind::Store(unit2, wrap1), IrType::Void); | |
| 1354 | + push_inst( | |
| 1355 | + &mut caller, | |
| 1356 | + caller_entry, | |
| 1357 | + InstKind::Store(unit2, wrap1), | |
| 1358 | + IrType::Void, | |
| 1359 | + ); | |
| 1179 | 1360 | let call1 = push_inst( |
| 1180 | 1361 | &mut caller, |
| 1181 | 1362 | caller_entry, |
@@ -1188,7 +1369,12 @@ mod tests { | ||
| 1188 | 1369 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1189 | 1370 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1190 | 1371 | ); |
| 1191 | - push_inst(&mut caller, caller_entry, InstKind::Store(unit, wrap2), IrType::Void); | |
| 1372 | + push_inst( | |
| 1373 | + &mut caller, | |
| 1374 | + caller_entry, | |
| 1375 | + InstKind::Store(unit, wrap2), | |
| 1376 | + IrType::Void, | |
| 1377 | + ); | |
| 1192 | 1378 | let call2 = push_inst( |
| 1193 | 1379 | &mut caller, |
| 1194 | 1380 | caller_entry, |
@@ -1212,7 +1398,10 @@ mod tests { | ||
| 1212 | 1398 | .iter() |
| 1213 | 1399 | .filter(|inst| matches!(inst.kind, InstKind::Call(..))) |
| 1214 | 1400 | .count(); |
| 1215 | - assert_eq!(call_count, 1, "recursive PURE factorial calls should dedupe"); | |
| 1401 | + assert_eq!( | |
| 1402 | + call_count, 1, | |
| 1403 | + "recursive PURE factorial calls should dedupe" | |
| 1404 | + ); | |
| 1216 | 1405 | } |
| 1217 | 1406 | |
| 1218 | 1407 | #[test] |
@@ -1274,7 +1463,8 @@ mod tests { | ||
| 1274 | 1463 | id: ValueId(0), |
| 1275 | 1464 | fortran_noalias: false, |
| 1276 | 1465 | }; |
| 1277 | - let mut callee = Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 1466 | + let mut callee = | |
| 1467 | + Function::new("heavy_fact".into(), vec![param], IrType::Int(IntWidth::I32)); | |
| 1278 | 1468 | callee.is_pure = true; |
| 1279 | 1469 | let callee_entry = callee.entry; |
| 1280 | 1470 | let shadow = push_inst( |
@@ -1283,7 +1473,12 @@ mod tests { | ||
| 1283 | 1473 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 1284 | 1474 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 1285 | 1475 | ); |
| 1286 | - push_inst(&mut callee, callee_entry, InstKind::Store(ValueId(0), shadow), IrType::Void); | |
| 1476 | + push_inst( | |
| 1477 | + &mut callee, | |
| 1478 | + callee_entry, | |
| 1479 | + InstKind::Store(ValueId(0), shadow), | |
| 1480 | + IrType::Void, | |
| 1481 | + ); | |
| 1287 | 1482 | let arg_ptr = push_inst( |
| 1288 | 1483 | &mut callee, |
| 1289 | 1484 | callee_entry, |
@@ -1307,7 +1502,10 @@ mod tests { | ||
| 1307 | 1502 | .iter() |
| 1308 | 1503 | .filter(|inst| matches!(inst.kind, InstKind::Call(..))) |
| 1309 | 1504 | .count(); |
| 1310 | - assert_eq!(call_count, 1, "caller-before-callee ordering should still dedupe"); | |
| 1505 | + assert_eq!( | |
| 1506 | + call_count, 1, | |
| 1507 | + "caller-before-callee ordering should still dedupe" | |
| 1508 | + ); | |
| 1311 | 1509 | } |
| 1312 | 1510 | |
| 1313 | 1511 | #[test] |
@@ -1316,7 +1514,11 @@ mod tests { | ||
| 1316 | 1514 | let pm = build_pre_gvn_o2_pipeline(); |
| 1317 | 1515 | pm.run(&mut module); |
| 1318 | 1516 | |
| 1319 | - let pure_calls: Vec<PureCallPolicy> = module.functions.iter().map(PureCallPolicy::for_function).collect(); | |
| 1517 | + let pure_calls: Vec<PureCallPolicy> = module | |
| 1518 | + .functions | |
| 1519 | + .iter() | |
| 1520 | + .map(PureCallPolicy::for_function) | |
| 1521 | + .collect(); | |
| 1320 | 1522 | assert!( |
| 1321 | 1523 | pure_calls.iter().any(|policy| policy.reusable), |
| 1322 | 1524 | "at least one function in the fixture should remain a reusable PURE callee:\n{}", |
@@ -1335,7 +1537,10 @@ mod tests { | ||
| 1335 | 1537 | ); |
| 1336 | 1538 | |
| 1337 | 1539 | let pass = Gvn; |
| 1338 | - assert!(pass.run(&mut module), "GVN should make progress on the real fixture"); | |
| 1540 | + assert!( | |
| 1541 | + pass.run(&mut module), | |
| 1542 | + "GVN should make progress on the real fixture" | |
| 1543 | + ); | |
| 1339 | 1544 | |
| 1340 | 1545 | let caller = &module.functions[caller_idx]; |
| 1341 | 1546 | let call_count = caller.blocks[0] |
@@ -1343,7 +1548,10 @@ mod tests { | ||
| 1343 | 1548 | .iter() |
| 1344 | 1549 | .filter(|inst| matches!(inst.kind, InstKind::Call(FuncRef::Internal(_), _))) |
| 1345 | 1550 | .count(); |
| 1346 | - assert_eq!(call_count, 1, "real fixture caller should end with one PURE recursive call"); | |
| 1551 | + assert_eq!( | |
| 1552 | + call_count, 1, | |
| 1553 | + "real fixture caller should end with one PURE recursive call" | |
| 1554 | + ); | |
| 1347 | 1555 | } |
| 1348 | 1556 | |
| 1349 | 1557 | #[test] |
@@ -1398,7 +1606,10 @@ mod tests { | ||
| 1398 | 1606 | m.add_function(caller); |
| 1399 | 1607 | |
| 1400 | 1608 | let pass = Gvn; |
| 1401 | - assert!(!pass.run(&mut m), "pointer-arg PURE calls must stay distinct"); | |
| 1609 | + assert!( | |
| 1610 | + !pass.run(&mut m), | |
| 1611 | + "pointer-arg PURE calls must stay distinct" | |
| 1612 | + ); | |
| 1402 | 1613 | |
| 1403 | 1614 | let caller = &m.functions[1]; |
| 1404 | 1615 | let call_count = caller.blocks[0] |
src/opt/inline.rsmodified@@ -13,14 +13,14 @@ | ||
| 13 | 13 | //! 5. Split the call-containing block: pre-call instructions + |
| 14 | 14 | //! branch to cloned entry, post-call block receives return value |
| 15 | 15 | |
| 16 | -use std::collections::HashMap; | |
| 17 | -use crate::ir::inst::*; | |
| 18 | -use crate::ir::types::IrType; | |
| 19 | -use crate::ir::walk::prune_unreachable; | |
| 20 | 16 | use super::callgraph::CallGraph; |
| 21 | 17 | use super::loop_utils::{remap_inst_kind, remap_terminator}; |
| 22 | 18 | use super::pass::Pass; |
| 23 | 19 | use super::pipeline::OptLevel; |
| 20 | +use crate::ir::inst::*; | |
| 21 | +use crate::ir::types::IrType; | |
| 22 | +use crate::ir::walk::prune_unreachable; | |
| 23 | +use std::collections::HashMap; | |
| 24 | 24 | |
| 25 | 25 | /// Maximum callee instruction count for inlining. |
| 26 | 26 | const INLINE_THRESHOLD_O1: usize = 20; |
@@ -43,10 +43,14 @@ impl Inline { | ||
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | impl Pass for Inline { |
| 46 | - fn name(&self) -> &'static str { "inline" } | |
| 46 | + fn name(&self) -> &'static str { | |
| 47 | + "inline" | |
| 48 | + } | |
| 47 | 49 | |
| 48 | 50 | fn run(&self, module: &mut Module) -> bool { |
| 49 | - if self.threshold == 0 { return false; } | |
| 51 | + if self.threshold == 0 { | |
| 52 | + return false; | |
| 53 | + } | |
| 50 | 54 | |
| 51 | 55 | let cg = CallGraph::build(module); |
| 52 | 56 | let order = cg.bottom_up_order(); |
@@ -77,11 +81,17 @@ fn inline_calls_in_function( | ||
| 77 | 81 | if let InstKind::Call(FuncRef::Internal(callee_idx), args) = &inst.kind { |
| 78 | 82 | let ci = *callee_idx; |
| 79 | 83 | // Don't inline recursive functions. |
| 80 | - if cg.is_recursive(ci) { continue; } | |
| 84 | + if cg.is_recursive(ci) { | |
| 85 | + continue; | |
| 86 | + } | |
| 81 | 87 | // Don't self-inline. |
| 82 | - if ci == caller_idx { continue; } | |
| 88 | + if ci == caller_idx { | |
| 89 | + continue; | |
| 90 | + } | |
| 83 | 91 | // Cost check. |
| 84 | - if cg.inline_cost(ci) > threshold { continue; } | |
| 92 | + if cg.inline_cost(ci) > threshold { | |
| 93 | + continue; | |
| 94 | + } | |
| 85 | 95 | // Argument/parameter type agreement. When a |
| 86 | 96 | // Fortran OPTIONAL parameter is absent at a call |
| 87 | 97 | // site, the caller passes `const_i64 0` as a null |
@@ -94,14 +104,19 @@ fn inline_calls_in_function( | ||
| 94 | 104 | // callee param type exactly. The same check also |
| 95 | 105 | // guards any future call-boundary coercion shims. |
| 96 | 106 | let callee = &module.functions[ci as usize]; |
| 97 | - if callee.params.len() != args.len() { continue; } | |
| 98 | - let mismatched = callee.params.iter().zip(args.iter()).any(|(p, a)| { | |
| 99 | - match caller.value_type(*a) { | |
| 100 | - Some(ty) => ty != p.ty, | |
| 101 | - None => true, | |
| 102 | - } | |
| 103 | - }); | |
| 104 | - if mismatched { continue; } | |
| 107 | + if callee.params.len() != args.len() { | |
| 108 | + continue; | |
| 109 | + } | |
| 110 | + let mismatched = | |
| 111 | + callee.params.iter().zip(args.iter()).any(|(p, a)| { | |
| 112 | + match caller.value_type(*a) { | |
| 113 | + Some(ty) => ty != p.ty, | |
| 114 | + None => true, | |
| 115 | + } | |
| 116 | + }); | |
| 117 | + if mismatched { | |
| 118 | + continue; | |
| 119 | + } | |
| 105 | 120 | sites.push((block.id, inst_idx, ci, args.clone())); |
| 106 | 121 | } |
| 107 | 122 | } |
@@ -109,7 +124,9 @@ fn inline_calls_in_function( | ||
| 109 | 124 | sites |
| 110 | 125 | }; |
| 111 | 126 | |
| 112 | - if call_sites.is_empty() { return false; } | |
| 127 | + if call_sites.is_empty() { | |
| 128 | + return false; | |
| 129 | + } | |
| 113 | 130 | |
| 114 | 131 | // Inline one call site, then return true to let the pass manager |
| 115 | 132 | // re-run us. Processing multiple sites in one invocation is unsafe: |
@@ -117,139 +134,133 @@ fn inline_calls_in_function( | ||
| 117 | 134 | // same block. The pass manager's fixpoint loop handles re-invocation. |
| 118 | 135 | let (call_block_id, call_inst_idx, callee_idx, caller_args) = call_sites[0].clone(); |
| 119 | 136 | { |
| 137 | + // Clone the callee's body into the caller. | |
| 138 | + let callee = &module.functions[callee_idx as usize]; | |
| 139 | + let callee_entry = callee.entry; | |
| 140 | + let callee_blocks: Vec<BasicBlock> = callee.blocks.clone(); | |
| 141 | + let callee_params: Vec<Param> = callee.params.clone(); | |
| 142 | + let callee_return_ty = callee.return_type.clone(); | |
| 143 | + | |
| 144 | + let caller = &mut module.functions[caller_idx as usize]; | |
| 145 | + | |
| 146 | + // Build value map: callee params → caller args. | |
| 147 | + let mut val_map: HashMap<ValueId, ValueId> = HashMap::new(); | |
| 148 | + for (param, &arg) in callee_params.iter().zip(caller_args.iter()) { | |
| 149 | + val_map.insert(param.id, arg); | |
| 150 | + } | |
| 120 | 151 | |
| 121 | - // Clone the callee's body into the caller. | |
| 122 | - let callee = &module.functions[callee_idx as usize]; | |
| 123 | - let callee_entry = callee.entry; | |
| 124 | - let callee_blocks: Vec<BasicBlock> = callee.blocks.clone(); | |
| 125 | - let callee_params: Vec<Param> = callee.params.clone(); | |
| 126 | - let callee_return_ty = callee.return_type.clone(); | |
| 127 | - | |
| 128 | - let caller = &mut module.functions[caller_idx as usize]; | |
| 129 | - | |
| 130 | - // Build value map: callee params → caller args. | |
| 131 | - let mut val_map: HashMap<ValueId, ValueId> = HashMap::new(); | |
| 132 | - for (param, &arg) in callee_params.iter().zip(caller_args.iter()) { | |
| 133 | - val_map.insert(param.id, arg); | |
| 134 | - } | |
| 135 | - | |
| 136 | - // Allocate fresh IDs for all callee values. | |
| 137 | - let mut block_map: HashMap<BlockId, BlockId> = HashMap::new(); | |
| 138 | - for cb in &callee_blocks { | |
| 139 | - let new_bid = caller.create_block(&format!("inline_{}", cb.name)); | |
| 140 | - block_map.insert(cb.id, new_bid); | |
| 141 | - } | |
| 142 | - | |
| 143 | - // Create post-call block to receive the return value. | |
| 144 | - let post_call = caller.create_block("inline_post"); | |
| 145 | - let has_return_val = !matches!(callee_return_ty, IrType::Void); | |
| 152 | + // Allocate fresh IDs for all callee values. | |
| 153 | + let mut block_map: HashMap<BlockId, BlockId> = HashMap::new(); | |
| 154 | + for cb in &callee_blocks { | |
| 155 | + let new_bid = caller.create_block(&format!("inline_{}", cb.name)); | |
| 156 | + block_map.insert(cb.id, new_bid); | |
| 157 | + } | |
| 146 | 158 | |
| 147 | - let result_param_id = if has_return_val { | |
| 148 | - let pid = caller.next_value_id(); | |
| 149 | - caller.register_type(pid, callee_return_ty.clone()); | |
| 150 | - caller.block_mut(post_call).params.push(BlockParam { | |
| 151 | - id: pid, | |
| 152 | - ty: callee_return_ty.clone(), | |
| 153 | - }); | |
| 154 | - Some(pid) | |
| 155 | - } else { | |
| 156 | - None | |
| 157 | - }; | |
| 159 | + // Create post-call block to receive the return value. | |
| 160 | + let post_call = caller.create_block("inline_post"); | |
| 161 | + let has_return_val = !matches!(callee_return_ty, IrType::Void); | |
| 158 | 162 | |
| 159 | - // Clone block params and instructions. | |
| 160 | - for cb in &callee_blocks { | |
| 161 | - let new_bid = block_map[&cb.id]; | |
| 162 | - // Clone block params. | |
| 163 | - for bp in &cb.params { | |
| 164 | - let new_id = caller.next_value_id(); | |
| 165 | - caller.register_type(new_id, bp.ty.clone()); | |
| 166 | - val_map.insert(bp.id, new_id); | |
| 167 | - caller.block_mut(new_bid).params.push(BlockParam { | |
| 168 | - id: new_id, | |
| 169 | - ty: bp.ty.clone(), | |
| 170 | - }); | |
| 171 | - } | |
| 172 | - // Clone instructions. | |
| 173 | - for inst in &cb.insts { | |
| 174 | - let new_id = caller.next_value_id(); | |
| 175 | - caller.register_type(new_id, inst.ty.clone()); | |
| 176 | - val_map.insert(inst.id, new_id); | |
| 177 | - let new_kind = remap_inst_kind(&inst.kind, &val_map); | |
| 178 | - caller.block_mut(new_bid).insts.push(Inst { | |
| 179 | - id: new_id, | |
| 180 | - kind: new_kind, | |
| 181 | - ty: inst.ty.clone(), | |
| 182 | - span: inst.span, | |
| 163 | + let result_param_id = if has_return_val { | |
| 164 | + let pid = caller.next_value_id(); | |
| 165 | + caller.register_type(pid, callee_return_ty.clone()); | |
| 166 | + caller.block_mut(post_call).params.push(BlockParam { | |
| 167 | + id: pid, | |
| 168 | + ty: callee_return_ty.clone(), | |
| 183 | 169 | }); |
| 184 | - } | |
| 185 | - } | |
| 170 | + Some(pid) | |
| 171 | + } else { | |
| 172 | + None | |
| 173 | + }; | |
| 186 | 174 | |
| 187 | - // Clone terminators, replacing Return with Branch to post_call. | |
| 188 | - for cb in &callee_blocks { | |
| 189 | - let new_bid = block_map[&cb.id]; | |
| 190 | - let new_term = match &cb.terminator { | |
| 191 | - Some(Terminator::Return(Some(val))) => { | |
| 192 | - let remapped = *val_map.get(val).unwrap_or(val); | |
| 193 | - Terminator::Branch(post_call, vec![remapped]) | |
| 194 | - } | |
| 195 | - Some(Terminator::Return(None)) => { | |
| 196 | - Terminator::Branch(post_call, vec![]) | |
| 175 | + // Clone block params and instructions. | |
| 176 | + for cb in &callee_blocks { | |
| 177 | + let new_bid = block_map[&cb.id]; | |
| 178 | + // Clone block params. | |
| 179 | + for bp in &cb.params { | |
| 180 | + let new_id = caller.next_value_id(); | |
| 181 | + caller.register_type(new_id, bp.ty.clone()); | |
| 182 | + val_map.insert(bp.id, new_id); | |
| 183 | + caller.block_mut(new_bid).params.push(BlockParam { | |
| 184 | + id: new_id, | |
| 185 | + ty: bp.ty.clone(), | |
| 186 | + }); | |
| 197 | 187 | } |
| 198 | - Some(other) => { | |
| 199 | - remap_terminator(other, &block_map, &val_map) | |
| 188 | + // Clone instructions. | |
| 189 | + for inst in &cb.insts { | |
| 190 | + let new_id = caller.next_value_id(); | |
| 191 | + caller.register_type(new_id, inst.ty.clone()); | |
| 192 | + val_map.insert(inst.id, new_id); | |
| 193 | + let new_kind = remap_inst_kind(&inst.kind, &val_map); | |
| 194 | + caller.block_mut(new_bid).insts.push(Inst { | |
| 195 | + id: new_id, | |
| 196 | + kind: new_kind, | |
| 197 | + ty: inst.ty.clone(), | |
| 198 | + span: inst.span, | |
| 199 | + }); | |
| 200 | 200 | } |
| 201 | - None => Terminator::Unreachable, | |
| 202 | - }; | |
| 203 | - caller.block_mut(new_bid).terminator = Some(new_term); | |
| 204 | - } | |
| 201 | + } | |
| 205 | 202 | |
| 206 | - // Split the call-containing block: move instructions after the call | |
| 207 | - // into the post-call block. | |
| 208 | - let call_block = caller.block_mut(call_block_id); | |
| 209 | - let call_result_id = call_block.insts[call_inst_idx].id; | |
| 203 | + // Clone terminators, replacing Return with Branch to post_call. | |
| 204 | + for cb in &callee_blocks { | |
| 205 | + let new_bid = block_map[&cb.id]; | |
| 206 | + let new_term = match &cb.terminator { | |
| 207 | + Some(Terminator::Return(Some(val))) => { | |
| 208 | + let remapped = *val_map.get(val).unwrap_or(val); | |
| 209 | + Terminator::Branch(post_call, vec![remapped]) | |
| 210 | + } | |
| 211 | + Some(Terminator::Return(None)) => Terminator::Branch(post_call, vec![]), | |
| 212 | + Some(other) => remap_terminator(other, &block_map, &val_map), | |
| 213 | + None => Terminator::Unreachable, | |
| 214 | + }; | |
| 215 | + caller.block_mut(new_bid).terminator = Some(new_term); | |
| 216 | + } | |
| 210 | 217 | |
| 211 | - // Move post-call instructions to the new block. | |
| 212 | - let post_insts: Vec<Inst> = call_block.insts.split_off(call_inst_idx + 1); | |
| 213 | - let old_term = call_block.terminator.take(); | |
| 218 | + // Split the call-containing block: move instructions after the call | |
| 219 | + // into the post-call block. | |
| 220 | + let call_block = caller.block_mut(call_block_id); | |
| 221 | + let call_result_id = call_block.insts[call_inst_idx].id; | |
| 214 | 222 | |
| 215 | - // Remove the call instruction itself. | |
| 216 | - call_block.insts.pop(); // removes the call at call_inst_idx | |
| 223 | + // Move post-call instructions to the new block. | |
| 224 | + let post_insts: Vec<Inst> = call_block.insts.split_off(call_inst_idx + 1); | |
| 225 | + let old_term = call_block.terminator.take(); | |
| 217 | 226 | |
| 218 | - // Add branch from call block to inlined entry. | |
| 219 | - let inlined_entry = block_map[&callee_entry]; | |
| 220 | - caller.block_mut(call_block_id).terminator = | |
| 221 | - Some(Terminator::Branch(inlined_entry, vec![])); | |
| 227 | + // Remove the call instruction itself. | |
| 228 | + call_block.insts.pop(); // removes the call at call_inst_idx | |
| 222 | 229 | |
| 223 | - // Populate post-call block with remaining instructions and terminator. | |
| 224 | - // Remap uses of the call result to the post-call block param. | |
| 225 | - let mut post_remap: HashMap<ValueId, ValueId> = HashMap::new(); | |
| 226 | - if let Some(param_id) = result_param_id { | |
| 227 | - post_remap.insert(call_result_id, param_id); | |
| 228 | - } | |
| 230 | + // Add branch from call block to inlined entry. | |
| 231 | + let inlined_entry = block_map[&callee_entry]; | |
| 232 | + caller.block_mut(call_block_id).terminator = | |
| 233 | + Some(Terminator::Branch(inlined_entry, vec![])); | |
| 229 | 234 | |
| 230 | - for inst in post_insts { | |
| 231 | - let new_kind = if post_remap.is_empty() { | |
| 232 | - inst.kind.clone() | |
| 233 | - } else { | |
| 234 | - remap_inst_kind(&inst.kind, &post_remap) | |
| 235 | - }; | |
| 236 | - caller.block_mut(post_call).insts.push(Inst { | |
| 237 | - id: inst.id, | |
| 238 | - kind: new_kind, | |
| 239 | - ty: inst.ty, | |
| 240 | - span: inst.span, | |
| 241 | - }); | |
| 242 | - } | |
| 235 | + // Populate post-call block with remaining instructions and terminator. | |
| 236 | + // Remap uses of the call result to the post-call block param. | |
| 237 | + let mut post_remap: HashMap<ValueId, ValueId> = HashMap::new(); | |
| 238 | + if let Some(param_id) = result_param_id { | |
| 239 | + post_remap.insert(call_result_id, param_id); | |
| 240 | + } | |
| 243 | 241 | |
| 244 | - if let Some(term) = old_term { | |
| 245 | - let new_term = if post_remap.is_empty() { | |
| 246 | - term | |
| 247 | - } else { | |
| 248 | - remap_terminator(&term, &HashMap::new(), &post_remap) | |
| 249 | - }; | |
| 250 | - caller.block_mut(post_call).terminator = Some(new_term); | |
| 251 | - } | |
| 242 | + for inst in post_insts { | |
| 243 | + let new_kind = if post_remap.is_empty() { | |
| 244 | + inst.kind.clone() | |
| 245 | + } else { | |
| 246 | + remap_inst_kind(&inst.kind, &post_remap) | |
| 247 | + }; | |
| 248 | + caller.block_mut(post_call).insts.push(Inst { | |
| 249 | + id: inst.id, | |
| 250 | + kind: new_kind, | |
| 251 | + ty: inst.ty, | |
| 252 | + span: inst.span, | |
| 253 | + }); | |
| 254 | + } | |
| 252 | 255 | |
| 256 | + if let Some(term) = old_term { | |
| 257 | + let new_term = if post_remap.is_empty() { | |
| 258 | + term | |
| 259 | + } else { | |
| 260 | + remap_terminator(&term, &HashMap::new(), &post_remap) | |
| 261 | + }; | |
| 262 | + caller.block_mut(post_call).terminator = Some(new_term); | |
| 263 | + } | |
| 253 | 264 | } // end single inline |
| 254 | 265 | |
| 255 | 266 | let caller = &mut module.functions[caller_idx as usize]; |
@@ -260,7 +271,7 @@ fn inline_calls_in_function( | ||
| 260 | 271 | #[cfg(test)] |
| 261 | 272 | mod tests { |
| 262 | 273 | use super::*; |
| 263 | - use crate::ir::types::{IrType, IntWidth}; | |
| 274 | + use crate::ir::types::{IntWidth, IrType}; | |
| 264 | 275 | use crate::opt::pass::Pass; |
| 265 | 276 | |
| 266 | 277 | #[test] |
src/opt/interchange.rsmodified@@ -24,20 +24,24 @@ | ||
| 24 | 24 | //! use both IVs as simple direct subscripts with no cross-iteration |
| 25 | 25 | //! read-before-write. This avoids needing full dependence analysis. |
| 26 | 26 | |
| 27 | -use crate::ir::inst::*; | |
| 28 | -use crate::ir::walk::predecessors; | |
| 29 | 27 | use super::loop_tree::build_loop_tree; |
| 30 | 28 | use super::pass::Pass; |
| 29 | +use crate::ir::inst::*; | |
| 30 | +use crate::ir::walk::predecessors; | |
| 31 | 31 | |
| 32 | 32 | pub struct LoopInterchange; |
| 33 | 33 | |
| 34 | 34 | impl Pass for LoopInterchange { |
| 35 | - fn name(&self) -> &'static str { "loop-interchange" } | |
| 35 | + fn name(&self) -> &'static str { | |
| 36 | + "loop-interchange" | |
| 37 | + } | |
| 36 | 38 | |
| 37 | 39 | fn run(&self, module: &mut Module) -> bool { |
| 38 | 40 | let mut changed = false; |
| 39 | 41 | for func in &mut module.functions { |
| 40 | - if interchange_in_function(func) { changed = true; } | |
| 42 | + if interchange_in_function(func) { | |
| 43 | + changed = true; | |
| 44 | + } | |
| 41 | 45 | } |
| 42 | 46 | changed |
| 43 | 47 | } |
@@ -54,8 +58,12 @@ fn interchange_in_function(func: &mut Function) -> bool { | ||
| 54 | 58 | |
| 55 | 59 | // Both loops must have a recognized counted-loop structure: |
| 56 | 60 | // header(%iv) → cmp_block(icmp, condBr) → body → latch(iadd, br header) |
| 57 | - let Some(outer_shape) = detect_loop_shape(func, outer, &preds) else { continue }; | |
| 58 | - let Some(inner_shape) = detect_loop_shape(func, inner, &preds) else { continue }; | |
| 61 | + let Some(outer_shape) = detect_loop_shape(func, outer, &preds) else { | |
| 62 | + continue; | |
| 63 | + }; | |
| 64 | + let Some(inner_shape) = detect_loop_shape(func, inner, &preds) else { | |
| 65 | + continue; | |
| 66 | + }; | |
| 59 | 67 | |
| 60 | 68 | // Check profitability: is the outer IV used as the first (fast) |
| 61 | 69 | // subscript of a multi-dimensional array GEP? |
@@ -80,11 +88,11 @@ fn interchange_in_function(func: &mut Function) -> bool { | ||
| 80 | 88 | struct LoopShape { |
| 81 | 89 | header: BlockId, |
| 82 | 90 | cmp_block: BlockId, |
| 83 | - iv: ValueId, // block param on header | |
| 84 | - bound: ValueId, // the upper-bound value in the comparison | |
| 91 | + iv: ValueId, // block param on header | |
| 92 | + bound: ValueId, // the upper-bound value in the comparison | |
| 85 | 93 | latch: BlockId, |
| 86 | 94 | /// The value passed to the header from the preheader (initial IV). |
| 87 | - init_arg_idx: usize, // index in preheader's branch args | |
| 95 | + init_arg_idx: usize, // index in preheader's branch args | |
| 88 | 96 | } |
| 89 | 97 | |
| 90 | 98 | fn detect_loop_shape( |
@@ -96,16 +104,22 @@ fn detect_loop_shape( | ||
| 96 | 104 | let hdr = func.block(header); |
| 97 | 105 | |
| 98 | 106 | // Header must have exactly 1 block param (the IV). |
| 99 | - if hdr.params.len() != 1 { return None; } | |
| 107 | + if hdr.params.len() != 1 { | |
| 108 | + return None; | |
| 109 | + } | |
| 100 | 110 | let iv = hdr.params[0].id; |
| 101 | 111 | |
| 102 | 112 | // Header must be a relay (0 instructions, branch to cmp_block). |
| 103 | - if !hdr.insts.is_empty() { return None; } | |
| 113 | + if !hdr.insts.is_empty() { | |
| 114 | + return None; | |
| 115 | + } | |
| 104 | 116 | let cmp_block = match &hdr.terminator { |
| 105 | 117 | Some(Terminator::Branch(t, args)) if args.is_empty() => *t, |
| 106 | 118 | _ => return None, |
| 107 | 119 | }; |
| 108 | - if !node.body.contains(&cmp_block) { return None; } | |
| 120 | + if !node.body.contains(&cmp_block) { | |
| 121 | + return None; | |
| 122 | + } | |
| 109 | 123 | |
| 110 | 124 | // Cmp block must have icmp + condBr. |
| 111 | 125 | let cmp_blk = func.block(cmp_block); |
@@ -114,15 +128,20 @@ fn detect_loop_shape( | ||
| 114 | 128 | for inst in &cmp_blk.insts { |
| 115 | 129 | if let InstKind::ICmp(_, a, b) = &inst.kind { |
| 116 | 130 | // One operand should be the IV, the other is the bound. |
| 117 | - if *a == iv { found_bound = Some(*b); } | |
| 118 | - else if *b == iv { found_bound = Some(*a); } | |
| 131 | + if *a == iv { | |
| 132 | + found_bound = Some(*b); | |
| 133 | + } else if *b == iv { | |
| 134 | + found_bound = Some(*a); | |
| 135 | + } | |
| 119 | 136 | } |
| 120 | 137 | } |
| 121 | 138 | found_bound? |
| 122 | 139 | }; |
| 123 | 140 | |
| 124 | 141 | // Find the single latch. |
| 125 | - if node.latches.len() != 1 { return None; } | |
| 142 | + if node.latches.len() != 1 { | |
| 143 | + return None; | |
| 144 | + } | |
| 126 | 145 | let latch = node.latches[0]; |
| 127 | 146 | |
| 128 | 147 | Some(LoopShape { |
@@ -185,7 +204,9 @@ fn uses_iv_in_fast_position( | ||
| 185 | 204 | _inner_iv: ValueId, |
| 186 | 205 | ) -> bool { |
| 187 | 206 | // Find the instruction that produces `offset`. |
| 188 | - let Some(inst) = find_inst(func, offset) else { return false }; | |
| 207 | + let Some(inst) = find_inst(func, offset) else { | |
| 208 | + return false; | |
| 209 | + }; | |
| 189 | 210 | |
| 190 | 211 | // The offset should be an iadd of two parts. |
| 191 | 212 | let (a, b) = match &inst.kind { |
@@ -199,9 +220,13 @@ fn uses_iv_in_fast_position( | ||
| 199 | 220 | let b_uses_mul = trace_involves_mul(func, b); |
| 200 | 221 | |
| 201 | 222 | // The fast part is the one WITHOUT multiplication. |
| 202 | - let fast_part = if !a_uses_mul && b_uses_mul { a } | |
| 203 | - else if a_uses_mul && !b_uses_mul { b } | |
| 204 | - else { return false }; | |
| 223 | + let fast_part = if !a_uses_mul && b_uses_mul { | |
| 224 | + a | |
| 225 | + } else if a_uses_mul && !b_uses_mul { | |
| 226 | + b | |
| 227 | + } else { | |
| 228 | + return false; | |
| 229 | + }; | |
| 205 | 230 | |
| 206 | 231 | // Does the fast part trace back to the outer IV? |
| 207 | 232 | traces_to_iv(func, fast_part, outer_iv) |
@@ -209,11 +234,14 @@ fn uses_iv_in_fast_position( | ||
| 209 | 234 | |
| 210 | 235 | /// Check if a value's computation involves an IMul somewhere. |
| 211 | 236 | fn trace_involves_mul(func: &Function, val: ValueId) -> bool { |
| 212 | - let Some(inst) = find_inst(func, val) else { return false }; | |
| 237 | + let Some(inst) = find_inst(func, val) else { | |
| 238 | + return false; | |
| 239 | + }; | |
| 213 | 240 | match &inst.kind { |
| 214 | 241 | InstKind::IMul(..) => true, |
| 215 | - InstKind::IAdd(a, b) | InstKind::ISub(a, b) => | |
| 216 | - trace_involves_mul(func, *a) || trace_involves_mul(func, *b), | |
| 242 | + InstKind::IAdd(a, b) | InstKind::ISub(a, b) => { | |
| 243 | + trace_involves_mul(func, *a) || trace_involves_mul(func, *b) | |
| 244 | + } | |
| 217 | 245 | InstKind::IntExtend(a, _, _) => trace_involves_mul(func, *a), |
| 218 | 246 | _ => false, |
| 219 | 247 | } |
@@ -221,8 +249,12 @@ fn trace_involves_mul(func: &Function, val: ValueId) -> bool { | ||
| 221 | 249 | |
| 222 | 250 | /// Check if a value traces back to a specific IV (through isub, int_extend). |
| 223 | 251 | fn traces_to_iv(func: &Function, val: ValueId, iv: ValueId) -> bool { |
| 224 | - if val == iv { return true; } | |
| 225 | - let Some(inst) = find_inst(func, val) else { return false }; | |
| 252 | + if val == iv { | |
| 253 | + return true; | |
| 254 | + } | |
| 255 | + let Some(inst) = find_inst(func, val) else { | |
| 256 | + return false; | |
| 257 | + }; | |
| 226 | 258 | match &inst.kind { |
| 227 | 259 | InstKind::ISub(a, _) => traces_to_iv(func, *a, iv), |
| 228 | 260 | InstKind::IntExtend(a, _, _) => traces_to_iv(func, *a, iv), |
@@ -234,7 +266,9 @@ fn traces_to_iv(func: &Function, val: ValueId, iv: ValueId) -> bool { | ||
| 234 | 266 | fn find_inst(func: &Function, vid: ValueId) -> Option<&Inst> { |
| 235 | 267 | for block in &func.blocks { |
| 236 | 268 | for inst in &block.insts { |
| 237 | - if inst.id == vid { return Some(inst); } | |
| 269 | + if inst.id == vid { | |
| 270 | + return Some(inst); | |
| 271 | + } | |
| 238 | 272 | } |
| 239 | 273 | } |
| 240 | 274 | None |
@@ -253,7 +287,9 @@ fn is_interchange_legal( | ||
| 253 | 287 | |
| 254 | 288 | /// Trace through a GEP chain to find the base array pointer. |
| 255 | 289 | fn trace_gep_base(func: &Function, ptr: ValueId) -> Option<ValueId> { |
| 256 | - let Some(inst) = find_inst(func, ptr) else { return Some(ptr); }; | |
| 290 | + let Some(inst) = find_inst(func, ptr) else { | |
| 291 | + return Some(ptr); | |
| 292 | + }; | |
| 257 | 293 | match &inst.kind { |
| 258 | 294 | InstKind::GetElementPtr(base, _) => Some(*base), |
| 259 | 295 | _ => Some(ptr), |
@@ -277,7 +313,12 @@ fn do_interchange(func: &mut Function, outer: &LoopShape, inner: &LoopShape) { | ||
| 277 | 313 | break; |
| 278 | 314 | } |
| 279 | 315 | } |
| 280 | - if let Some(Terminator::CondBranch { true_dest, false_dest, .. }) = &block.terminator { | |
| 316 | + if let Some(Terminator::CondBranch { | |
| 317 | + true_dest, | |
| 318 | + false_dest, | |
| 319 | + .. | |
| 320 | + }) = &block.terminator | |
| 321 | + { | |
| 281 | 322 | if *true_dest == outer.header || *false_dest == outer.header { |
| 282 | 323 | // Could be a condBr preheader from preheader insertion |
| 283 | 324 | // but we need the unconditional one. |
@@ -286,7 +327,9 @@ fn do_interchange(func: &mut Function, outer: &LoopShape, inner: &LoopShape) { | ||
| 286 | 327 | } |
| 287 | 328 | ph |
| 288 | 329 | }; |
| 289 | - let Some(outer_ph) = outer_preheader else { return; }; | |
| 330 | + let Some(outer_ph) = outer_preheader else { | |
| 331 | + return; | |
| 332 | + }; | |
| 290 | 333 | |
| 291 | 334 | // Find the block that branches to the inner header with the inner |
| 292 | 335 | // IV init value (this is the outer loop's "body entry" block). |
@@ -299,7 +342,12 @@ fn do_interchange(func: &mut Function, outer: &LoopShape, inner: &LoopShape) { | ||
| 299 | 342 | break; |
| 300 | 343 | } |
| 301 | 344 | } |
| 302 | - if let Some(Terminator::CondBranch { true_dest, true_args, .. }) = &block.terminator { | |
| 345 | + if let Some(Terminator::CondBranch { | |
| 346 | + true_dest, | |
| 347 | + true_args, | |
| 348 | + .. | |
| 349 | + }) = &block.terminator | |
| 350 | + { | |
| 303 | 351 | if *true_dest == inner.header && !true_args.is_empty() { |
| 304 | 352 | ie = Some(block.id); |
| 305 | 353 | break; |
@@ -308,13 +356,19 @@ fn do_interchange(func: &mut Function, outer: &LoopShape, inner: &LoopShape) { | ||
| 308 | 356 | } |
| 309 | 357 | ie |
| 310 | 358 | }; |
| 311 | - let Some(inner_entry_block) = inner_entry else { return; }; | |
| 359 | + let Some(inner_entry_block) = inner_entry else { | |
| 360 | + return; | |
| 361 | + }; | |
| 312 | 362 | |
| 313 | 363 | // Get current init values. |
| 314 | 364 | let outer_init = get_branch_arg_to(func, outer_ph, outer.header, 0); |
| 315 | 365 | let inner_init = get_branch_arg_to(func, inner_entry_block, inner.header, 0); |
| 316 | - let Some(outer_init_val) = outer_init else { return }; | |
| 317 | - let Some(inner_init_val) = inner_init else { return }; | |
| 366 | + let Some(outer_init_val) = outer_init else { | |
| 367 | + return; | |
| 368 | + }; | |
| 369 | + let Some(inner_init_val) = inner_init else { | |
| 370 | + return; | |
| 371 | + }; | |
| 318 | 372 | |
| 319 | 373 | // Swap init values: outer preheader now passes inner's init to |
| 320 | 374 | // outer's header, and inner entry now passes outer's init to |
@@ -332,10 +386,20 @@ fn get_branch_arg_to(func: &Function, from: BlockId, to: BlockId, idx: usize) -> | ||
| 332 | 386 | let block = func.block(from); |
| 333 | 387 | match &block.terminator { |
| 334 | 388 | Some(Terminator::Branch(dest, args)) if *dest == to => args.get(idx).copied(), |
| 335 | - Some(Terminator::CondBranch { true_dest, true_args, false_dest, false_args, .. }) => { | |
| 336 | - if *true_dest == to { true_args.get(idx).copied() } | |
| 337 | - else if *false_dest == to { false_args.get(idx).copied() } | |
| 338 | - else { None } | |
| 389 | + Some(Terminator::CondBranch { | |
| 390 | + true_dest, | |
| 391 | + true_args, | |
| 392 | + false_dest, | |
| 393 | + false_args, | |
| 394 | + .. | |
| 395 | + }) => { | |
| 396 | + if *true_dest == to { | |
| 397 | + true_args.get(idx).copied() | |
| 398 | + } else if *false_dest == to { | |
| 399 | + false_args.get(idx).copied() | |
| 400 | + } else { | |
| 401 | + None | |
| 402 | + } | |
| 339 | 403 | } |
| 340 | 404 | _ => None, |
| 341 | 405 | } |
@@ -346,18 +410,35 @@ fn set_branch_arg_to(func: &mut Function, from: BlockId, to: BlockId, idx: usize | ||
| 346 | 410 | let block = func.block_mut(from); |
| 347 | 411 | match &mut block.terminator { |
| 348 | 412 | Some(Terminator::Branch(dest, args)) if *dest == to => { |
| 349 | - if idx < args.len() { args[idx] = val; } | |
| 413 | + if idx < args.len() { | |
| 414 | + args[idx] = val; | |
| 415 | + } | |
| 350 | 416 | } |
| 351 | - Some(Terminator::CondBranch { true_dest, true_args, false_dest, false_args, .. }) => { | |
| 352 | - if *true_dest == to && idx < true_args.len() { true_args[idx] = val; } | |
| 353 | - else if *false_dest == to && idx < false_args.len() { false_args[idx] = val; } | |
| 417 | + Some(Terminator::CondBranch { | |
| 418 | + true_dest, | |
| 419 | + true_args, | |
| 420 | + false_dest, | |
| 421 | + false_args, | |
| 422 | + .. | |
| 423 | + }) => { | |
| 424 | + if *true_dest == to && idx < true_args.len() { | |
| 425 | + true_args[idx] = val; | |
| 426 | + } else if *false_dest == to && idx < false_args.len() { | |
| 427 | + false_args[idx] = val; | |
| 428 | + } | |
| 354 | 429 | } |
| 355 | 430 | _ => {} |
| 356 | 431 | } |
| 357 | 432 | } |
| 358 | 433 | |
| 359 | 434 | /// Swap the bound value in a comparison block's ICmp instruction. |
| 360 | -fn swap_bound(func: &mut Function, cmp_block: BlockId, iv: ValueId, old_bound: ValueId, new_bound: ValueId) { | |
| 435 | +fn swap_bound( | |
| 436 | + func: &mut Function, | |
| 437 | + cmp_block: BlockId, | |
| 438 | + iv: ValueId, | |
| 439 | + old_bound: ValueId, | |
| 440 | + new_bound: ValueId, | |
| 441 | +) { | |
| 361 | 442 | let block = func.block_mut(cmp_block); |
| 362 | 443 | for inst in &mut block.insts { |
| 363 | 444 | if let InstKind::ICmp(_op, a, b) = &mut inst.kind { |
@@ -380,12 +461,16 @@ fn swap_bound(func: &mut Function, cmp_block: BlockId, iv: ValueId, old_bound: V | ||
| 380 | 461 | mod tests { |
| 381 | 462 | use super::*; |
| 382 | 463 | use crate::ir::types::IrType; |
| 464 | + use crate::lexer::{Position, Span}; | |
| 383 | 465 | use crate::opt::pass::Pass; |
| 384 | - use crate::lexer::{Span, Position}; | |
| 385 | 466 | |
| 386 | 467 | fn span() -> Span { |
| 387 | 468 | let pos = Position { line: 0, col: 0 }; |
| 388 | - Span { file_id: 0, start: pos, end: pos } | |
| 469 | + Span { | |
| 470 | + file_id: 0, | |
| 471 | + start: pos, | |
| 472 | + end: pos, | |
| 473 | + } | |
| 389 | 474 | } |
| 390 | 475 | |
| 391 | 476 | #[test] |
src/opt/licm.rsmodified@@ -45,7 +45,7 @@ | ||
| 45 | 45 | |
| 46 | 46 | use super::alias::{self, AliasResult}; |
| 47 | 47 | use super::pass::Pass; |
| 48 | -use super::util::{find_natural_loops, predecessors, inst_uses, NaturalLoop}; | |
| 48 | +use super::util::{find_natural_loops, inst_uses, predecessors, NaturalLoop}; | |
| 49 | 49 | use crate::ir::inst::*; |
| 50 | 50 | use std::collections::{HashMap, HashSet}; |
| 51 | 51 | |
@@ -140,7 +140,9 @@ fn licm_function(func: &mut Function) -> bool { | ||
| 140 | 140 | let pruned = super::util::prune_unreachable(func); |
| 141 | 141 | |
| 142 | 142 | let loops = find_natural_loops(func); |
| 143 | - if loops.is_empty() { return pruned; } | |
| 143 | + if loops.is_empty() { | |
| 144 | + return pruned; | |
| 145 | + } | |
| 144 | 146 | |
| 145 | 147 | let preds = predecessors(func); |
| 146 | 148 | let mut any_hoisted = pruned; |
@@ -152,14 +154,18 @@ fn licm_function(func: &mut Function) -> bool { | ||
| 152 | 154 | // we'd "rebuild per iteration" — that was always aspirational |
| 153 | 155 | // and would only matter if a future variant of LICM started |
| 154 | 156 | // mutating the block vector. |
| 155 | - let block_index: HashMap<BlockId, usize> = func.blocks.iter() | |
| 157 | + let block_index: HashMap<BlockId, usize> = func | |
| 158 | + .blocks | |
| 159 | + .iter() | |
| 156 | 160 | .enumerate() |
| 157 | 161 | .map(|(i, b)| (b.id, i)) |
| 158 | 162 | .collect(); |
| 159 | 163 | |
| 160 | 164 | for lp in &loops { |
| 161 | 165 | // Need a preheader to hoist into. |
| 162 | - let Some(ph_id) = find_preheader(func, lp, &preds) else { continue; }; | |
| 166 | + let Some(ph_id) = find_preheader(func, lp, &preds) else { | |
| 167 | + continue; | |
| 168 | + }; | |
| 163 | 169 | |
| 164 | 170 | // Iteratively find invariant instructions until a full pass |
| 165 | 171 | // turns up nothing new. After each round we mark the hoisted |
@@ -170,7 +176,9 @@ fn licm_function(func: &mut Function) -> bool { | ||
| 170 | 176 | loop { |
| 171 | 177 | let mut hoists: Vec<Hoist> = Vec::new(); |
| 172 | 178 | for (bi, block) in func.blocks.iter().enumerate() { |
| 173 | - if !lp.body.contains(&block.id) { continue; } | |
| 179 | + if !lp.body.contains(&block.id) { | |
| 180 | + continue; | |
| 181 | + } | |
| 174 | 182 | for (ii, inst) in block.insts.iter().enumerate() { |
| 175 | 183 | if !loop_defs.contains(&inst.id) { |
| 176 | 184 | // Already hoisted (we mark it removed from |
@@ -181,16 +189,25 @@ fn licm_function(func: &mut Function) -> bool { | ||
| 181 | 189 | // not in the loop_defs set (i.e., previously |
| 182 | 190 | // hoisted, or defined outside). |
| 183 | 191 | let operands = inst_uses(&inst.kind); |
| 184 | - if operands.iter().any(|v| loop_defs.contains(v)) { continue; } | |
| 192 | + if operands.iter().any(|v| loop_defs.contains(v)) { | |
| 193 | + continue; | |
| 194 | + } | |
| 185 | 195 | let hoistable = match &inst.kind { |
| 186 | 196 | InstKind::Load(ptr) => load_is_loop_invariant(func, lp, inst.id, *ptr), |
| 187 | 197 | _ => is_non_memory_hoist_candidate(&inst.kind), |
| 188 | 198 | }; |
| 189 | - if !hoistable { continue; } | |
| 190 | - hoists.push(Hoist { block_idx: bi, inst_idx: ii }); | |
| 199 | + if !hoistable { | |
| 200 | + continue; | |
| 201 | + } | |
| 202 | + hoists.push(Hoist { | |
| 203 | + block_idx: bi, | |
| 204 | + inst_idx: ii, | |
| 205 | + }); | |
| 191 | 206 | } |
| 192 | 207 | } |
| 193 | - if hoists.is_empty() { break; } | |
| 208 | + if hoists.is_empty() { | |
| 209 | + break; | |
| 210 | + } | |
| 194 | 211 | |
| 195 | 212 | // Apply hoists: remove from source blocks (in reverse |
| 196 | 213 | // index order so earlier indices remain valid), then |
@@ -241,12 +258,16 @@ fn licm_function(func: &mut Function) -> bool { | ||
| 241 | 258 | pub struct Licm; |
| 242 | 259 | |
| 243 | 260 | impl Pass for Licm { |
| 244 | - fn name(&self) -> &'static str { "licm" } | |
| 261 | + fn name(&self) -> &'static str { | |
| 262 | + "licm" | |
| 263 | + } | |
| 245 | 264 | |
| 246 | 265 | fn run(&self, module: &mut Module) -> bool { |
| 247 | 266 | let mut changed = false; |
| 248 | 267 | for func in &mut module.functions { |
| 249 | - if licm_function(func) { changed = true; } | |
| 268 | + if licm_function(func) { | |
| 269 | + changed = true; | |
| 270 | + } | |
| 250 | 271 | } |
| 251 | 272 | changed |
| 252 | 273 | } |
@@ -255,12 +276,16 @@ impl Pass for Licm { | ||
| 255 | 276 | #[cfg(test)] |
| 256 | 277 | mod tests { |
| 257 | 278 | use super::*; |
| 258 | - use crate::ir::types::{IrType, IntWidth}; | |
| 259 | - use crate::lexer::{Span, Position}; | |
| 279 | + use crate::ir::types::{IntWidth, IrType}; | |
| 280 | + use crate::lexer::{Position, Span}; | |
| 260 | 281 | |
| 261 | 282 | fn dummy_span() -> Span { |
| 262 | 283 | let p = Position { line: 1, col: 1 }; |
| 263 | - Span { start: p, end: p, file_id: 0 } | |
| 284 | + Span { | |
| 285 | + start: p, | |
| 286 | + end: p, | |
| 287 | + file_id: 0, | |
| 288 | + } | |
| 264 | 289 | } |
| 265 | 290 | |
| 266 | 291 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
@@ -318,7 +343,10 @@ mod tests { | ||
| 318 | 343 | // Header. |
| 319 | 344 | let header = f.create_block("header"); |
| 320 | 345 | let i_param = f.next_value_id(); |
| 321 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 346 | + f.block_mut(header).params.push(BlockParam { | |
| 347 | + id: i_param, | |
| 348 | + ty: IrType::Int(IntWidth::I32), | |
| 349 | + }); | |
| 322 | 350 | let k = f.next_value_id(); |
| 323 | 351 | let tmp = f.next_value_id(); |
| 324 | 352 | let done = f.next_value_id(); |
@@ -344,7 +372,10 @@ mod tests { | ||
| 344 | 372 | // Latch. |
| 345 | 373 | let latch = f.create_block("latch"); |
| 346 | 374 | let i_in = f.next_value_id(); |
| 347 | - f.block_mut(latch).params.push(BlockParam { id: i_in, ty: IrType::Int(IntWidth::I32) }); | |
| 375 | + f.block_mut(latch).params.push(BlockParam { | |
| 376 | + id: i_in, | |
| 377 | + ty: IrType::Int(IntWidth::I32), | |
| 378 | + }); | |
| 348 | 379 | let one = f.next_value_id(); |
| 349 | 380 | let next = f.next_value_id(); |
| 350 | 381 | f.block_mut(latch).insts.push(Inst { |
@@ -389,11 +420,20 @@ mod tests { | ||
| 389 | 420 | let entry_block = f.block(f.entry); |
| 390 | 421 | // The const(5) should now live in the entry block (which is |
| 391 | 422 | // the natural preheader), not the header. |
| 392 | - let in_entry_const5 = entry_block.insts.iter().any(|i| matches!(i.kind, InstKind::ConstInt(5, IntWidth::I32))); | |
| 393 | - assert!(in_entry_const5, "const(5) should be hoisted into preheader (entry)"); | |
| 423 | + let in_entry_const5 = entry_block | |
| 424 | + .insts | |
| 425 | + .iter() | |
| 426 | + .any(|i| matches!(i.kind, InstKind::ConstInt(5, IntWidth::I32))); | |
| 427 | + assert!( | |
| 428 | + in_entry_const5, | |
| 429 | + "const(5) should be hoisted into preheader (entry)" | |
| 430 | + ); | |
| 394 | 431 | |
| 395 | 432 | let header_block = f.block(header); |
| 396 | - let in_header_const5 = header_block.insts.iter().any(|i| matches!(i.kind, InstKind::ConstInt(5, IntWidth::I32))); | |
| 433 | + let in_header_const5 = header_block | |
| 434 | + .insts | |
| 435 | + .iter() | |
| 436 | + .any(|i| matches!(i.kind, InstKind::ConstInt(5, IntWidth::I32))); | |
| 397 | 437 | assert!(!in_header_const5, "const(5) should be removed from header"); |
| 398 | 438 | } |
| 399 | 439 | |
@@ -405,8 +445,14 @@ mod tests { | ||
| 405 | 445 | Licm.run(&mut m); |
| 406 | 446 | let f = &m.functions[0]; |
| 407 | 447 | let header_block = f.block(header); |
| 408 | - let header_has_iadd = header_block.insts.iter().any(|i| matches!(i.kind, InstKind::IAdd(..))); | |
| 409 | - assert!(header_has_iadd, "IAdd(i_param, k) must remain in the header"); | |
| 448 | + let header_has_iadd = header_block | |
| 449 | + .insts | |
| 450 | + .iter() | |
| 451 | + .any(|i| matches!(i.kind, InstKind::IAdd(..))); | |
| 452 | + assert!( | |
| 453 | + header_has_iadd, | |
| 454 | + "IAdd(i_param, k) must remain in the header" | |
| 455 | + ); | |
| 410 | 456 | } |
| 411 | 457 | |
| 412 | 458 | #[test] |
@@ -459,7 +505,10 @@ mod tests { | ||
| 459 | 505 | |
| 460 | 506 | let header = f.create_block("header"); |
| 461 | 507 | let i_param = f.next_value_id(); |
| 462 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 508 | + f.block_mut(header).params.push(BlockParam { | |
| 509 | + id: i_param, | |
| 510 | + ty: IrType::Int(IntWidth::I32), | |
| 511 | + }); | |
| 463 | 512 | let v = f.next_value_id(); |
| 464 | 513 | f.block_mut(header).insts.push(Inst { |
| 465 | 514 | id: v, |
@@ -503,9 +552,14 @@ mod tests { | ||
| 503 | 552 | // The Load must still be in the header after LICM. |
| 504 | 553 | let f = &m.functions[0]; |
| 505 | 554 | let header_block = f.block(header); |
| 506 | - let load_in_header = header_block.insts.iter() | |
| 555 | + let load_in_header = header_block | |
| 556 | + .insts | |
| 557 | + .iter() | |
| 507 | 558 | .any(|i| matches!(i.kind, InstKind::Load(_))); |
| 508 | - assert!(load_in_header, "ambiguous loop load should not have been hoisted"); | |
| 559 | + assert!( | |
| 560 | + load_in_header, | |
| 561 | + "ambiguous loop load should not have been hoisted" | |
| 562 | + ); | |
| 509 | 563 | } |
| 510 | 564 | |
| 511 | 565 | #[test] |
@@ -554,7 +608,10 @@ mod tests { | ||
| 554 | 608 | |
| 555 | 609 | let header = f.create_block("header"); |
| 556 | 610 | let i_param = f.next_value_id(); |
| 557 | - f.block_mut(header).params.push(BlockParam { id: i_param, ty: IrType::Int(IntWidth::I32) }); | |
| 611 | + f.block_mut(header).params.push(BlockParam { | |
| 612 | + id: i_param, | |
| 613 | + ty: IrType::Int(IntWidth::I32), | |
| 614 | + }); | |
| 558 | 615 | let v = f.next_value_id(); |
| 559 | 616 | f.block_mut(header).insts.push(Inst { |
| 560 | 617 | id: v, |
@@ -584,7 +641,10 @@ mod tests { | ||
| 584 | 641 | |
| 585 | 642 | m.add_function(f); |
| 586 | 643 | |
| 587 | - assert!(Licm.run(&mut m), "LICM should report that it hoisted the loop-invariant load"); | |
| 644 | + assert!( | |
| 645 | + Licm.run(&mut m), | |
| 646 | + "LICM should report that it hoisted the loop-invariant load" | |
| 647 | + ); | |
| 588 | 648 | |
| 589 | 649 | // The Load must have moved out of the header — into the |
| 590 | 650 | // preheader or the entry block (LICM hoists into the |
@@ -596,14 +656,20 @@ mod tests { | ||
| 596 | 656 | .insts |
| 597 | 657 | .iter() |
| 598 | 658 | .any(|i| matches!(i.kind, InstKind::Load(_))); |
| 599 | - assert!(!load_still_in_header, "invariant load should have left the header"); | |
| 659 | + assert!( | |
| 660 | + !load_still_in_header, | |
| 661 | + "invariant load should have left the header" | |
| 662 | + ); | |
| 600 | 663 | let load_somewhere_else = f |
| 601 | 664 | .blocks |
| 602 | 665 | .iter() |
| 603 | 666 | .filter(|b| b.id != header) |
| 604 | 667 | .flat_map(|b| b.insts.iter()) |
| 605 | 668 | .any(|i| matches!(i.kind, InstKind::Load(_))); |
| 606 | - assert!(load_somewhere_else, "invariant load should have been placed in a dominating block"); | |
| 669 | + assert!( | |
| 670 | + load_somewhere_else, | |
| 671 | + "invariant load should have been placed in a dominating block" | |
| 672 | + ); | |
| 607 | 673 | } |
| 608 | 674 | |
| 609 | 675 | #[test] |
@@ -625,8 +691,16 @@ mod tests { | ||
| 625 | 691 | let mut m = Module::new("t".into()); |
| 626 | 692 | let mut f = Function::new("f".into(), params, IrType::Void); |
| 627 | 693 | |
| 628 | - let init = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 629 | - let limit = push(&mut f, InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 694 | + let init = push( | |
| 695 | + &mut f, | |
| 696 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 697 | + IrType::Int(IntWidth::I32), | |
| 698 | + ); | |
| 699 | + let limit = push( | |
| 700 | + &mut f, | |
| 701 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 702 | + IrType::Int(IntWidth::I32), | |
| 703 | + ); | |
| 630 | 704 | |
| 631 | 705 | let header = f.create_block("header"); |
| 632 | 706 | let i_param = f.next_value_id(); |
@@ -685,7 +759,10 @@ mod tests { | ||
| 685 | 759 | |
| 686 | 760 | m.add_function(f); |
| 687 | 761 | |
| 688 | - assert!(Licm.run(&mut m), "LICM should hoist the invariant dummy-arg load"); | |
| 762 | + assert!( | |
| 763 | + Licm.run(&mut m), | |
| 764 | + "LICM should hoist the invariant dummy-arg load" | |
| 765 | + ); | |
| 689 | 766 | |
| 690 | 767 | let f = &m.functions[0]; |
| 691 | 768 | let entry_block = f.block(f.entry); |
@@ -721,16 +798,22 @@ mod tests { | ||
| 721 | 798 | let init = f.next_value_id(); |
| 722 | 799 | let entry = f.entry; |
| 723 | 800 | f.block_mut(entry).insts.push(Inst { |
| 724 | - id: a, kind: InstKind::ConstInt(3, IntWidth::I32), | |
| 725 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 801 | + id: a, | |
| 802 | + kind: InstKind::ConstInt(3, IntWidth::I32), | |
| 803 | + ty: IrType::Int(IntWidth::I32), | |
| 804 | + span: dummy_span(), | |
| 726 | 805 | }); |
| 727 | 806 | f.block_mut(entry).insts.push(Inst { |
| 728 | - id: bv, kind: InstKind::ConstInt(4, IntWidth::I32), | |
| 729 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 807 | + id: bv, | |
| 808 | + kind: InstKind::ConstInt(4, IntWidth::I32), | |
| 809 | + ty: IrType::Int(IntWidth::I32), | |
| 810 | + span: dummy_span(), | |
| 730 | 811 | }); |
| 731 | 812 | f.block_mut(entry).insts.push(Inst { |
| 732 | - id: init, kind: InstKind::ConstInt(0, IntWidth::I32), | |
| 733 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 813 | + id: init, | |
| 814 | + kind: InstKind::ConstInt(0, IntWidth::I32), | |
| 815 | + ty: IrType::Int(IntWidth::I32), | |
| 816 | + span: dummy_span(), | |
| 734 | 817 | }); |
| 735 | 818 | |
| 736 | 819 | // Header: i_param, then `prod = a * b` (invariant), then |
@@ -738,44 +821,58 @@ mod tests { | ||
| 738 | 821 | let header = f.create_block("header"); |
| 739 | 822 | let i_param = f.next_value_id(); |
| 740 | 823 | f.block_mut(header).params.push(BlockParam { |
| 741 | - id: i_param, ty: IrType::Int(IntWidth::I32), | |
| 824 | + id: i_param, | |
| 825 | + ty: IrType::Int(IntWidth::I32), | |
| 742 | 826 | }); |
| 743 | 827 | let prod = f.next_value_id(); |
| 744 | 828 | let tmp = f.next_value_id(); |
| 745 | 829 | let limit = f.next_value_id(); |
| 746 | 830 | let done = f.next_value_id(); |
| 747 | 831 | f.block_mut(header).insts.push(Inst { |
| 748 | - id: prod, kind: InstKind::IMul(a, bv), | |
| 749 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 832 | + id: prod, | |
| 833 | + kind: InstKind::IMul(a, bv), | |
| 834 | + ty: IrType::Int(IntWidth::I32), | |
| 835 | + span: dummy_span(), | |
| 750 | 836 | }); |
| 751 | 837 | f.block_mut(header).insts.push(Inst { |
| 752 | - id: tmp, kind: InstKind::IAdd(i_param, prod), | |
| 753 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 838 | + id: tmp, | |
| 839 | + kind: InstKind::IAdd(i_param, prod), | |
| 840 | + ty: IrType::Int(IntWidth::I32), | |
| 841 | + span: dummy_span(), | |
| 754 | 842 | }); |
| 755 | 843 | f.block_mut(header).insts.push(Inst { |
| 756 | - id: limit, kind: InstKind::ConstInt(10, IntWidth::I32), | |
| 757 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 844 | + id: limit, | |
| 845 | + kind: InstKind::ConstInt(10, IntWidth::I32), | |
| 846 | + ty: IrType::Int(IntWidth::I32), | |
| 847 | + span: dummy_span(), | |
| 758 | 848 | }); |
| 759 | 849 | f.block_mut(header).insts.push(Inst { |
| 760 | - id: done, kind: InstKind::ICmp(CmpOp::Ge, i_param, limit), | |
| 761 | - ty: IrType::Bool, span: dummy_span(), | |
| 850 | + id: done, | |
| 851 | + kind: InstKind::ICmp(CmpOp::Ge, i_param, limit), | |
| 852 | + ty: IrType::Bool, | |
| 853 | + span: dummy_span(), | |
| 762 | 854 | }); |
| 763 | 855 | |
| 764 | 856 | // Latch: increment i and loop back. |
| 765 | 857 | let latch = f.create_block("latch"); |
| 766 | 858 | let i_in = f.next_value_id(); |
| 767 | 859 | f.block_mut(latch).params.push(BlockParam { |
| 768 | - id: i_in, ty: IrType::Int(IntWidth::I32), | |
| 860 | + id: i_in, | |
| 861 | + ty: IrType::Int(IntWidth::I32), | |
| 769 | 862 | }); |
| 770 | 863 | let one = f.next_value_id(); |
| 771 | 864 | let next = f.next_value_id(); |
| 772 | 865 | f.block_mut(latch).insts.push(Inst { |
| 773 | - id: one, kind: InstKind::ConstInt(1, IntWidth::I32), | |
| 774 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 866 | + id: one, | |
| 867 | + kind: InstKind::ConstInt(1, IntWidth::I32), | |
| 868 | + ty: IrType::Int(IntWidth::I32), | |
| 869 | + span: dummy_span(), | |
| 775 | 870 | }); |
| 776 | 871 | f.block_mut(latch).insts.push(Inst { |
| 777 | - id: next, kind: InstKind::IAdd(i_in, one), | |
| 778 | - ty: IrType::Int(IntWidth::I32), span: dummy_span(), | |
| 872 | + id: next, | |
| 873 | + kind: InstKind::IAdd(i_in, one), | |
| 874 | + ty: IrType::Int(IntWidth::I32), | |
| 875 | + span: dummy_span(), | |
| 779 | 876 | }); |
| 780 | 877 | f.block_mut(latch).terminator = Some(Terminator::Branch(header, vec![next])); |
| 781 | 878 | |
@@ -785,8 +882,10 @@ mod tests { | ||
| 785 | 882 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![init])); |
| 786 | 883 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 787 | 884 | cond: done, |
| 788 | - true_dest: exit, true_args: vec![], | |
| 789 | - false_dest: latch, false_args: vec![i_param], | |
| 885 | + true_dest: exit, | |
| 886 | + true_args: vec![], | |
| 887 | + false_dest: latch, | |
| 888 | + false_args: vec![i_param], | |
| 790 | 889 | }); |
| 791 | 890 | |
| 792 | 891 | m.add_function(f); |
@@ -797,23 +896,35 @@ mod tests { | ||
| 797 | 896 | // The IMul must have moved out of the header into the entry |
| 798 | 897 | // (the natural preheader). |
| 799 | 898 | let header_block = f.block(header); |
| 800 | - let imul_in_header = header_block.insts.iter() | |
| 899 | + let imul_in_header = header_block | |
| 900 | + .insts | |
| 901 | + .iter() | |
| 801 | 902 | .any(|i| matches!(i.kind, InstKind::IMul(..))); |
| 802 | - assert!(!imul_in_header, | |
| 903 | + assert!( | |
| 904 | + !imul_in_header, | |
| 803 | 905 | "invariant IMul should be hoisted out of the header: {:?}", |
| 804 | - header_block.insts); | |
| 906 | + header_block.insts | |
| 907 | + ); | |
| 805 | 908 | |
| 806 | 909 | let entry_block = f.block(f.entry); |
| 807 | - let imul_in_entry = entry_block.insts.iter() | |
| 910 | + let imul_in_entry = entry_block | |
| 911 | + .insts | |
| 912 | + .iter() | |
| 808 | 913 | .any(|i| matches!(i.kind, InstKind::IMul(..))); |
| 809 | - assert!(imul_in_entry, | |
| 914 | + assert!( | |
| 915 | + imul_in_entry, | |
| 810 | 916 | "invariant IMul should land in entry/preheader after hoist: {:?}", |
| 811 | - entry_block.insts); | |
| 917 | + entry_block.insts | |
| 918 | + ); | |
| 812 | 919 | |
| 813 | 920 | // The loop-dependent IAdd must NOT have moved. |
| 814 | - let iadd_in_header = header_block.insts.iter() | |
| 921 | + let iadd_in_header = header_block | |
| 922 | + .insts | |
| 923 | + .iter() | |
| 815 | 924 | .any(|i| matches!(i.kind, InstKind::IAdd(_, _))); |
| 816 | - assert!(iadd_in_header, | |
| 817 | - "loop-dependent IAdd(i_param, prod) should still be in the header"); | |
| 925 | + assert!( | |
| 926 | + iadd_in_header, | |
| 927 | + "loop-dependent IAdd(i_param, prod) should still be in the header" | |
| 928 | + ); | |
| 818 | 929 | } |
| 819 | 930 | } |
src/opt/loop_tree.rsmodified@@ -5,9 +5,9 @@ | ||
| 5 | 5 | //! parent, and children, so passes like interchange can find |
| 6 | 6 | //! perfectly-nested pairs and unswitching can target innermost loops. |
| 7 | 7 | |
| 8 | -use std::collections::{HashMap, HashSet}; | |
| 9 | 8 | use crate::ir::inst::{BlockId, Function}; |
| 10 | 9 | use crate::ir::walk::find_natural_loops; |
| 10 | +use std::collections::{HashMap, HashSet}; | |
| 11 | 11 | |
| 12 | 12 | /// Unique identifier for a loop in the tree. |
| 13 | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -37,7 +37,8 @@ pub struct LoopTree { | ||
| 37 | 37 | impl LoopTree { |
| 38 | 38 | /// Return the IDs of all innermost (leaf) loops — loops with no children. |
| 39 | 39 | pub fn innermost_loops(&self) -> Vec<LoopId> { |
| 40 | - self.nodes.iter() | |
| 40 | + self.nodes | |
| 41 | + .iter() | |
| 41 | 42 | .filter(|n| n.children.is_empty()) |
| 42 | 43 | .map(|n| n.id) |
| 43 | 44 | .collect() |
@@ -50,7 +51,8 @@ impl LoopTree { | ||
| 50 | 51 | |
| 51 | 52 | /// Nesting depth of a block (0 if not in any loop). |
| 52 | 53 | pub fn loop_depth(&self, block: BlockId) -> u32 { |
| 53 | - self.block_to_loop.get(&block) | |
| 54 | + self.block_to_loop | |
| 55 | + .get(&block) | |
| 54 | 56 | .map(|lid| self.node(*lid).depth) |
| 55 | 57 | .unwrap_or(0) |
| 56 | 58 | } |
@@ -67,7 +69,9 @@ impl LoopTree { | ||
| 67 | 69 | pub fn perfectly_nested_pairs(&self, func: &Function) -> Vec<(LoopId, LoopId)> { |
| 68 | 70 | let mut pairs = Vec::new(); |
| 69 | 71 | for node in &self.nodes { |
| 70 | - if node.children.len() != 1 { continue; } | |
| 72 | + if node.children.len() != 1 { | |
| 73 | + continue; | |
| 74 | + } | |
| 71 | 75 | let child_id = node.children[0]; |
| 72 | 76 | let child = self.node(child_id); |
| 73 | 77 | |
@@ -78,16 +82,17 @@ impl LoopTree { | ||
| 78 | 82 | // - the outer latch (increment + branch) |
| 79 | 83 | // - the outer "body" block that just branches to the inner preheader |
| 80 | 84 | // Everything else must be part of the inner loop. |
| 81 | - let outer_only: Vec<BlockId> = node.body.iter() | |
| 85 | + let outer_only: Vec<BlockId> = node | |
| 86 | + .body | |
| 87 | + .iter() | |
| 82 | 88 | .filter(|b| !child.body.contains(b)) |
| 83 | 89 | .copied() |
| 84 | 90 | .collect(); |
| 85 | 91 | |
| 86 | 92 | // Conservative check: outer-only blocks should have very few |
| 87 | 93 | // instructions total (header relay + cmp + latch + body-entry). |
| 88 | - let total_outer_insts: usize = outer_only.iter() | |
| 89 | - .map(|&b| func.block(b).insts.len()) | |
| 90 | - .sum(); | |
| 94 | + let total_outer_insts: usize = | |
| 95 | + outer_only.iter().map(|&b| func.block(b).insts.len()).sum(); | |
| 91 | 96 | |
| 92 | 97 | // A typical Fortran DO nest has ~4-6 instructions in the |
| 93 | 98 | // outer shell (const bound, icmp, iadd). Allow up to 10 |
@@ -120,8 +125,10 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 120 | 125 | indexed.sort_by(|a, b| b.1.body.len().cmp(&a.1.body.len())); |
| 121 | 126 | |
| 122 | 127 | // Build nodes with stable IDs (original discovery order). |
| 123 | - let mut nodes: Vec<LoopTreeNode> = natural.iter().enumerate().map(|(i, nl)| { | |
| 124 | - LoopTreeNode { | |
| 128 | + let mut nodes: Vec<LoopTreeNode> = natural | |
| 129 | + .iter() | |
| 130 | + .enumerate() | |
| 131 | + .map(|(i, nl)| LoopTreeNode { | |
| 125 | 132 | id: LoopId(i as u32), |
| 126 | 133 | header: nl.header, |
| 127 | 134 | body: nl.body.clone(), |
@@ -129,8 +136,8 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 129 | 136 | parent: None, |
| 130 | 137 | children: Vec::new(), |
| 131 | 138 | depth: 0, |
| 132 | - } | |
| 133 | - }).collect(); | |
| 139 | + }) | |
| 140 | + .collect(); | |
| 134 | 141 | |
| 135 | 142 | // For each loop, find its parent = the smallest loop that strictly |
| 136 | 143 | // contains it. We iterate in body-size order so we can check |
@@ -140,7 +147,9 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 140 | 147 | let mut best_parent: Option<LoopId> = None; |
| 141 | 148 | let mut best_size = usize::MAX; |
| 142 | 149 | for j in 0..n { |
| 143 | - if i == j { continue; } | |
| 150 | + if i == j { | |
| 151 | + continue; | |
| 152 | + } | |
| 144 | 153 | // j is a candidate parent if j's body strictly contains i's body. |
| 145 | 154 | if nodes[j].body.len() > nodes[i].body.len() |
| 146 | 155 | && nodes[i].body.is_subset(&nodes[j].body) |
@@ -164,11 +173,17 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 164 | 173 | // Sort children by header for determinism. |
| 165 | 174 | // (Two-step to satisfy the borrow checker: collect sort keys, then sort.) |
| 166 | 175 | for i in 0..n { |
| 167 | - let headers: Vec<(LoopId, u32)> = nodes[i].children.iter() | |
| 176 | + let headers: Vec<(LoopId, u32)> = nodes[i] | |
| 177 | + .children | |
| 178 | + .iter() | |
| 168 | 179 | .map(|c| (*c, nodes[c.0 as usize].header.0)) |
| 169 | 180 | .collect(); |
| 170 | 181 | nodes[i].children.sort_by_key(|c| { |
| 171 | - headers.iter().find(|(id, _)| id == c).map(|(_, h)| *h).unwrap_or(0) | |
| 182 | + headers | |
| 183 | + .iter() | |
| 184 | + .find(|(id, _)| id == c) | |
| 185 | + .map(|(_, h)| *h) | |
| 186 | + .unwrap_or(0) | |
| 172 | 187 | }); |
| 173 | 188 | } |
| 174 | 189 | |
@@ -189,9 +204,7 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 189 | 204 | // Build block → innermost loop mapping. |
| 190 | 205 | // Process innermost (deepest) loops last so they overwrite parents. |
| 191 | 206 | let mut block_to_loop: HashMap<BlockId, LoopId> = HashMap::new(); |
| 192 | - let mut by_depth: Vec<(u32, LoopId)> = nodes.iter() | |
| 193 | - .map(|n| (n.depth, n.id)) | |
| 194 | - .collect(); | |
| 207 | + let mut by_depth: Vec<(u32, LoopId)> = nodes.iter().map(|n| (n.depth, n.id)).collect(); | |
| 195 | 208 | by_depth.sort_by_key(|(d, _)| *d); |
| 196 | 209 | for (_, lid) in by_depth { |
| 197 | 210 | for &block in &nodes[lid.0 as usize].body { |
@@ -199,7 +212,10 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 199 | 212 | } |
| 200 | 213 | } |
| 201 | 214 | |
| 202 | - LoopTree { nodes, block_to_loop } | |
| 215 | + LoopTree { | |
| 216 | + nodes, | |
| 217 | + block_to_loop, | |
| 218 | + } | |
| 203 | 219 | } |
| 204 | 220 | |
| 205 | 221 | // --------------------------------------------------------------------------- |
@@ -209,13 +225,17 @@ pub fn build_loop_tree(func: &Function) -> LoopTree { | ||
| 209 | 225 | #[cfg(test)] |
| 210 | 226 | mod tests { |
| 211 | 227 | use super::*; |
| 212 | - use crate::ir::types::{IrType, IntWidth}; | |
| 213 | 228 | use crate::ir::inst::*; |
| 214 | - use crate::lexer::{Span, Position}; | |
| 229 | + use crate::ir::types::{IntWidth, IrType}; | |
| 230 | + use crate::lexer::{Position, Span}; | |
| 215 | 231 | |
| 216 | 232 | fn span() -> Span { |
| 217 | 233 | let pos = Position { line: 0, col: 0 }; |
| 218 | - Span { file_id: 0, start: pos, end: pos } | |
| 234 | + Span { | |
| 235 | + file_id: 0, | |
| 236 | + start: pos, | |
| 237 | + end: pos, | |
| 238 | + } | |
| 219 | 239 | } |
| 220 | 240 | |
| 221 | 241 | /// Build a function with 3-level nested DO loops: |
@@ -228,17 +248,17 @@ mod tests { | ||
| 228 | 248 | let mut f = Function::new("triple".into(), vec![], IrType::Void); |
| 229 | 249 | |
| 230 | 250 | // Create all blocks upfront. |
| 231 | - let outer_hdr = f.create_block("outer_hdr"); | |
| 232 | - let outer_cmp = f.create_block("outer_cmp"); | |
| 233 | - let inner_hdr = f.create_block("inner_hdr"); | |
| 234 | - let inner_cmp = f.create_block("inner_cmp"); | |
| 235 | - let deep_hdr = f.create_block("deep_hdr"); | |
| 236 | - let deep_cmp = f.create_block("deep_cmp"); | |
| 237 | - let body = f.create_block("body"); | |
| 238 | - let deep_latch = f.create_block("deep_latch"); | |
| 251 | + let outer_hdr = f.create_block("outer_hdr"); | |
| 252 | + let outer_cmp = f.create_block("outer_cmp"); | |
| 253 | + let inner_hdr = f.create_block("inner_hdr"); | |
| 254 | + let inner_cmp = f.create_block("inner_cmp"); | |
| 255 | + let deep_hdr = f.create_block("deep_hdr"); | |
| 256 | + let deep_cmp = f.create_block("deep_cmp"); | |
| 257 | + let body = f.create_block("body"); | |
| 258 | + let deep_latch = f.create_block("deep_latch"); | |
| 239 | 259 | let inner_latch = f.create_block("inner_latch"); |
| 240 | 260 | let outer_latch = f.create_block("outer_latch"); |
| 241 | - let exit = f.create_block("exit"); | |
| 261 | + let exit = f.create_block("exit"); | |
| 242 | 262 | |
| 243 | 263 | let entry = f.entry; |
| 244 | 264 | |
@@ -246,53 +266,72 @@ mod tests { | ||
| 246 | 266 | let one = f.next_value_id(); |
| 247 | 267 | f.register_type(one, IrType::Int(IntWidth::I32)); |
| 248 | 268 | f.block_mut(entry).insts.push(Inst { |
| 249 | - id: one, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 269 | + id: one, | |
| 270 | + ty: IrType::Int(IntWidth::I32), | |
| 271 | + span: span(), | |
| 250 | 272 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 251 | 273 | }); |
| 252 | 274 | f.block_mut(entry).terminator = Some(Terminator::Branch(outer_hdr, vec![one])); |
| 253 | 275 | |
| 254 | 276 | // Helper: add a simple loop level (header with param → cmp → condBr) |
| 255 | 277 | fn add_loop_level( |
| 256 | - f: &mut Function, hdr: BlockId, cmp: BlockId, | |
| 257 | - body_target: BlockId, exit_target: BlockId, | |
| 258 | - latch: BlockId, init_src: ValueId, | |
| 278 | + f: &mut Function, | |
| 279 | + hdr: BlockId, | |
| 280 | + cmp: BlockId, | |
| 281 | + body_target: BlockId, | |
| 282 | + exit_target: BlockId, | |
| 283 | + latch: BlockId, | |
| 284 | + init_src: ValueId, | |
| 259 | 285 | ) -> (ValueId, ValueId) { |
| 260 | 286 | // Header: block param |
| 261 | 287 | let iv = f.next_value_id(); |
| 262 | 288 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 263 | - f.block_mut(hdr).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 289 | + f.block_mut(hdr).params.push(BlockParam { | |
| 290 | + id: iv, | |
| 291 | + ty: IrType::Int(IntWidth::I32), | |
| 292 | + }); | |
| 264 | 293 | f.block_mut(hdr).terminator = Some(Terminator::Branch(cmp, vec![])); |
| 265 | 294 | |
| 266 | 295 | // Cmp: icmp le iv, 10; condBr body, exit |
| 267 | 296 | let bound = f.next_value_id(); |
| 268 | 297 | f.register_type(bound, IrType::Int(IntWidth::I32)); |
| 269 | 298 | f.block_mut(cmp).insts.push(Inst { |
| 270 | - id: bound, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 299 | + id: bound, | |
| 300 | + ty: IrType::Int(IntWidth::I32), | |
| 301 | + span: span(), | |
| 271 | 302 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 272 | 303 | }); |
| 273 | 304 | let cmp_val = f.next_value_id(); |
| 274 | 305 | f.register_type(cmp_val, IrType::Bool); |
| 275 | 306 | f.block_mut(cmp).insts.push(Inst { |
| 276 | - id: cmp_val, ty: IrType::Bool, span: span(), | |
| 307 | + id: cmp_val, | |
| 308 | + ty: IrType::Bool, | |
| 309 | + span: span(), | |
| 277 | 310 | kind: InstKind::ICmp(CmpOp::Le, iv, bound), |
| 278 | 311 | }); |
| 279 | 312 | f.block_mut(cmp).terminator = Some(Terminator::CondBranch { |
| 280 | 313 | cond: cmp_val, |
| 281 | - true_dest: body_target, true_args: vec![], | |
| 282 | - false_dest: exit_target, false_args: vec![], | |
| 314 | + true_dest: body_target, | |
| 315 | + true_args: vec![], | |
| 316 | + false_dest: exit_target, | |
| 317 | + false_args: vec![], | |
| 283 | 318 | }); |
| 284 | 319 | |
| 285 | 320 | // Latch: iadd iv, 1; br hdr(next) |
| 286 | 321 | let one_l = f.next_value_id(); |
| 287 | 322 | f.register_type(one_l, IrType::Int(IntWidth::I32)); |
| 288 | 323 | f.block_mut(latch).insts.push(Inst { |
| 289 | - id: one_l, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 324 | + id: one_l, | |
| 325 | + ty: IrType::Int(IntWidth::I32), | |
| 326 | + span: span(), | |
| 290 | 327 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 291 | 328 | }); |
| 292 | 329 | let next = f.next_value_id(); |
| 293 | 330 | f.register_type(next, IrType::Int(IntWidth::I32)); |
| 294 | 331 | f.block_mut(latch).insts.push(Inst { |
| 295 | - id: next, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 332 | + id: next, | |
| 333 | + ty: IrType::Int(IntWidth::I32), | |
| 334 | + span: span(), | |
| 296 | 335 | kind: InstKind::IAdd(iv, one_l), |
| 297 | 336 | }); |
| 298 | 337 | f.block_mut(latch).terminator = Some(Terminator::Branch(hdr, vec![next])); |
@@ -302,15 +341,33 @@ mod tests { | ||
| 302 | 341 | } |
| 303 | 342 | |
| 304 | 343 | // Outer loop: entry→outer_hdr(1)→outer_cmp→inner_hdr/exit |
| 305 | - let (_, _) = add_loop_level(&mut f, outer_hdr, outer_cmp, inner_hdr, exit, outer_latch, one); | |
| 344 | + let (_, _) = add_loop_level( | |
| 345 | + &mut f, | |
| 346 | + outer_hdr, | |
| 347 | + outer_cmp, | |
| 348 | + inner_hdr, | |
| 349 | + exit, | |
| 350 | + outer_latch, | |
| 351 | + one, | |
| 352 | + ); | |
| 306 | 353 | // Wire outer_cmp's true branch to pass `one` to inner_hdr |
| 307 | 354 | // (inner loop starts at 1 each outer iteration) |
| 308 | - if let Some(Terminator::CondBranch { true_args, .. }) = &mut f.block_mut(outer_cmp).terminator { | |
| 355 | + if let Some(Terminator::CondBranch { true_args, .. }) = | |
| 356 | + &mut f.block_mut(outer_cmp).terminator | |
| 357 | + { | |
| 309 | 358 | true_args.push(one); |
| 310 | 359 | } |
| 311 | 360 | |
| 312 | 361 | // Inner loop |
| 313 | - let (_, _) = add_loop_level(&mut f, inner_hdr, inner_cmp, deep_hdr, inner_latch, inner_latch, one); | |
| 362 | + let (_, _) = add_loop_level( | |
| 363 | + &mut f, | |
| 364 | + inner_hdr, | |
| 365 | + inner_cmp, | |
| 366 | + deep_hdr, | |
| 367 | + inner_latch, | |
| 368 | + inner_latch, | |
| 369 | + one, | |
| 370 | + ); | |
| 314 | 371 | // Inner latch needs to go to outer_latch after the inner loop exits... |
| 315 | 372 | // Actually inner_latch IS the inner latch (iadd + br inner_hdr). The exit |
| 316 | 373 | // of the inner loop goes to outer_latch. Let me fix: inner_cmp's false goes to outer_latch. |
@@ -331,13 +388,17 @@ mod tests { | ||
| 331 | 388 | let c1 = f.next_value_id(); |
| 332 | 389 | f.register_type(c1, IrType::Int(IntWidth::I32)); |
| 333 | 390 | f.block_mut(entry).insts.push(Inst { |
| 334 | - id: c1, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 391 | + id: c1, | |
| 392 | + ty: IrType::Int(IntWidth::I32), | |
| 393 | + span: span(), | |
| 335 | 394 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 336 | 395 | }); |
| 337 | 396 | let c10 = f.next_value_id(); |
| 338 | 397 | f.register_type(c10, IrType::Int(IntWidth::I32)); |
| 339 | 398 | f.block_mut(entry).insts.push(Inst { |
| 340 | - id: c10, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 399 | + id: c10, | |
| 400 | + ty: IrType::Int(IntWidth::I32), | |
| 401 | + span: span(), | |
| 341 | 402 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 342 | 403 | }); |
| 343 | 404 | f.block_mut(entry).terminator = Some(Terminator::Branch(outer_hdr, vec![c1])); |
@@ -347,7 +408,10 @@ mod tests { | ||
| 347 | 408 | ($f:expr, $hdr:expr, $cmp:expr) => {{ |
| 348 | 409 | let iv = $f.next_value_id(); |
| 349 | 410 | $f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 350 | - $f.block_mut($hdr).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 411 | + $f.block_mut($hdr).params.push(BlockParam { | |
| 412 | + id: iv, | |
| 413 | + ty: IrType::Int(IntWidth::I32), | |
| 414 | + }); | |
| 351 | 415 | $f.block_mut($hdr).terminator = Some(Terminator::Branch($cmp, vec![])); |
| 352 | 416 | iv |
| 353 | 417 | }}; |
@@ -357,12 +421,17 @@ mod tests { | ||
| 357 | 421 | let cv = $f.next_value_id(); |
| 358 | 422 | $f.register_type(cv, IrType::Bool); |
| 359 | 423 | $f.block_mut($cmp).insts.push(Inst { |
| 360 | - id: cv, ty: IrType::Bool, span: span(), | |
| 424 | + id: cv, | |
| 425 | + ty: IrType::Bool, | |
| 426 | + span: span(), | |
| 361 | 427 | kind: InstKind::ICmp(CmpOp::Le, $iv, $bound), |
| 362 | 428 | }); |
| 363 | 429 | $f.block_mut($cmp).terminator = Some(Terminator::CondBranch { |
| 364 | - cond: cv, true_dest: $t, true_args: $t_args, | |
| 365 | - false_dest: $fal, false_args: vec![], | |
| 430 | + cond: cv, | |
| 431 | + true_dest: $t, | |
| 432 | + true_args: $t_args, | |
| 433 | + false_dest: $fal, | |
| 434 | + false_args: vec![], | |
| 366 | 435 | }); |
| 367 | 436 | }}; |
| 368 | 437 | } |
@@ -371,7 +440,9 @@ mod tests { | ||
| 371 | 440 | let nxt = $f.next_value_id(); |
| 372 | 441 | $f.register_type(nxt, IrType::Int(IntWidth::I32)); |
| 373 | 442 | $f.block_mut($latch).insts.push(Inst { |
| 374 | - id: nxt, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 443 | + id: nxt, | |
| 444 | + ty: IrType::Int(IntWidth::I32), | |
| 445 | + span: span(), | |
| 375 | 446 | kind: InstKind::IAdd($iv, $one), |
| 376 | 447 | }); |
| 377 | 448 | $f.block_mut($latch).terminator = Some(Terminator::Branch($hdr, vec![nxt])); |
@@ -442,7 +513,11 @@ mod tests { | ||
| 442 | 513 | let tree = build_loop_tree(&f); |
| 443 | 514 | |
| 444 | 515 | // The body block should map to the innermost loop. |
| 445 | - let body_block = f.blocks.iter().find(|b| b.name.starts_with("body")).unwrap(); | |
| 516 | + let body_block = f | |
| 517 | + .blocks | |
| 518 | + .iter() | |
| 519 | + .find(|b| b.name.starts_with("body")) | |
| 520 | + .unwrap(); | |
| 446 | 521 | let mapped = tree.block_to_loop.get(&body_block.id); |
| 447 | 522 | assert!(mapped.is_some(), "body block should be in a loop"); |
| 448 | 523 | let mapped_node = tree.node(*mapped.unwrap()); |
src/opt/loop_utils.rsmodified@@ -4,9 +4,9 @@ | ||
| 4 | 4 | //! (from unroll.rs) into public shared functions so all loop passes |
| 5 | 5 | //! use the same logic. |
| 6 | 6 | |
| 7 | -use std::collections::{HashMap, HashSet}; | |
| 8 | 7 | use crate::ir::inst::*; |
| 9 | 8 | use crate::ir::walk::NaturalLoop; |
| 9 | +use std::collections::{HashMap, HashSet}; | |
| 10 | 10 | |
| 11 | 11 | /// Find the unique preheader for a natural loop, if one exists. |
| 12 | 12 | /// |
@@ -28,9 +28,13 @@ pub fn find_preheader( | ||
| 28 | 28 | .collect(); |
| 29 | 29 | outside.sort_by_key(|b| b.0); |
| 30 | 30 | outside.dedup(); |
| 31 | - if outside.len() != 1 { return None; } | |
| 31 | + if outside.len() != 1 { | |
| 32 | + return None; | |
| 33 | + } | |
| 32 | 34 | let ph = outside[0]; |
| 33 | - if ph == lp.header { return None; } | |
| 35 | + if ph == lp.header { | |
| 36 | + return None; | |
| 37 | + } | |
| 34 | 38 | let ph_block = func.block(ph); |
| 35 | 39 | match &ph_block.terminator { |
| 36 | 40 | Some(Terminator::Branch(dest, _)) if *dest == lp.header => Some(ph), |
@@ -60,8 +64,12 @@ pub fn loop_defined_values(func: &Function, lp: &NaturalLoop) -> HashSet<ValueId | ||
| 60 | 64 | let mut defs = HashSet::new(); |
| 61 | 65 | for &bid in &lp.body { |
| 62 | 66 | let block = func.block(bid); |
| 63 | - for bp in &block.params { defs.insert(bp.id); } | |
| 64 | - for inst in &block.insts { defs.insert(inst.id); } | |
| 67 | + for bp in &block.params { | |
| 68 | + defs.insert(bp.id); | |
| 69 | + } | |
| 70 | + for inst in &block.insts { | |
| 71 | + defs.insert(inst.id); | |
| 72 | + } | |
| 65 | 73 | } |
| 66 | 74 | defs |
| 67 | 75 | } |
@@ -167,60 +175,61 @@ pub fn build_value_map( | ||
| 167 | 175 | pub fn remap_inst_kind(kind: &InstKind, map: &HashMap<ValueId, ValueId>) -> InstKind { |
| 168 | 176 | let r = |v: &ValueId| *map.get(v).unwrap_or(v); |
| 169 | 177 | match kind { |
| 170 | - InstKind::ConstInt(v, w) => InstKind::ConstInt(*v, *w), | |
| 171 | - InstKind::ConstFloat(v, w) => InstKind::ConstFloat(*v, *w), | |
| 172 | - InstKind::ConstBool(v) => InstKind::ConstBool(*v), | |
| 173 | - InstKind::ConstString(v) => InstKind::ConstString(v.clone()), | |
| 174 | - InstKind::Undef(t) => InstKind::Undef(t.clone()), | |
| 175 | - InstKind::GlobalAddr(s) => InstKind::GlobalAddr(s.clone()), | |
| 176 | - InstKind::IAdd(a, b) => InstKind::IAdd(r(a), r(b)), | |
| 177 | - InstKind::ISub(a, b) => InstKind::ISub(r(a), r(b)), | |
| 178 | - InstKind::IMul(a, b) => InstKind::IMul(r(a), r(b)), | |
| 179 | - InstKind::IDiv(a, b) => InstKind::IDiv(r(a), r(b)), | |
| 180 | - InstKind::IMod(a, b) => InstKind::IMod(r(a), r(b)), | |
| 181 | - InstKind::INeg(a) => InstKind::INeg(r(a)), | |
| 182 | - InstKind::FAdd(a, b) => InstKind::FAdd(r(a), r(b)), | |
| 183 | - InstKind::FSub(a, b) => InstKind::FSub(r(a), r(b)), | |
| 184 | - InstKind::FMul(a, b) => InstKind::FMul(r(a), r(b)), | |
| 185 | - InstKind::FDiv(a, b) => InstKind::FDiv(r(a), r(b)), | |
| 186 | - InstKind::FNeg(a) => InstKind::FNeg(r(a)), | |
| 187 | - InstKind::FAbs(a) => InstKind::FAbs(r(a)), | |
| 188 | - InstKind::FSqrt(a) => InstKind::FSqrt(r(a)), | |
| 189 | - InstKind::FPow(a, b) => InstKind::FPow(r(a), r(b)), | |
| 178 | + InstKind::ConstInt(v, w) => InstKind::ConstInt(*v, *w), | |
| 179 | + InstKind::ConstFloat(v, w) => InstKind::ConstFloat(*v, *w), | |
| 180 | + InstKind::ConstBool(v) => InstKind::ConstBool(*v), | |
| 181 | + InstKind::ConstString(v) => InstKind::ConstString(v.clone()), | |
| 182 | + InstKind::Undef(t) => InstKind::Undef(t.clone()), | |
| 183 | + InstKind::GlobalAddr(s) => InstKind::GlobalAddr(s.clone()), | |
| 184 | + InstKind::IAdd(a, b) => InstKind::IAdd(r(a), r(b)), | |
| 185 | + InstKind::ISub(a, b) => InstKind::ISub(r(a), r(b)), | |
| 186 | + InstKind::IMul(a, b) => InstKind::IMul(r(a), r(b)), | |
| 187 | + InstKind::IDiv(a, b) => InstKind::IDiv(r(a), r(b)), | |
| 188 | + InstKind::IMod(a, b) => InstKind::IMod(r(a), r(b)), | |
| 189 | + InstKind::INeg(a) => InstKind::INeg(r(a)), | |
| 190 | + InstKind::FAdd(a, b) => InstKind::FAdd(r(a), r(b)), | |
| 191 | + InstKind::FSub(a, b) => InstKind::FSub(r(a), r(b)), | |
| 192 | + InstKind::FMul(a, b) => InstKind::FMul(r(a), r(b)), | |
| 193 | + InstKind::FDiv(a, b) => InstKind::FDiv(r(a), r(b)), | |
| 194 | + InstKind::FNeg(a) => InstKind::FNeg(r(a)), | |
| 195 | + InstKind::FAbs(a) => InstKind::FAbs(r(a)), | |
| 196 | + InstKind::FSqrt(a) => InstKind::FSqrt(r(a)), | |
| 197 | + InstKind::FPow(a, b) => InstKind::FPow(r(a), r(b)), | |
| 190 | 198 | InstKind::ICmp(op, a, b) => InstKind::ICmp(*op, r(a), r(b)), |
| 191 | 199 | InstKind::FCmp(op, a, b) => InstKind::FCmp(*op, r(a), r(b)), |
| 192 | 200 | InstKind::And(a, b) => InstKind::And(r(a), r(b)), |
| 193 | - InstKind::Or(a, b) => InstKind::Or(r(a), r(b)), | |
| 194 | - InstKind::Not(a) => InstKind::Not(r(a)), | |
| 201 | + InstKind::Or(a, b) => InstKind::Or(r(a), r(b)), | |
| 202 | + InstKind::Not(a) => InstKind::Not(r(a)), | |
| 195 | 203 | InstKind::Select(c, t, f) => InstKind::Select(r(c), r(t), r(f)), |
| 196 | - InstKind::BitAnd(a, b) => InstKind::BitAnd(r(a), r(b)), | |
| 197 | - InstKind::BitOr(a, b) => InstKind::BitOr(r(a), r(b)), | |
| 198 | - InstKind::BitXor(a, b) => InstKind::BitXor(r(a), r(b)), | |
| 199 | - InstKind::BitNot(a) => InstKind::BitNot(r(a)), | |
| 200 | - InstKind::Shl(a, b) => InstKind::Shl(r(a), r(b)), | |
| 201 | - InstKind::LShr(a, b) => InstKind::LShr(r(a), r(b)), | |
| 202 | - InstKind::AShr(a, b) => InstKind::AShr(r(a), r(b)), | |
| 203 | - InstKind::CountLeadingZeros(a) => InstKind::CountLeadingZeros(r(a)), | |
| 204 | - InstKind::CountTrailingZeros(a) => InstKind::CountTrailingZeros(r(a)), | |
| 205 | - InstKind::PopCount(a) => InstKind::PopCount(r(a)), | |
| 206 | - InstKind::IntToFloat(a, w) => InstKind::IntToFloat(r(a), *w), | |
| 207 | - InstKind::FloatToInt(a, w) => InstKind::FloatToInt(r(a), *w), | |
| 208 | - InstKind::FloatExtend(a, w) => InstKind::FloatExtend(r(a), *w), | |
| 209 | - InstKind::FloatTrunc(a, w) => InstKind::FloatTrunc(r(a), *w), | |
| 210 | - InstKind::IntExtend(a, w, s) => InstKind::IntExtend(r(a), *w, *s), | |
| 211 | - InstKind::IntTrunc(a, w) => InstKind::IntTrunc(r(a), *w), | |
| 212 | - InstKind::PtrToInt(a) => InstKind::PtrToInt(r(a)), | |
| 213 | - InstKind::IntToPtr(a, ty) => InstKind::IntToPtr(r(a), ty.clone()), | |
| 214 | - InstKind::Alloca(t) => InstKind::Alloca(t.clone()), | |
| 215 | - InstKind::Load(a) => InstKind::Load(r(a)), | |
| 204 | + InstKind::BitAnd(a, b) => InstKind::BitAnd(r(a), r(b)), | |
| 205 | + InstKind::BitOr(a, b) => InstKind::BitOr(r(a), r(b)), | |
| 206 | + InstKind::BitXor(a, b) => InstKind::BitXor(r(a), r(b)), | |
| 207 | + InstKind::BitNot(a) => InstKind::BitNot(r(a)), | |
| 208 | + InstKind::Shl(a, b) => InstKind::Shl(r(a), r(b)), | |
| 209 | + InstKind::LShr(a, b) => InstKind::LShr(r(a), r(b)), | |
| 210 | + InstKind::AShr(a, b) => InstKind::AShr(r(a), r(b)), | |
| 211 | + InstKind::CountLeadingZeros(a) => InstKind::CountLeadingZeros(r(a)), | |
| 212 | + InstKind::CountTrailingZeros(a) => InstKind::CountTrailingZeros(r(a)), | |
| 213 | + InstKind::PopCount(a) => InstKind::PopCount(r(a)), | |
| 214 | + InstKind::IntToFloat(a, w) => InstKind::IntToFloat(r(a), *w), | |
| 215 | + InstKind::FloatToInt(a, w) => InstKind::FloatToInt(r(a), *w), | |
| 216 | + InstKind::FloatExtend(a, w) => InstKind::FloatExtend(r(a), *w), | |
| 217 | + InstKind::FloatTrunc(a, w) => InstKind::FloatTrunc(r(a), *w), | |
| 218 | + InstKind::IntExtend(a, w, s) => InstKind::IntExtend(r(a), *w, *s), | |
| 219 | + InstKind::IntTrunc(a, w) => InstKind::IntTrunc(r(a), *w), | |
| 220 | + InstKind::PtrToInt(a) => InstKind::PtrToInt(r(a)), | |
| 221 | + InstKind::IntToPtr(a, ty) => InstKind::IntToPtr(r(a), ty.clone()), | |
| 222 | + InstKind::Alloca(t) => InstKind::Alloca(t.clone()), | |
| 223 | + InstKind::Load(a) => InstKind::Load(r(a)), | |
| 216 | 224 | InstKind::Store(v, p) => InstKind::Store(r(v), r(p)), |
| 217 | - InstKind::GetElementPtr(base, idxs) => | |
| 218 | - InstKind::GetElementPtr(r(base), idxs.iter().map(&r).collect()), | |
| 219 | - InstKind::Call(f, args) => | |
| 220 | - InstKind::Call(f.clone(), args.iter().map(&r).collect()), | |
| 221 | - InstKind::RuntimeCall(f, args) => | |
| 222 | - InstKind::RuntimeCall(f.clone(), args.iter().map(&r).collect()), | |
| 223 | - InstKind::ExtractField(v, idx) => InstKind::ExtractField(r(v), *idx), | |
| 225 | + InstKind::GetElementPtr(base, idxs) => { | |
| 226 | + InstKind::GetElementPtr(r(base), idxs.iter().map(&r).collect()) | |
| 227 | + } | |
| 228 | + InstKind::Call(f, args) => InstKind::Call(f.clone(), args.iter().map(&r).collect()), | |
| 229 | + InstKind::RuntimeCall(f, args) => { | |
| 230 | + InstKind::RuntimeCall(f.clone(), args.iter().map(&r).collect()) | |
| 231 | + } | |
| 232 | + InstKind::ExtractField(v, idx) => InstKind::ExtractField(r(v), *idx), | |
| 224 | 233 | InstKind::InsertField(v, idx, fld) => InstKind::InsertField(r(v), *idx, r(fld)), |
| 225 | 234 | } |
| 226 | 235 | } |
@@ -237,20 +246,28 @@ pub fn remap_terminator( | ||
| 237 | 246 | match term { |
| 238 | 247 | Terminator::Return(v) => Terminator::Return(v.map(|x| rv(&x))), |
| 239 | 248 | Terminator::Branch(dest, args) => Terminator::Branch(rb(dest), rvs(args)), |
| 240 | - Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args } => | |
| 241 | - Terminator::CondBranch { | |
| 242 | - cond: rv(cond), | |
| 243 | - true_dest: rb(true_dest), | |
| 244 | - true_args: rvs(true_args), | |
| 245 | - false_dest: rb(false_dest), | |
| 246 | - false_args: rvs(false_args), | |
| 247 | - }, | |
| 248 | - Terminator::Switch { selector, default, cases } => | |
| 249 | - Terminator::Switch { | |
| 250 | - selector: rv(selector), | |
| 251 | - default: rb(default), | |
| 252 | - cases: cases.iter().map(|(v, d)| (*v, rb(d))).collect(), | |
| 253 | - }, | |
| 249 | + Terminator::CondBranch { | |
| 250 | + cond, | |
| 251 | + true_dest, | |
| 252 | + true_args, | |
| 253 | + false_dest, | |
| 254 | + false_args, | |
| 255 | + } => Terminator::CondBranch { | |
| 256 | + cond: rv(cond), | |
| 257 | + true_dest: rb(true_dest), | |
| 258 | + true_args: rvs(true_args), | |
| 259 | + false_dest: rb(false_dest), | |
| 260 | + false_args: rvs(false_args), | |
| 261 | + }, | |
| 262 | + Terminator::Switch { | |
| 263 | + selector, | |
| 264 | + default, | |
| 265 | + cases, | |
| 266 | + } => Terminator::Switch { | |
| 267 | + selector: rv(selector), | |
| 268 | + default: rb(default), | |
| 269 | + cases: cases.iter().map(|(v, d)| (*v, rb(d))).collect(), | |
| 270 | + }, | |
| 254 | 271 | Terminator::Unreachable => Terminator::Unreachable, |
| 255 | 272 | } |
| 256 | 273 | } |
src/opt/lsf.rsmodified@@ -43,8 +43,8 @@ | ||
| 43 | 43 | //! %x = iadd %42, %1 |
| 44 | 44 | //! ``` |
| 45 | 45 | |
| 46 | +use super::alias::{self, may_reach_through_call_arg, AliasResult}; | |
| 46 | 47 | use super::pass::Pass; |
| 47 | -use super::alias::{self, AliasResult, may_reach_through_call_arg}; | |
| 48 | 48 | use crate::ir::inst::*; |
| 49 | 49 | use crate::ir::types::IrType; |
| 50 | 50 | use crate::ir::walk::{for_each_operand_mut, for_each_terminator_operand_mut}; |
@@ -53,7 +53,9 @@ use std::collections::HashMap; | ||
| 53 | 53 | pub struct LocalLsf; |
| 54 | 54 | |
| 55 | 55 | impl Pass for LocalLsf { |
| 56 | - fn name(&self) -> &'static str { "load-store-fwd" } | |
| 56 | + fn name(&self) -> &'static str { | |
| 57 | + "load-store-fwd" | |
| 58 | + } | |
| 57 | 59 | |
| 58 | 60 | fn run(&self, module: &mut Module) -> bool { |
| 59 | 61 | let mut changed = false; |
@@ -85,13 +87,19 @@ fn lsf_in_function(func: &mut Function) -> bool { | ||
| 85 | 87 | available.retain(|entry| { |
| 86 | 88 | matches!(alias::query(func, entry.ptr, eff_ptr), AliasResult::NoAlias) |
| 87 | 89 | }); |
| 88 | - available.push(AvailableStore { ptr: eff_ptr, val: eff_val }); | |
| 90 | + available.push(AvailableStore { | |
| 91 | + ptr: eff_ptr, | |
| 92 | + val: eff_val, | |
| 93 | + }); | |
| 89 | 94 | } |
| 90 | 95 | |
| 91 | 96 | InstKind::Load(ptr) => { |
| 92 | 97 | let eff_ptr = resolve(&all_rewrites, *ptr); |
| 93 | 98 | if let Some(entry) = available.iter().rev().find(|entry| { |
| 94 | - matches!(alias::query(func, entry.ptr, eff_ptr), AliasResult::MustAlias) | |
| 99 | + matches!( | |
| 100 | + alias::query(func, entry.ptr, eff_ptr), | |
| 101 | + AliasResult::MustAlias | |
| 102 | + ) | |
| 95 | 103 | }) { |
| 96 | 104 | all_rewrites.insert(inst.id, entry.val); |
| 97 | 105 | changed = true; |
@@ -113,9 +121,9 @@ fn lsf_in_function(func: &mut Function) -> bool { | ||
| 113 | 121 | // so a precise "different GEP offset → |
| 114 | 122 | // NoAlias" answer is unsound here. |
| 115 | 123 | available.retain(|entry| { |
| 116 | - pointer_args.iter().all(|arg| { | |
| 117 | - !may_reach_through_call_arg(func, entry.ptr, *arg) | |
| 118 | - }) | |
| 124 | + pointer_args | |
| 125 | + .iter() | |
| 126 | + .all(|arg| !may_reach_through_call_arg(func, entry.ptr, *arg)) | |
| 119 | 127 | }); |
| 120 | 128 | } |
| 121 | 129 | } |
@@ -145,7 +153,9 @@ fn resolve(rewrites: &HashMap<ValueId, ValueId>, mut v: ValueId) -> ValueId { | ||
| 145 | 153 | while let Some(&next) = rewrites.get(&v) { |
| 146 | 154 | v = next; |
| 147 | 155 | steps += 1; |
| 148 | - if steps > 64 { break; } // cycle guard (SSA has none, but be safe) | |
| 156 | + if steps > 64 { | |
| 157 | + break; | |
| 158 | + } // cycle guard (SSA has none, but be safe) | |
| 149 | 159 | } |
| 150 | 160 | v |
| 151 | 161 | } |
@@ -175,10 +185,17 @@ fn value_is_pointer(func: &Function, value: ValueId) -> bool { | ||
| 175 | 185 | if matches!(func.value_type(value), Some(IrType::Ptr(_))) { |
| 176 | 186 | return true; |
| 177 | 187 | } |
| 178 | - if func.params.iter().any(|param| param.id == value && matches!(param.ty, IrType::Ptr(_))) { | |
| 188 | + if func | |
| 189 | + .params | |
| 190 | + .iter() | |
| 191 | + .any(|param| param.id == value && matches!(param.ty, IrType::Ptr(_))) | |
| 192 | + { | |
| 179 | 193 | return true; |
| 180 | 194 | } |
| 181 | - func.blocks.iter().flat_map(|block| block.insts.iter()).find(|inst| inst.id == value) | |
| 195 | + func.blocks | |
| 196 | + .iter() | |
| 197 | + .flat_map(|block| block.insts.iter()) | |
| 198 | + .find(|inst| inst.id == value) | |
| 182 | 199 | .map(|inst| matches!(inst.ty, IrType::Ptr(_))) |
| 183 | 200 | .unwrap_or(false) |
| 184 | 201 | } |
@@ -190,19 +207,28 @@ fn value_is_pointer(func: &Function, value: ValueId) -> bool { | ||
| 190 | 207 | #[cfg(test)] |
| 191 | 208 | mod tests { |
| 192 | 209 | use super::*; |
| 193 | - use crate::ir::types::{IrType, IntWidth}; | |
| 194 | - use crate::opt::pass::Pass; | |
| 210 | + use crate::ir::types::{IntWidth, IrType}; | |
| 195 | 211 | use crate::lexer::{Position, Span}; |
| 212 | + use crate::opt::pass::Pass; | |
| 196 | 213 | |
| 197 | 214 | fn dummy_span() -> Span { |
| 198 | 215 | let p = Position { line: 0, col: 0 }; |
| 199 | - Span { file_id: 0, start: p, end: p } | |
| 216 | + Span { | |
| 217 | + file_id: 0, | |
| 218 | + start: p, | |
| 219 | + end: p, | |
| 220 | + } | |
| 200 | 221 | } |
| 201 | 222 | |
| 202 | 223 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 203 | 224 | let id = f.next_value_id(); |
| 204 | 225 | let entry = f.entry; |
| 205 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 226 | + f.block_mut(entry).insts.push(Inst { | |
| 227 | + id, | |
| 228 | + kind, | |
| 229 | + ty, | |
| 230 | + span: dummy_span(), | |
| 231 | + }); | |
| 206 | 232 | id |
| 207 | 233 | } |
| 208 | 234 | |
@@ -223,16 +249,29 @@ mod tests { | ||
| 223 | 249 | let mut m = Module::new("test".into()); |
| 224 | 250 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 225 | 251 | |
| 226 | - let alloca = push(&mut f, InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 227 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 228 | - let val42 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), | |
| 229 | - IrType::Int(IntWidth::I32)); | |
| 230 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), | |
| 231 | - IrType::Int(IntWidth::I32)); | |
| 252 | + let alloca = push( | |
| 253 | + &mut f, | |
| 254 | + InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 255 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 256 | + ); | |
| 257 | + let val42 = push( | |
| 258 | + &mut f, | |
| 259 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 260 | + IrType::Int(IntWidth::I32), | |
| 261 | + ); | |
| 262 | + let one = push( | |
| 263 | + &mut f, | |
| 264 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 265 | + IrType::Int(IntWidth::I32), | |
| 266 | + ); | |
| 232 | 267 | push(&mut f, InstKind::Store(val42, alloca), IrType::Void); |
| 233 | - let load = push(&mut f, InstKind::Load(alloca), IrType::Int(IntWidth::I32)); | |
| 234 | - let add = push(&mut f, InstKind::IAdd(load, one), IrType::Int(IntWidth::I32)); | |
| 235 | - let entry = f.entry; | |
| 268 | + let load = push(&mut f, InstKind::Load(alloca), IrType::Int(IntWidth::I32)); | |
| 269 | + let add = push( | |
| 270 | + &mut f, | |
| 271 | + InstKind::IAdd(load, one), | |
| 272 | + IrType::Int(IntWidth::I32), | |
| 273 | + ); | |
| 274 | + let entry = f.entry; | |
| 236 | 275 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(add))); |
| 237 | 276 | m.add_function(f); |
| 238 | 277 | |
@@ -246,7 +285,8 @@ mod tests { | ||
| 246 | 285 | let add_inst = insts.iter().find(|i| i.id == add).unwrap(); |
| 247 | 286 | assert!( |
| 248 | 287 | matches!(&add_inst.kind, InstKind::IAdd(a, _) if *a == val42), |
| 249 | - "IAdd operand should be forwarded to val42, got {:?}", add_inst.kind | |
| 288 | + "IAdd operand should be forwarded to val42, got {:?}", | |
| 289 | + add_inst.kind | |
| 250 | 290 | ); |
| 251 | 291 | } |
| 252 | 292 | |
@@ -258,12 +298,21 @@ mod tests { | ||
| 258 | 298 | let mut m = Module::new("test".into()); |
| 259 | 299 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 260 | 300 | |
| 261 | - let alloca = push(&mut f, InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 262 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 263 | - let v42 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), | |
| 264 | - IrType::Int(IntWidth::I32)); | |
| 265 | - let v99 = push(&mut f, InstKind::ConstInt(99, IntWidth::I32), | |
| 266 | - IrType::Int(IntWidth::I32)); | |
| 301 | + let alloca = push( | |
| 302 | + &mut f, | |
| 303 | + InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 304 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 305 | + ); | |
| 306 | + let v42 = push( | |
| 307 | + &mut f, | |
| 308 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 309 | + IrType::Int(IntWidth::I32), | |
| 310 | + ); | |
| 311 | + let v99 = push( | |
| 312 | + &mut f, | |
| 313 | + InstKind::ConstInt(99, IntWidth::I32), | |
| 314 | + IrType::Int(IntWidth::I32), | |
| 315 | + ); | |
| 267 | 316 | push(&mut f, InstKind::Store(v42, alloca), IrType::Void); |
| 268 | 317 | push(&mut f, InstKind::Store(v99, alloca), IrType::Void); |
| 269 | 318 | let load = push(&mut f, InstKind::Load(alloca), IrType::Int(IntWidth::I32)); |
@@ -279,7 +328,8 @@ mod tests { | ||
| 279 | 328 | let term = func.block(func.entry).terminator.as_ref().unwrap(); |
| 280 | 329 | assert!( |
| 281 | 330 | matches!(term, Terminator::Return(Some(v)) if *v == v99), |
| 282 | - "Return should use v99 (the latest store), got {:?}", term | |
| 331 | + "Return should use v99 (the latest store), got {:?}", | |
| 332 | + term | |
| 283 | 333 | ); |
| 284 | 334 | } |
| 285 | 335 | |
@@ -291,15 +341,23 @@ mod tests { | ||
| 291 | 341 | let mut m = Module::new("test".into()); |
| 292 | 342 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 293 | 343 | |
| 294 | - let alloca = push(&mut f, InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 295 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 296 | - let v42 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), | |
| 297 | - IrType::Int(IntWidth::I32)); | |
| 344 | + let alloca = push( | |
| 345 | + &mut f, | |
| 346 | + InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 347 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 348 | + ); | |
| 349 | + let v42 = push( | |
| 350 | + &mut f, | |
| 351 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 352 | + IrType::Int(IntWidth::I32), | |
| 353 | + ); | |
| 298 | 354 | push(&mut f, InstKind::Store(v42, alloca), IrType::Void); |
| 299 | 355 | // A call that might write to the alloca. |
| 300 | - push(&mut f, InstKind::Call( | |
| 301 | - FuncRef::External("ext".into()), vec![alloca] | |
| 302 | - ), IrType::Void); | |
| 356 | + push( | |
| 357 | + &mut f, | |
| 358 | + InstKind::Call(FuncRef::External("ext".into()), vec![alloca]), | |
| 359 | + IrType::Void, | |
| 360 | + ); | |
| 303 | 361 | let load = push(&mut f, InstKind::Load(alloca), IrType::Int(IntWidth::I32)); |
| 304 | 362 | let entry = f.entry; |
| 305 | 363 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(load))); |
@@ -317,8 +375,11 @@ mod tests { | ||
| 317 | 375 | let mut m = Module::new("test".into()); |
| 318 | 376 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 319 | 377 | |
| 320 | - let alloca = push(&mut f, InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 321 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 378 | + let alloca = push( | |
| 379 | + &mut f, | |
| 380 | + InstKind::Alloca(IrType::Int(IntWidth::I32)), | |
| 381 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 382 | + ); | |
| 322 | 383 | let load = push(&mut f, InstKind::Load(alloca), IrType::Int(IntWidth::I32)); |
| 323 | 384 | let entry = f.entry; |
| 324 | 385 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(load))); |
@@ -337,8 +398,16 @@ mod tests { | ||
| 337 | 398 | let arr_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 4); |
| 338 | 399 | let arr_ptr_ty = IrType::Ptr(Box::new(arr_ty.clone())); |
| 339 | 400 | let ptr = push(&mut f, InstKind::Alloca(arr_ty), arr_ptr_ty); |
| 340 | - let zero0 = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 341 | - let zero1 = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 401 | + let zero0 = push( | |
| 402 | + &mut f, | |
| 403 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 404 | + IrType::Int(IntWidth::I32), | |
| 405 | + ); | |
| 406 | + let zero1 = push( | |
| 407 | + &mut f, | |
| 408 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 409 | + IrType::Int(IntWidth::I32), | |
| 410 | + ); | |
| 342 | 411 | let gep0 = push( |
| 343 | 412 | &mut f, |
| 344 | 413 | InstKind::GetElementPtr(ptr, vec![zero0]), |
@@ -349,15 +418,26 @@ mod tests { | ||
| 349 | 418 | InstKind::GetElementPtr(ptr, vec![zero1]), |
| 350 | 419 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 351 | 420 | ); |
| 352 | - let v42 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 421 | + let v42 = push( | |
| 422 | + &mut f, | |
| 423 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 424 | + IrType::Int(IntWidth::I32), | |
| 425 | + ); | |
| 353 | 426 | push(&mut f, InstKind::Store(v42, gep0), IrType::Void); |
| 354 | 427 | let load = push(&mut f, InstKind::Load(gep1), IrType::Int(IntWidth::I32)); |
| 355 | 428 | let entry = f.entry; |
| 356 | 429 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(load))); |
| 357 | 430 | m.add_function(f); |
| 358 | 431 | |
| 359 | - assert!(LocalLsf.run(&mut m), "LSF should forward across must-alias GEPs"); | |
| 360 | - let term = m.functions[0].block(m.functions[0].entry).terminator.as_ref().unwrap(); | |
| 432 | + assert!( | |
| 433 | + LocalLsf.run(&mut m), | |
| 434 | + "LSF should forward across must-alias GEPs" | |
| 435 | + ); | |
| 436 | + let term = m.functions[0] | |
| 437 | + .block(m.functions[0].entry) | |
| 438 | + .terminator | |
| 439 | + .as_ref() | |
| 440 | + .unwrap(); | |
| 361 | 441 | assert!( |
| 362 | 442 | matches!(term, Terminator::Return(Some(v)) if *v == v42), |
| 363 | 443 | "return should use the stored value after forwarding, got {:?}", |
@@ -378,24 +458,53 @@ mod tests { | ||
| 378 | 458 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 379 | 459 | |
| 380 | 460 | let arr_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 2); |
| 381 | - let alloca = push(&mut f, InstKind::Alloca(arr_ty.clone()), | |
| 382 | - IrType::Ptr(Box::new(arr_ty))); | |
| 383 | - let c0 = push(&mut f, InstKind::ConstInt(0, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 384 | - let c1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 385 | - let g0 = push(&mut f, InstKind::GetElementPtr(alloca, vec![c0]), | |
| 386 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 387 | - let g1 = push(&mut f, InstKind::GetElementPtr(alloca, vec![c1]), | |
| 388 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 389 | - let v20 = push(&mut f, InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 461 | + let alloca = push( | |
| 462 | + &mut f, | |
| 463 | + InstKind::Alloca(arr_ty.clone()), | |
| 464 | + IrType::Ptr(Box::new(arr_ty)), | |
| 465 | + ); | |
| 466 | + let c0 = push( | |
| 467 | + &mut f, | |
| 468 | + InstKind::ConstInt(0, IntWidth::I64), | |
| 469 | + IrType::Int(IntWidth::I64), | |
| 470 | + ); | |
| 471 | + let c1 = push( | |
| 472 | + &mut f, | |
| 473 | + InstKind::ConstInt(1, IntWidth::I64), | |
| 474 | + IrType::Int(IntWidth::I64), | |
| 475 | + ); | |
| 476 | + let g0 = push( | |
| 477 | + &mut f, | |
| 478 | + InstKind::GetElementPtr(alloca, vec![c0]), | |
| 479 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 480 | + ); | |
| 481 | + let g1 = push( | |
| 482 | + &mut f, | |
| 483 | + InstKind::GetElementPtr(alloca, vec![c1]), | |
| 484 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 485 | + ); | |
| 486 | + let v20 = push( | |
| 487 | + &mut f, | |
| 488 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 489 | + IrType::Int(IntWidth::I32), | |
| 490 | + ); | |
| 390 | 491 | push(&mut f, InstKind::Store(v20, g1), IrType::Void); |
| 391 | - push(&mut f, InstKind::Call(FuncRef::External("touch".into()), vec![g0]), IrType::Void); | |
| 492 | + push( | |
| 493 | + &mut f, | |
| 494 | + InstKind::Call(FuncRef::External("touch".into()), vec![g0]), | |
| 495 | + IrType::Void, | |
| 496 | + ); | |
| 392 | 497 | let load = push(&mut f, InstKind::Load(g1), IrType::Int(IntWidth::I32)); |
| 393 | 498 | let entry = f.entry; |
| 394 | 499 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(load))); |
| 395 | 500 | m.add_function(f); |
| 396 | 501 | |
| 397 | 502 | LocalLsf.run(&mut m); |
| 398 | - let term = m.functions[0].block(m.functions[0].entry).terminator.as_ref().unwrap(); | |
| 503 | + let term = m.functions[0] | |
| 504 | + .block(m.functions[0].entry) | |
| 505 | + .terminator | |
| 506 | + .as_ref() | |
| 507 | + .unwrap(); | |
| 399 | 508 | if let Terminator::Return(Some(v)) = term { |
| 400 | 509 | assert_ne!(*v, v20, "LSF forwarded arr[1] load to the caller's pre-call constant across a call that received arr[0]; callee could have walked to arr[1]"); |
| 401 | 510 | } else { |
@@ -425,26 +534,61 @@ mod tests { | ||
| 425 | 534 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 426 | 535 | |
| 427 | 536 | let arr_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 2); |
| 428 | - let alloca = push(&mut f, InstKind::Alloca(arr_ty.clone()), | |
| 429 | - IrType::Ptr(Box::new(arr_ty))); | |
| 430 | - let c0 = push(&mut f, InstKind::ConstInt(0, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 431 | - let c1 = push(&mut f, InstKind::ConstInt(1, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 432 | - let g0 = push(&mut f, InstKind::GetElementPtr(alloca, vec![c0]), | |
| 433 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 434 | - let g1 = push(&mut f, InstKind::GetElementPtr(alloca, vec![c1]), | |
| 435 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 436 | - | |
| 437 | - let v20 = push(&mut f, InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 537 | + let alloca = push( | |
| 538 | + &mut f, | |
| 539 | + InstKind::Alloca(arr_ty.clone()), | |
| 540 | + IrType::Ptr(Box::new(arr_ty)), | |
| 541 | + ); | |
| 542 | + let c0 = push( | |
| 543 | + &mut f, | |
| 544 | + InstKind::ConstInt(0, IntWidth::I64), | |
| 545 | + IrType::Int(IntWidth::I64), | |
| 546 | + ); | |
| 547 | + let c1 = push( | |
| 548 | + &mut f, | |
| 549 | + InstKind::ConstInt(1, IntWidth::I64), | |
| 550 | + IrType::Int(IntWidth::I64), | |
| 551 | + ); | |
| 552 | + let g0 = push( | |
| 553 | + &mut f, | |
| 554 | + InstKind::GetElementPtr(alloca, vec![c0]), | |
| 555 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 556 | + ); | |
| 557 | + let g1 = push( | |
| 558 | + &mut f, | |
| 559 | + InstKind::GetElementPtr(alloca, vec![c1]), | |
| 560 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 561 | + ); | |
| 562 | + | |
| 563 | + let v20 = push( | |
| 564 | + &mut f, | |
| 565 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 566 | + IrType::Int(IntWidth::I32), | |
| 567 | + ); | |
| 438 | 568 | push(&mut f, InstKind::Store(v20, g1), IrType::Void); |
| 439 | 569 | |
| 440 | - let chain0 = push(&mut f, InstKind::GetElementPtr(g0, vec![c0]), | |
| 441 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 442 | - let v11 = push(&mut f, InstKind::ConstInt(11, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 570 | + let chain0 = push( | |
| 571 | + &mut f, | |
| 572 | + InstKind::GetElementPtr(g0, vec![c0]), | |
| 573 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 574 | + ); | |
| 575 | + let v11 = push( | |
| 576 | + &mut f, | |
| 577 | + InstKind::ConstInt(11, IntWidth::I32), | |
| 578 | + IrType::Int(IntWidth::I32), | |
| 579 | + ); | |
| 443 | 580 | push(&mut f, InstKind::Store(v11, chain0), IrType::Void); |
| 444 | 581 | |
| 445 | - let chain1 = push(&mut f, InstKind::GetElementPtr(g0, vec![c1]), | |
| 446 | - IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 447 | - let v31 = push(&mut f, InstKind::ConstInt(31, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 582 | + let chain1 = push( | |
| 583 | + &mut f, | |
| 584 | + InstKind::GetElementPtr(g0, vec![c1]), | |
| 585 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 586 | + ); | |
| 587 | + let v31 = push( | |
| 588 | + &mut f, | |
| 589 | + InstKind::ConstInt(31, IntWidth::I32), | |
| 590 | + IrType::Int(IntWidth::I32), | |
| 591 | + ); | |
| 448 | 592 | push(&mut f, InstKind::Store(v31, chain1), IrType::Void); |
| 449 | 593 | |
| 450 | 594 | let load = push(&mut f, InstKind::Load(g1), IrType::Int(IntWidth::I32)); |
@@ -453,7 +597,11 @@ mod tests { | ||
| 453 | 597 | m.add_function(f); |
| 454 | 598 | |
| 455 | 599 | LocalLsf.run(&mut m); |
| 456 | - let term = m.functions[0].block(m.functions[0].entry).terminator.as_ref().unwrap(); | |
| 600 | + let term = m.functions[0] | |
| 601 | + .block(m.functions[0].entry) | |
| 602 | + .terminator | |
| 603 | + .as_ref() | |
| 604 | + .unwrap(); | |
| 457 | 605 | // The load must either remain or be forwarded to v31 — never |
| 458 | 606 | // to v20. |
| 459 | 607 | if let Terminator::Return(Some(v)) = term { |
@@ -472,14 +620,22 @@ mod tests { | ||
| 472 | 620 | IrType::Int(IntWidth::I32), |
| 473 | 621 | ); |
| 474 | 622 | |
| 475 | - let v42 = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 623 | + let v42 = push( | |
| 624 | + &mut f, | |
| 625 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 626 | + IrType::Int(IntWidth::I32), | |
| 627 | + ); | |
| 476 | 628 | push(&mut f, InstKind::Store(v42, ValueId(0)), IrType::Void); |
| 477 | 629 | push( |
| 478 | 630 | &mut f, |
| 479 | 631 | InstKind::Call(FuncRef::External("touch".into()), vec![ValueId(1)]), |
| 480 | 632 | IrType::Void, |
| 481 | 633 | ); |
| 482 | - let load = push(&mut f, InstKind::Load(ValueId(0)), IrType::Int(IntWidth::I32)); | |
| 634 | + let load = push( | |
| 635 | + &mut f, | |
| 636 | + InstKind::Load(ValueId(0)), | |
| 637 | + IrType::Int(IntWidth::I32), | |
| 638 | + ); | |
| 483 | 639 | let entry = f.entry; |
| 484 | 640 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(load))); |
| 485 | 641 | m.add_function(f); |
@@ -488,7 +644,11 @@ mod tests { | ||
| 488 | 644 | LocalLsf.run(&mut m), |
| 489 | 645 | "LSF should preserve the stored value across a noalias pointer call" |
| 490 | 646 | ); |
| 491 | - let term = m.functions[0].block(m.functions[0].entry).terminator.as_ref().unwrap(); | |
| 647 | + let term = m.functions[0] | |
| 648 | + .block(m.functions[0].entry) | |
| 649 | + .terminator | |
| 650 | + .as_ref() | |
| 651 | + .unwrap(); | |
| 492 | 652 | assert!( |
| 493 | 653 | matches!(term, Terminator::Return(Some(v)) if *v == v42), |
| 494 | 654 | "return should use the forwarded value across the noalias call, got {:?}", |
src/opt/mem2reg.rsmodified@@ -62,11 +62,8 @@ | ||
| 62 | 62 | |
| 63 | 63 | use super::pass::Pass; |
| 64 | 64 | use super::util::{ |
| 65 | - compute_dominance_frontiers, | |
| 66 | - compute_immediate_dominators, | |
| 67 | - dominator_tree_children, | |
| 68 | - prune_unreachable, | |
| 69 | - substitute_uses, | |
| 65 | + compute_dominance_frontiers, compute_immediate_dominators, dominator_tree_children, | |
| 66 | + prune_unreachable, substitute_uses, | |
| 70 | 67 | }; |
| 71 | 68 | use crate::ir::inst::*; |
| 72 | 69 | use crate::ir::types::IrType; |
@@ -76,7 +73,9 @@ use std::collections::{HashMap, HashSet, VecDeque}; | ||
| 76 | 73 | pub struct Mem2Reg; |
| 77 | 74 | |
| 78 | 75 | impl Pass for Mem2Reg { |
| 79 | - fn name(&self) -> &'static str { "mem2reg" } | |
| 76 | + fn name(&self) -> &'static str { | |
| 77 | + "mem2reg" | |
| 78 | + } | |
| 80 | 79 | |
| 81 | 80 | fn run(&self, module: &mut Module) -> bool { |
| 82 | 81 | let mut changed = false; |
@@ -122,11 +121,14 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 122 | 121 | |
| 123 | 122 | // ---- Phase 1: find promotable allocas ------------------------- |
| 124 | 123 | let promotable = find_promotable_allocas(func); |
| 125 | - if promotable.is_empty() { return pruned; } | |
| 124 | + if promotable.is_empty() { | |
| 125 | + return pruned; | |
| 126 | + } | |
| 126 | 127 | |
| 127 | 128 | // Map from alloca ValueId → index into `promotable`. We use the |
| 128 | 129 | // index as a compact key throughout the rest of the pass. |
| 129 | - let alloca_index: HashMap<ValueId, usize> = promotable.iter() | |
| 130 | + let alloca_index: HashMap<ValueId, usize> = promotable | |
| 131 | + .iter() | |
| 130 | 132 | .enumerate() |
| 131 | 133 | .map(|(i, p)| (p.alloca_id, i)) |
| 132 | 134 | .collect(); |
@@ -188,7 +190,10 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 188 | 190 | let mut undef_values: Vec<ValueId> = Vec::with_capacity(promotable.len()); |
| 189 | 191 | |
| 190 | 192 | // Grab a dummy span we can reuse for inserted insts. |
| 191 | - let span = func.block(func.entry).insts.first() | |
| 193 | + let span = func | |
| 194 | + .block(func.entry) | |
| 195 | + .insts | |
| 196 | + .first() | |
| 192 | 197 | .map(|i| i.span) |
| 193 | 198 | .or_else(|| { |
| 194 | 199 | func.block(func.entry).terminator.as_ref().map(|_t| { |
@@ -196,14 +201,14 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 196 | 201 | // zero span — good enough for synthesized insts. |
| 197 | 202 | crate::lexer::Span { |
| 198 | 203 | start: crate::lexer::Position { line: 0, col: 0 }, |
| 199 | - end: crate::lexer::Position { line: 0, col: 0 }, | |
| 204 | + end: crate::lexer::Position { line: 0, col: 0 }, | |
| 200 | 205 | file_id: 0, |
| 201 | 206 | } |
| 202 | 207 | }) |
| 203 | 208 | }) |
| 204 | 209 | .unwrap_or(crate::lexer::Span { |
| 205 | 210 | start: crate::lexer::Position { line: 0, col: 0 }, |
| 206 | - end: crate::lexer::Position { line: 0, col: 0 }, | |
| 211 | + end: crate::lexer::Position { line: 0, col: 0 }, | |
| 207 | 212 | file_id: 0, |
| 208 | 213 | }); |
| 209 | 214 | |
@@ -223,12 +228,15 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 223 | 228 | new_undefs.push((id, p.pointee_ty.clone())); |
| 224 | 229 | } |
| 225 | 230 | for (id, ty) in new_undefs.iter().rev() { |
| 226 | - func.block_mut(entry).insts.insert(0, Inst { | |
| 227 | - id: *id, | |
| 228 | - kind: InstKind::Undef(ty.clone()), | |
| 229 | - ty: ty.clone(), | |
| 230 | - span, | |
| 231 | - }); | |
| 231 | + func.block_mut(entry).insts.insert( | |
| 232 | + 0, | |
| 233 | + Inst { | |
| 234 | + id: *id, | |
| 235 | + kind: InstKind::Undef(ty.clone()), | |
| 236 | + ty: ty.clone(), | |
| 237 | + span, | |
| 238 | + }, | |
| 239 | + ); | |
| 232 | 240 | } |
| 233 | 241 | |
| 234 | 242 | // Now insert block params. Order within a block matters: we |
@@ -301,9 +309,7 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 301 | 309 | // Current value stack per alloca. Initialized with the entry |
| 302 | 310 | // Undef for each alloca so that any Load before any Store on |
| 303 | 311 | // this dom-tree path sees undef. |
| 304 | - let mut stacks: Vec<Vec<ValueId>> = undef_values.iter() | |
| 305 | - .map(|&u| vec![u]) | |
| 306 | - .collect(); | |
| 312 | + let mut stacks: Vec<Vec<ValueId>> = undef_values.iter().map(|&u| vec![u]).collect(); | |
| 307 | 313 | |
| 308 | 314 | // (old_load_id, new_value_id) rewrites to apply at the end. |
| 309 | 315 | let mut load_renames: HashMap<ValueId, ValueId> = HashMap::new(); |
@@ -395,7 +401,11 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 395 | 401 | let raw: Vec<BlockId> = match &block.terminator { |
| 396 | 402 | Some(Terminator::Return(_)) | Some(Terminator::Unreachable) | None => vec![], |
| 397 | 403 | Some(Terminator::Branch(d, _)) => vec![*d], |
| 398 | - Some(Terminator::CondBranch { true_dest, false_dest, .. }) => { | |
| 404 | + Some(Terminator::CondBranch { | |
| 405 | + true_dest, | |
| 406 | + false_dest, | |
| 407 | + .. | |
| 408 | + }) => { | |
| 399 | 409 | vec![*true_dest, *false_dest] |
| 400 | 410 | } |
| 401 | 411 | Some(Terminator::Switch { cases, default, .. }) => { |
@@ -413,11 +423,14 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 413 | 423 | // current stack top as a branch arg. The order |
| 414 | 424 | // matches the order the params were pushed onto |
| 415 | 425 | // `succ.params` during phase 3. |
| 416 | - let new_args: Vec<ValueId> = order.iter() | |
| 417 | - .map(|&idx| resolve_promoted_value( | |
| 418 | - *stacks[idx].last().expect("mem2reg: stack empty at branch"), | |
| 419 | - load_renames, | |
| 420 | - )) | |
| 426 | + let new_args: Vec<ValueId> = order | |
| 427 | + .iter() | |
| 428 | + .map(|&idx| { | |
| 429 | + resolve_promoted_value( | |
| 430 | + *stacks[idx].last().expect("mem2reg: stack empty at branch"), | |
| 431 | + load_renames, | |
| 432 | + ) | |
| 433 | + }) | |
| 421 | 434 | .collect(); |
| 422 | 435 | // Locate the slots in the terminator's arg list. |
| 423 | 436 | let block_mut = func.block_mut(block_id); |
@@ -431,9 +444,17 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 431 | 444 | let kids: Vec<BlockId> = children.get(&block_id).cloned().unwrap_or_default(); |
| 432 | 445 | for kid in kids { |
| 433 | 446 | rename_block( |
| 434 | - func, kid, promotable, alloca_index, phi_params, | |
| 435 | - block_phi_order, children, stacks, | |
| 436 | - load_renames, dead_loads, dead_stores, | |
| 447 | + func, | |
| 448 | + kid, | |
| 449 | + promotable, | |
| 450 | + alloca_index, | |
| 451 | + phi_params, | |
| 452 | + block_phi_order, | |
| 453 | + children, | |
| 454 | + stacks, | |
| 455 | + load_renames, | |
| 456 | + dead_loads, | |
| 457 | + dead_stores, | |
| 437 | 458 | ); |
| 438 | 459 | } |
| 439 | 460 | |
@@ -446,9 +467,17 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 446 | 467 | } |
| 447 | 468 | |
| 448 | 469 | rename_block( |
| 449 | - func, func.entry, &promotable, &alloca_index, &phi_params, | |
| 450 | - &block_phi_order, &children, &mut stacks, | |
| 451 | - &mut load_renames, &mut dead_loads, &mut dead_stores, | |
| 470 | + func, | |
| 471 | + func.entry, | |
| 472 | + &promotable, | |
| 473 | + &alloca_index, | |
| 474 | + &phi_params, | |
| 475 | + &block_phi_order, | |
| 476 | + &children, | |
| 477 | + &mut stacks, | |
| 478 | + &mut load_renames, | |
| 479 | + &mut dead_loads, | |
| 480 | + &mut dead_stores, | |
| 452 | 481 | ); |
| 453 | 482 | |
| 454 | 483 | // ---- Phase 5: apply load renames and delete dead insts -------- |
@@ -460,9 +489,15 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 460 | 489 | let alloca_ids: HashSet<ValueId> = promotable.iter().map(|p| p.alloca_id).collect(); |
| 461 | 490 | for block in &mut func.blocks { |
| 462 | 491 | block.insts.retain(|inst| { |
| 463 | - if dead_loads.contains(&inst.id) { return false; } | |
| 464 | - if dead_stores.contains(&inst.id) { return false; } | |
| 465 | - if alloca_ids.contains(&inst.id) { return false; } | |
| 492 | + if dead_loads.contains(&inst.id) { | |
| 493 | + return false; | |
| 494 | + } | |
| 495 | + if dead_stores.contains(&inst.id) { | |
| 496 | + return false; | |
| 497 | + } | |
| 498 | + if alloca_ids.contains(&inst.id) { | |
| 499 | + return false; | |
| 500 | + } | |
| 466 | 501 | true |
| 467 | 502 | }); |
| 468 | 503 | } |
@@ -470,10 +505,7 @@ fn promote_function(func: &mut Function) -> bool { | ||
| 470 | 505 | true |
| 471 | 506 | } |
| 472 | 507 | |
| 473 | -fn resolve_promoted_value( | |
| 474 | - mut value: ValueId, | |
| 475 | - load_renames: &HashMap<ValueId, ValueId>, | |
| 476 | -) -> ValueId { | |
| 508 | +fn resolve_promoted_value(mut value: ValueId, load_renames: &HashMap<ValueId, ValueId>) -> ValueId { | |
| 477 | 509 | let mut seen = HashSet::new(); |
| 478 | 510 | while let Some(&next) = load_renames.get(&value) { |
| 479 | 511 | if !seen.insert(value) || next == value { |
@@ -500,7 +532,9 @@ fn find_promotable_allocas(func: &Function) -> Vec<Promotable> { | ||
| 500 | 532 | } |
| 501 | 533 | } |
| 502 | 534 | } |
| 503 | - if candidates.is_empty() { return Vec::new(); } | |
| 535 | + if candidates.is_empty() { | |
| 536 | + return Vec::new(); | |
| 537 | + } | |
| 504 | 538 | |
| 505 | 539 | // Second pass: walk every use and disqualify any alloca whose |
| 506 | 540 | // ValueId appears in a non-load/non-store-addr position. |
@@ -557,7 +591,10 @@ fn find_promotable_allocas(func: &Function) -> Vec<Promotable> { | ||
| 557 | 591 | for inst in &block.insts { |
| 558 | 592 | if let InstKind::Alloca(_) = &inst.kind { |
| 559 | 593 | if let Some(ty) = candidates.remove(&inst.id) { |
| 560 | - out.push(Promotable { alloca_id: inst.id, pointee_ty: ty }); | |
| 594 | + out.push(Promotable { | |
| 595 | + alloca_id: inst.id, | |
| 596 | + pointee_ty: ty, | |
| 597 | + }); | |
| 561 | 598 | } |
| 562 | 599 | } |
| 563 | 600 | } |
@@ -581,7 +618,13 @@ fn append_branch_args_for(term: &mut Terminator, target: BlockId, new_args: &[Va | ||
| 581 | 618 | Terminator::Branch(d, args) if *d == target => { |
| 582 | 619 | args.extend_from_slice(new_args); |
| 583 | 620 | } |
| 584 | - Terminator::CondBranch { true_dest, true_args, false_dest, false_args, .. } => { | |
| 621 | + Terminator::CondBranch { | |
| 622 | + true_dest, | |
| 623 | + true_args, | |
| 624 | + false_dest, | |
| 625 | + false_args, | |
| 626 | + .. | |
| 627 | + } => { | |
| 585 | 628 | if *true_dest == target { |
| 586 | 629 | true_args.extend_from_slice(new_args); |
| 587 | 630 | } |
@@ -600,16 +643,25 @@ mod tests { | ||
| 600 | 643 | use super::*; |
| 601 | 644 | use crate::ir::types::IntWidth; |
| 602 | 645 | use crate::ir::verify::verify_module; |
| 603 | - use crate::lexer::{Span, Position}; | |
| 646 | + use crate::lexer::{Position, Span}; | |
| 604 | 647 | |
| 605 | 648 | fn dummy_span() -> Span { |
| 606 | 649 | let p = Position { line: 1, col: 1 }; |
| 607 | - Span { start: p, end: p, file_id: 0 } | |
| 650 | + Span { | |
| 651 | + start: p, | |
| 652 | + end: p, | |
| 653 | + file_id: 0, | |
| 654 | + } | |
| 608 | 655 | } |
| 609 | 656 | |
| 610 | 657 | fn push_inst(f: &mut Function, block: BlockId, kind: InstKind, ty: IrType) -> ValueId { |
| 611 | 658 | let id = f.next_value_id(); |
| 612 | - f.block_mut(block).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 659 | + f.block_mut(block).insts.push(Inst { | |
| 660 | + id, | |
| 661 | + kind, | |
| 662 | + ty, | |
| 663 | + span: dummy_span(), | |
| 664 | + }); | |
| 613 | 665 | id |
| 614 | 666 | } |
| 615 | 667 | |
@@ -623,19 +675,22 @@ mod tests { | ||
| 623 | 675 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 624 | 676 | let entry = f.entry; |
| 625 | 677 | |
| 626 | - let slot = push_inst(&mut f, entry, | |
| 678 | + let slot = push_inst( | |
| 679 | + &mut f, | |
| 680 | + entry, | |
| 627 | 681 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 628 | 682 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 629 | 683 | ); |
| 630 | - let c7 = push_inst(&mut f, entry, | |
| 684 | + let c7 = push_inst( | |
| 685 | + &mut f, | |
| 686 | + entry, | |
| 631 | 687 | InstKind::ConstInt(7, IntWidth::I32), |
| 632 | 688 | IrType::Int(IntWidth::I32), |
| 633 | 689 | ); |
| 634 | - push_inst(&mut f, entry, | |
| 635 | - InstKind::Store(c7, slot), | |
| 636 | - IrType::Void, | |
| 637 | - ); | |
| 638 | - let loaded = push_inst(&mut f, entry, | |
| 690 | + push_inst(&mut f, entry, InstKind::Store(c7, slot), IrType::Void); | |
| 691 | + let loaded = push_inst( | |
| 692 | + &mut f, | |
| 693 | + entry, | |
| 639 | 694 | InstKind::Load(slot), |
| 640 | 695 | IrType::Int(IntWidth::I32), |
| 641 | 696 | ); |
@@ -649,15 +704,31 @@ mod tests { | ||
| 649 | 704 | // After: alloca/store/load all gone. The return should |
| 650 | 705 | // reference c7 directly (via substitution). |
| 651 | 706 | let block = &m.functions[0].blocks[0]; |
| 652 | - assert!(!block.insts.iter().any(|i| matches!(i.kind, InstKind::Alloca(_))), | |
| 653 | - "alloca should be gone"); | |
| 654 | - assert!(!block.insts.iter().any(|i| matches!(i.kind, InstKind::Load(_))), | |
| 655 | - "load should be gone"); | |
| 656 | - assert!(!block.insts.iter().any(|i| matches!(i.kind, InstKind::Store(..))), | |
| 657 | - "store should be gone"); | |
| 707 | + assert!( | |
| 708 | + !block | |
| 709 | + .insts | |
| 710 | + .iter() | |
| 711 | + .any(|i| matches!(i.kind, InstKind::Alloca(_))), | |
| 712 | + "alloca should be gone" | |
| 713 | + ); | |
| 714 | + assert!( | |
| 715 | + !block | |
| 716 | + .insts | |
| 717 | + .iter() | |
| 718 | + .any(|i| matches!(i.kind, InstKind::Load(_))), | |
| 719 | + "load should be gone" | |
| 720 | + ); | |
| 721 | + assert!( | |
| 722 | + !block | |
| 723 | + .insts | |
| 724 | + .iter() | |
| 725 | + .any(|i| matches!(i.kind, InstKind::Store(..))), | |
| 726 | + "store should be gone" | |
| 727 | + ); | |
| 658 | 728 | match block.terminator.as_ref().unwrap() { |
| 659 | - Terminator::Return(Some(v)) => assert_eq!(*v, c7, | |
| 660 | - "return should reference the stored const directly"), | |
| 729 | + Terminator::Return(Some(v)) => { | |
| 730 | + assert_eq!(*v, c7, "return should reference the stored const directly") | |
| 731 | + } | |
| 661 | 732 | _ => panic!(), |
| 662 | 733 | } |
| 663 | 734 | } |
@@ -672,41 +743,45 @@ mod tests { | ||
| 672 | 743 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 673 | 744 | let entry = f.entry; |
| 674 | 745 | |
| 675 | - let slot = push_inst(&mut f, entry, | |
| 746 | + let slot = push_inst( | |
| 747 | + &mut f, | |
| 748 | + entry, | |
| 676 | 749 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 677 | 750 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 678 | 751 | ); |
| 679 | - let cond = push_inst(&mut f, entry, | |
| 680 | - InstKind::ConstBool(true), | |
| 681 | - IrType::Bool, | |
| 682 | - ); | |
| 752 | + let cond = push_inst(&mut f, entry, InstKind::ConstBool(true), IrType::Bool); | |
| 683 | 753 | let then_b = f.create_block("then"); |
| 684 | 754 | let else_b = f.create_block("else"); |
| 685 | 755 | let merge = f.create_block("merge"); |
| 686 | 756 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 687 | - cond, true_dest: then_b, true_args: vec![], | |
| 688 | - false_dest: else_b, false_args: vec![], | |
| 757 | + cond, | |
| 758 | + true_dest: then_b, | |
| 759 | + true_args: vec![], | |
| 760 | + false_dest: else_b, | |
| 761 | + false_args: vec![], | |
| 689 | 762 | }); |
| 690 | 763 | |
| 691 | - let c1 = push_inst(&mut f, then_b, | |
| 764 | + let c1 = push_inst( | |
| 765 | + &mut f, | |
| 766 | + then_b, | |
| 692 | 767 | InstKind::ConstInt(1, IntWidth::I32), |
| 693 | 768 | IrType::Int(IntWidth::I32), |
| 694 | 769 | ); |
| 695 | - push_inst(&mut f, then_b, | |
| 696 | - InstKind::Store(c1, slot), IrType::Void, | |
| 697 | - ); | |
| 770 | + push_inst(&mut f, then_b, InstKind::Store(c1, slot), IrType::Void); | |
| 698 | 771 | f.block_mut(then_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 699 | 772 | |
| 700 | - let c2 = push_inst(&mut f, else_b, | |
| 773 | + let c2 = push_inst( | |
| 774 | + &mut f, | |
| 775 | + else_b, | |
| 701 | 776 | InstKind::ConstInt(2, IntWidth::I32), |
| 702 | 777 | IrType::Int(IntWidth::I32), |
| 703 | 778 | ); |
| 704 | - push_inst(&mut f, else_b, | |
| 705 | - InstKind::Store(c2, slot), IrType::Void, | |
| 706 | - ); | |
| 779 | + push_inst(&mut f, else_b, InstKind::Store(c2, slot), IrType::Void); | |
| 707 | 780 | f.block_mut(else_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 708 | 781 | |
| 709 | - let loaded = push_inst(&mut f, merge, | |
| 782 | + let loaded = push_inst( | |
| 783 | + &mut f, | |
| 784 | + merge, | |
| 710 | 785 | InstKind::Load(slot), |
| 711 | 786 | IrType::Int(IntWidth::I32), |
| 712 | 787 | ); |
@@ -723,11 +798,17 @@ mod tests { | ||
| 723 | 798 | // that param. |
| 724 | 799 | let f = &m.functions[0]; |
| 725 | 800 | let merge_block = f.block(merge); |
| 726 | - assert_eq!(merge_block.params.len(), 1, "merge should have 1 block param"); | |
| 801 | + assert_eq!( | |
| 802 | + merge_block.params.len(), | |
| 803 | + 1, | |
| 804 | + "merge should have 1 block param" | |
| 805 | + ); | |
| 727 | 806 | let param_id = merge_block.params[0].id; |
| 728 | 807 | match merge_block.terminator.as_ref().unwrap() { |
| 729 | - Terminator::Return(Some(v)) => assert_eq!(*v, param_id, | |
| 730 | - "return should reference the merge block param"), | |
| 808 | + Terminator::Return(Some(v)) => assert_eq!( | |
| 809 | + *v, param_id, | |
| 810 | + "return should reference the merge block param" | |
| 811 | + ), | |
| 731 | 812 | _ => panic!(), |
| 732 | 813 | } |
| 733 | 814 | |
@@ -760,21 +841,30 @@ mod tests { | ||
| 760 | 841 | let mut m = Module::new("t".into()); |
| 761 | 842 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 762 | 843 | let entry = f.entry; |
| 763 | - let slot = push_inst(&mut f, entry, | |
| 844 | + let slot = push_inst( | |
| 845 | + &mut f, | |
| 846 | + entry, | |
| 764 | 847 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 765 | 848 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 766 | 849 | ); |
| 767 | 850 | // Call that takes the slot's address — escape! |
| 768 | - push_inst(&mut f, entry, | |
| 851 | + push_inst( | |
| 852 | + &mut f, | |
| 853 | + entry, | |
| 769 | 854 | InstKind::Call(FuncRef::External("takes_ptr".into()), vec![slot]), |
| 770 | 855 | IrType::Void, |
| 771 | 856 | ); |
| 772 | 857 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 773 | 858 | m.add_function(f); |
| 774 | 859 | |
| 775 | - assert!(!Mem2Reg.run(&mut m), "escaping alloca should not be promoted"); | |
| 860 | + assert!( | |
| 861 | + !Mem2Reg.run(&mut m), | |
| 862 | + "escaping alloca should not be promoted" | |
| 863 | + ); | |
| 776 | 864 | // The alloca is still there. |
| 777 | - assert!(m.functions[0].blocks[0].insts.iter() | |
| 865 | + assert!(m.functions[0].blocks[0] | |
| 866 | + .insts | |
| 867 | + .iter() | |
| 778 | 868 | .any(|i| matches!(i.kind, InstKind::Alloca(_)))); |
| 779 | 869 | } |
| 780 | 870 | |
@@ -787,28 +877,37 @@ mod tests { | ||
| 787 | 877 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 788 | 878 | let entry = f.entry; |
| 789 | 879 | // Promotable: only used by store + load. |
| 790 | - let good = push_inst(&mut f, entry, | |
| 880 | + let good = push_inst( | |
| 881 | + &mut f, | |
| 882 | + entry, | |
| 791 | 883 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 792 | 884 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 793 | 885 | ); |
| 794 | 886 | // Non-promotable: escapes via call. |
| 795 | - let bad = push_inst(&mut f, entry, | |
| 887 | + let bad = push_inst( | |
| 888 | + &mut f, | |
| 889 | + entry, | |
| 796 | 890 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 797 | 891 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 798 | 892 | ); |
| 799 | - let c42 = push_inst(&mut f, entry, | |
| 893 | + let c42 = push_inst( | |
| 894 | + &mut f, | |
| 895 | + entry, | |
| 800 | 896 | InstKind::ConstInt(42, IntWidth::I32), |
| 801 | 897 | IrType::Int(IntWidth::I32), |
| 802 | 898 | ); |
| 803 | - push_inst(&mut f, entry, | |
| 804 | - InstKind::Store(c42, good), IrType::Void, | |
| 805 | - ); | |
| 806 | - push_inst(&mut f, entry, | |
| 899 | + push_inst(&mut f, entry, InstKind::Store(c42, good), IrType::Void); | |
| 900 | + push_inst( | |
| 901 | + &mut f, | |
| 902 | + entry, | |
| 807 | 903 | InstKind::Call(FuncRef::External("takes_ptr".into()), vec![bad]), |
| 808 | 904 | IrType::Void, |
| 809 | 905 | ); |
| 810 | - let loaded = push_inst(&mut f, entry, | |
| 811 | - InstKind::Load(good), IrType::Int(IntWidth::I32), | |
| 906 | + let loaded = push_inst( | |
| 907 | + &mut f, | |
| 908 | + entry, | |
| 909 | + InstKind::Load(good), | |
| 910 | + IrType::Int(IntWidth::I32), | |
| 812 | 911 | ); |
| 813 | 912 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(loaded))); |
| 814 | 913 | m.add_function(f); |
@@ -819,7 +918,9 @@ mod tests { | ||
| 819 | 918 | |
| 820 | 919 | let block = &m.functions[0].blocks[0]; |
| 821 | 920 | // `good` is gone; `bad` remains. |
| 822 | - let alloca_count = block.insts.iter() | |
| 921 | + let alloca_count = block | |
| 922 | + .insts | |
| 923 | + .iter() | |
| 823 | 924 | .filter(|i| matches!(i.kind, InstKind::Alloca(_))) |
| 824 | 925 | .count(); |
| 825 | 926 | assert_eq!(alloca_count, 1, "bad alloca should survive"); |
@@ -840,17 +941,19 @@ mod tests { | ||
| 840 | 941 | let mut m = Module::new("t".into()); |
| 841 | 942 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 842 | 943 | let entry = f.entry; |
| 843 | - let slot = push_inst(&mut f, entry, | |
| 944 | + let slot = push_inst( | |
| 945 | + &mut f, | |
| 946 | + entry, | |
| 844 | 947 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 845 | 948 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 846 | 949 | ); |
| 847 | - let c0 = push_inst(&mut f, entry, | |
| 950 | + let c0 = push_inst( | |
| 951 | + &mut f, | |
| 952 | + entry, | |
| 848 | 953 | InstKind::ConstInt(0, IntWidth::I32), |
| 849 | 954 | IrType::Int(IntWidth::I32), |
| 850 | 955 | ); |
| 851 | - push_inst(&mut f, entry, | |
| 852 | - InstKind::Store(c0, slot), IrType::Void, | |
| 853 | - ); | |
| 956 | + push_inst(&mut f, entry, InstKind::Store(c0, slot), IrType::Void); | |
| 854 | 957 | |
| 855 | 958 | let header = f.create_block("header"); |
| 856 | 959 | let body = f.create_block("body"); |
@@ -858,37 +961,52 @@ mod tests { | ||
| 858 | 961 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![])); |
| 859 | 962 | |
| 860 | 963 | // header: load i, cmp i < 10, cond br body/exit |
| 861 | - let cur = push_inst(&mut f, header, | |
| 862 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 964 | + let cur = push_inst( | |
| 965 | + &mut f, | |
| 966 | + header, | |
| 967 | + InstKind::Load(slot), | |
| 968 | + IrType::Int(IntWidth::I32), | |
| 863 | 969 | ); |
| 864 | - let c10 = push_inst(&mut f, header, | |
| 970 | + let c10 = push_inst( | |
| 971 | + &mut f, | |
| 972 | + header, | |
| 865 | 973 | InstKind::ConstInt(10, IntWidth::I32), |
| 866 | 974 | IrType::Int(IntWidth::I32), |
| 867 | 975 | ); |
| 868 | - let cmp = push_inst(&mut f, header, | |
| 976 | + let cmp = push_inst( | |
| 977 | + &mut f, | |
| 978 | + header, | |
| 869 | 979 | InstKind::ICmp(CmpOp::Lt, cur, c10), |
| 870 | 980 | IrType::Bool, |
| 871 | 981 | ); |
| 872 | 982 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 873 | - cond: cmp, true_dest: body, true_args: vec![], | |
| 874 | - false_dest: exit, false_args: vec![], | |
| 983 | + cond: cmp, | |
| 984 | + true_dest: body, | |
| 985 | + true_args: vec![], | |
| 986 | + false_dest: exit, | |
| 987 | + false_args: vec![], | |
| 875 | 988 | }); |
| 876 | 989 | |
| 877 | 990 | // body: i = i + 1; br header |
| 878 | - let cur2 = push_inst(&mut f, body, | |
| 879 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 991 | + let cur2 = push_inst( | |
| 992 | + &mut f, | |
| 993 | + body, | |
| 994 | + InstKind::Load(slot), | |
| 995 | + IrType::Int(IntWidth::I32), | |
| 880 | 996 | ); |
| 881 | - let c1 = push_inst(&mut f, body, | |
| 997 | + let c1 = push_inst( | |
| 998 | + &mut f, | |
| 999 | + body, | |
| 882 | 1000 | InstKind::ConstInt(1, IntWidth::I32), |
| 883 | 1001 | IrType::Int(IntWidth::I32), |
| 884 | 1002 | ); |
| 885 | - let next = push_inst(&mut f, body, | |
| 1003 | + let next = push_inst( | |
| 1004 | + &mut f, | |
| 1005 | + body, | |
| 886 | 1006 | InstKind::IAdd(cur2, c1), |
| 887 | 1007 | IrType::Int(IntWidth::I32), |
| 888 | 1008 | ); |
| 889 | - push_inst(&mut f, body, | |
| 890 | - InstKind::Store(next, slot), IrType::Void, | |
| 891 | - ); | |
| 1009 | + push_inst(&mut f, body, InstKind::Store(next, slot), IrType::Void); | |
| 892 | 1010 | f.block_mut(body).terminator = Some(Terminator::Branch(header, vec![])); |
| 893 | 1011 | |
| 894 | 1012 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
@@ -901,17 +1019,26 @@ mod tests { | ||
| 901 | 1019 | let f = &m.functions[0]; |
| 902 | 1020 | let header_block = f.block(header); |
| 903 | 1021 | // Header should have exactly one block param (the promoted counter). |
| 904 | - assert_eq!(header_block.params.len(), 1, | |
| 905 | - "header should have 1 block param for the promoted counter"); | |
| 1022 | + assert_eq!( | |
| 1023 | + header_block.params.len(), | |
| 1024 | + 1, | |
| 1025 | + "header should have 1 block param for the promoted counter" | |
| 1026 | + ); | |
| 906 | 1027 | // No loads or stores anywhere. |
| 907 | 1028 | for b in &f.blocks { |
| 908 | 1029 | for i in &b.insts { |
| 909 | - assert!(!matches!(i.kind, InstKind::Load(_)), | |
| 910 | - "no loads should survive mem2reg"); | |
| 911 | - assert!(!matches!(i.kind, InstKind::Store(..)), | |
| 912 | - "no stores should survive mem2reg"); | |
| 913 | - assert!(!matches!(i.kind, InstKind::Alloca(_)), | |
| 914 | - "no allocas should survive mem2reg"); | |
| 1030 | + assert!( | |
| 1031 | + !matches!(i.kind, InstKind::Load(_)), | |
| 1032 | + "no loads should survive mem2reg" | |
| 1033 | + ); | |
| 1034 | + assert!( | |
| 1035 | + !matches!(i.kind, InstKind::Store(..)), | |
| 1036 | + "no stores should survive mem2reg" | |
| 1037 | + ); | |
| 1038 | + assert!( | |
| 1039 | + !matches!(i.kind, InstKind::Alloca(_)), | |
| 1040 | + "no allocas should survive mem2reg" | |
| 1041 | + ); | |
| 915 | 1042 | } |
| 916 | 1043 | } |
| 917 | 1044 | } |
@@ -926,11 +1053,15 @@ mod tests { | ||
| 926 | 1053 | let mut m = Module::new("t".into()); |
| 927 | 1054 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 928 | 1055 | let entry = f.entry; |
| 929 | - let slot = push_inst(&mut f, entry, | |
| 1056 | + let slot = push_inst( | |
| 1057 | + &mut f, | |
| 1058 | + entry, | |
| 930 | 1059 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 931 | 1060 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 932 | 1061 | ); |
| 933 | - let loaded = push_inst(&mut f, entry, | |
| 1062 | + let loaded = push_inst( | |
| 1063 | + &mut f, | |
| 1064 | + entry, | |
| 934 | 1065 | InstKind::Load(slot), |
| 935 | 1066 | IrType::Int(IntWidth::I32), |
| 936 | 1067 | ); |
@@ -943,7 +1074,9 @@ mod tests { | ||
| 943 | 1074 | |
| 944 | 1075 | // Return should reference the synthetic Undef. |
| 945 | 1076 | let f = &m.functions[0]; |
| 946 | - let undef_id = f.blocks[0].insts.iter() | |
| 1077 | + let undef_id = f.blocks[0] | |
| 1078 | + .insts | |
| 1079 | + .iter() | |
| 947 | 1080 | .find(|i| matches!(i.kind, InstKind::Undef(_))) |
| 948 | 1081 | .map(|i| i.id) |
| 949 | 1082 | .expect("no Undef inserted"); |
@@ -965,16 +1098,22 @@ mod tests { | ||
| 965 | 1098 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 966 | 1099 | let entry = f.entry; |
| 967 | 1100 | |
| 968 | - let slot = push_inst(&mut f, entry, | |
| 1101 | + let slot = push_inst( | |
| 1102 | + &mut f, | |
| 1103 | + entry, | |
| 969 | 1104 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 970 | 1105 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 971 | 1106 | ); |
| 972 | - let c1 = push_inst(&mut f, entry, | |
| 1107 | + let c1 = push_inst( | |
| 1108 | + &mut f, | |
| 1109 | + entry, | |
| 973 | 1110 | InstKind::ConstInt(1, IntWidth::I32), |
| 974 | 1111 | IrType::Int(IntWidth::I32), |
| 975 | 1112 | ); |
| 976 | 1113 | push_inst(&mut f, entry, InstKind::Store(c1, slot), IrType::Void); |
| 977 | - let loaded = push_inst(&mut f, entry, | |
| 1114 | + let loaded = push_inst( | |
| 1115 | + &mut f, | |
| 1116 | + entry, | |
| 978 | 1117 | InstKind::Load(slot), |
| 979 | 1118 | IrType::Int(IntWidth::I32), |
| 980 | 1119 | ); |
@@ -984,7 +1123,9 @@ mod tests { | ||
| 984 | 1123 | // Nothing branches to it from entry, so prune_unreachable |
| 985 | 1124 | // should remove it before the rename walk. |
| 986 | 1125 | let dead = f.create_block("dead"); |
| 987 | - let c99 = push_inst(&mut f, dead, | |
| 1126 | + let c99 = push_inst( | |
| 1127 | + &mut f, | |
| 1128 | + dead, | |
| 988 | 1129 | InstKind::ConstInt(99, IntWidth::I32), |
| 989 | 1130 | IrType::Int(IntWidth::I32), |
| 990 | 1131 | ); |
@@ -995,22 +1136,38 @@ mod tests { | ||
| 995 | 1136 | |
| 996 | 1137 | assert!(Mem2Reg.run(&mut m)); |
| 997 | 1138 | let errs = verify_module(&m); |
| 998 | - assert!(errs.is_empty(), | |
| 999 | - "post-mem2reg IR invalid (unreachable store regression): {:?}", errs); | |
| 1139 | + assert!( | |
| 1140 | + errs.is_empty(), | |
| 1141 | + "post-mem2reg IR invalid (unreachable store regression): {:?}", | |
| 1142 | + errs | |
| 1143 | + ); | |
| 1000 | 1144 | |
| 1001 | 1145 | let f = &m.functions[0]; |
| 1002 | 1146 | // The dead block must be gone (pruned by Phase 0). |
| 1003 | - assert!(!f.blocks.iter().any(|b| b.id == dead), | |
| 1004 | - "unreachable block should be pruned by mem2reg Phase 0"); | |
| 1147 | + assert!( | |
| 1148 | + !f.blocks.iter().any(|b| b.id == dead), | |
| 1149 | + "unreachable block should be pruned by mem2reg Phase 0" | |
| 1150 | + ); | |
| 1005 | 1151 | // The promoted alloca and its load/store should be gone. |
| 1006 | 1152 | let entry_block = f.block(f.entry); |
| 1007 | - assert!(!entry_block.insts.iter().any(|i| matches!(i.kind, InstKind::Alloca(_)))); | |
| 1008 | - assert!(!entry_block.insts.iter().any(|i| matches!(i.kind, InstKind::Load(_)))); | |
| 1009 | - assert!(!entry_block.insts.iter().any(|i| matches!(i.kind, InstKind::Store(..)))); | |
| 1153 | + assert!(!entry_block | |
| 1154 | + .insts | |
| 1155 | + .iter() | |
| 1156 | + .any(|i| matches!(i.kind, InstKind::Alloca(_)))); | |
| 1157 | + assert!(!entry_block | |
| 1158 | + .insts | |
| 1159 | + .iter() | |
| 1160 | + .any(|i| matches!(i.kind, InstKind::Load(_)))); | |
| 1161 | + assert!(!entry_block | |
| 1162 | + .insts | |
| 1163 | + .iter() | |
| 1164 | + .any(|i| matches!(i.kind, InstKind::Store(..)))); | |
| 1010 | 1165 | // Return must reference c1 (the live store value). |
| 1011 | 1166 | match entry_block.terminator.as_ref().unwrap() { |
| 1012 | - Terminator::Return(Some(v)) => assert_eq!(*v, c1, | |
| 1013 | - "return should reach c1 directly, not the dead block's c99"), | |
| 1167 | + Terminator::Return(Some(v)) => assert_eq!( | |
| 1168 | + *v, c1, | |
| 1169 | + "return should reach c1 directly, not the dead block's c99" | |
| 1170 | + ), | |
| 1014 | 1171 | _ => panic!(), |
| 1015 | 1172 | } |
| 1016 | 1173 | } |
@@ -1025,11 +1182,15 @@ mod tests { | ||
| 1025 | 1182 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1026 | 1183 | let entry = f.entry; |
| 1027 | 1184 | |
| 1028 | - let slot = push_inst(&mut f, entry, | |
| 1185 | + let slot = push_inst( | |
| 1186 | + &mut f, | |
| 1187 | + entry, | |
| 1029 | 1188 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1030 | 1189 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1031 | 1190 | ); |
| 1032 | - let c1 = push_inst(&mut f, entry, | |
| 1191 | + let c1 = push_inst( | |
| 1192 | + &mut f, | |
| 1193 | + entry, | |
| 1033 | 1194 | InstKind::ConstInt(1, IntWidth::I32), |
| 1034 | 1195 | IrType::Int(IntWidth::I32), |
| 1035 | 1196 | ); |
@@ -1041,9 +1202,7 @@ mod tests { | ||
| 1041 | 1202 | // Conditional branch where both arms target `then_b`. The |
| 1042 | 1203 | // pre-fix mem2reg would visit `then_b` twice via the |
| 1043 | 1204 | // successor list and double-append the new branch args. |
| 1044 | - let cond = push_inst(&mut f, entry, | |
| 1045 | - InstKind::ConstBool(true), IrType::Bool, | |
| 1046 | - ); | |
| 1205 | + let cond = push_inst(&mut f, entry, InstKind::ConstBool(true), IrType::Bool); | |
| 1047 | 1206 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 1048 | 1207 | cond, |
| 1049 | 1208 | true_dest: then_b, |
@@ -1053,7 +1212,9 @@ mod tests { | ||
| 1053 | 1212 | }); |
| 1054 | 1213 | |
| 1055 | 1214 | // `then_b` stores a different value, then branches to merge. |
| 1056 | - let c2 = push_inst(&mut f, then_b, | |
| 1215 | + let c2 = push_inst( | |
| 1216 | + &mut f, | |
| 1217 | + then_b, | |
| 1057 | 1218 | InstKind::ConstInt(2, IntWidth::I32), |
| 1058 | 1219 | IrType::Int(IntWidth::I32), |
| 1059 | 1220 | ); |
@@ -1061,8 +1222,11 @@ mod tests { | ||
| 1061 | 1222 | f.block_mut(then_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1062 | 1223 | |
| 1063 | 1224 | // `merge` loads from slot and returns it. |
| 1064 | - let loaded = push_inst(&mut f, merge, | |
| 1065 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 1225 | + let loaded = push_inst( | |
| 1226 | + &mut f, | |
| 1227 | + merge, | |
| 1228 | + InstKind::Load(slot), | |
| 1229 | + IrType::Int(IntWidth::I32), | |
| 1066 | 1230 | ); |
| 1067 | 1231 | f.block_mut(merge).terminator = Some(Terminator::Return(Some(loaded))); |
| 1068 | 1232 | |
@@ -1070,8 +1234,11 @@ mod tests { | ||
| 1070 | 1234 | |
| 1071 | 1235 | assert!(Mem2Reg.run(&mut m)); |
| 1072 | 1236 | let errs = verify_module(&m); |
| 1073 | - assert!(errs.is_empty(), | |
| 1074 | - "post-mem2reg IR invalid (same-target CondBranch regression): {:?}", errs); | |
| 1237 | + assert!( | |
| 1238 | + errs.is_empty(), | |
| 1239 | + "post-mem2reg IR invalid (same-target CondBranch regression): {:?}", | |
| 1240 | + errs | |
| 1241 | + ); | |
| 1075 | 1242 | |
| 1076 | 1243 | // Verify the entry's CondBranch has at most ONE arg per arm |
| 1077 | 1244 | // (the new phi-arg added for the promoted slot). Pre-fix it |
@@ -1079,11 +1246,21 @@ mod tests { | ||
| 1079 | 1246 | let f = &m.functions[0]; |
| 1080 | 1247 | let entry_block = f.block(f.entry); |
| 1081 | 1248 | match entry_block.terminator.as_ref().unwrap() { |
| 1082 | - Terminator::CondBranch { true_args, false_args, .. } => { | |
| 1083 | - assert!(true_args.len() <= 1, | |
| 1084 | - "true_args double-appended: {:?}", true_args); | |
| 1085 | - assert!(false_args.len() <= 1, | |
| 1086 | - "false_args double-appended: {:?}", false_args); | |
| 1249 | + Terminator::CondBranch { | |
| 1250 | + true_args, | |
| 1251 | + false_args, | |
| 1252 | + .. | |
| 1253 | + } => { | |
| 1254 | + assert!( | |
| 1255 | + true_args.len() <= 1, | |
| 1256 | + "true_args double-appended: {:?}", | |
| 1257 | + true_args | |
| 1258 | + ); | |
| 1259 | + assert!( | |
| 1260 | + false_args.len() <= 1, | |
| 1261 | + "false_args double-appended: {:?}", | |
| 1262 | + false_args | |
| 1263 | + ); | |
| 1087 | 1264 | } |
| 1088 | 1265 | // mem2reg may have collapsed the cond_branch to a |
| 1089 | 1266 | // direct branch if the cond was constant; that's |
@@ -1115,14 +1292,20 @@ mod tests { | ||
| 1115 | 1292 | |
| 1116 | 1293 | // The dead block is unreachable; it should not appear in |
| 1117 | 1294 | // the DF map at all. |
| 1118 | - assert!(!df.contains_key(&dead), | |
| 1119 | - "DF map should not contain unreachable block, got {:?}", df); | |
| 1295 | + assert!( | |
| 1296 | + !df.contains_key(&dead), | |
| 1297 | + "DF map should not contain unreachable block, got {:?}", | |
| 1298 | + df | |
| 1299 | + ); | |
| 1120 | 1300 | // The merge block has only ONE reachable predecessor |
| 1121 | 1301 | // (entry), so it isn't a true join point and merge ∉ any |
| 1122 | 1302 | // DF set. |
| 1123 | 1303 | for (b, frontier) in &df { |
| 1124 | - assert!(!frontier.contains(&merge), | |
| 1125 | - "merge should not be in DF[{:?}]: only one reachable pred", b); | |
| 1304 | + assert!( | |
| 1305 | + !frontier.contains(&merge), | |
| 1306 | + "merge should not be in DF[{:?}]: only one reachable pred", | |
| 1307 | + b | |
| 1308 | + ); | |
| 1126 | 1309 | } |
| 1127 | 1310 | } |
| 1128 | 1311 | |
@@ -1137,32 +1320,58 @@ mod tests { | ||
| 1137 | 1320 | let mut m = Module::new("t".into()); |
| 1138 | 1321 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1139 | 1322 | let entry = f.entry; |
| 1140 | - let slot = push_inst(&mut f, entry, | |
| 1323 | + let slot = push_inst( | |
| 1324 | + &mut f, | |
| 1325 | + entry, | |
| 1141 | 1326 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1142 | 1327 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1143 | 1328 | ); |
| 1144 | 1329 | // Two loads with no intervening store. |
| 1145 | - let l1 = push_inst(&mut f, entry, | |
| 1146 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 1330 | + let l1 = push_inst( | |
| 1331 | + &mut f, | |
| 1332 | + entry, | |
| 1333 | + InstKind::Load(slot), | |
| 1334 | + IrType::Int(IntWidth::I32), | |
| 1147 | 1335 | ); |
| 1148 | - let _ = push_inst(&mut f, entry, | |
| 1149 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 1336 | + let _ = push_inst( | |
| 1337 | + &mut f, | |
| 1338 | + entry, | |
| 1339 | + InstKind::Load(slot), | |
| 1340 | + IrType::Int(IntWidth::I32), | |
| 1150 | 1341 | ); |
| 1151 | 1342 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(l1))); |
| 1152 | 1343 | m.add_function(f); |
| 1153 | 1344 | |
| 1154 | - assert!(Mem2Reg.run(&mut m), "no-store alloca should still be promotable"); | |
| 1345 | + assert!( | |
| 1346 | + Mem2Reg.run(&mut m), | |
| 1347 | + "no-store alloca should still be promotable" | |
| 1348 | + ); | |
| 1155 | 1349 | let errs = verify_module(&m); |
| 1156 | 1350 | assert!(errs.is_empty(), "post-mem2reg IR invalid: {:?}", errs); |
| 1157 | 1351 | |
| 1158 | 1352 | let block = &m.functions[0].blocks[0]; |
| 1159 | - assert!(!block.insts.iter().any(|i| matches!(i.kind, InstKind::Alloca(_))), | |
| 1160 | - "alloca should be gone"); | |
| 1161 | - assert!(!block.insts.iter().any(|i| matches!(i.kind, InstKind::Load(_))), | |
| 1162 | - "loads should be gone"); | |
| 1353 | + assert!( | |
| 1354 | + !block | |
| 1355 | + .insts | |
| 1356 | + .iter() | |
| 1357 | + .any(|i| matches!(i.kind, InstKind::Alloca(_))), | |
| 1358 | + "alloca should be gone" | |
| 1359 | + ); | |
| 1360 | + assert!( | |
| 1361 | + !block | |
| 1362 | + .insts | |
| 1363 | + .iter() | |
| 1364 | + .any(|i| matches!(i.kind, InstKind::Load(_))), | |
| 1365 | + "loads should be gone" | |
| 1366 | + ); | |
| 1163 | 1367 | // Both loads should be replaced by an Undef sentinel. |
| 1164 | - assert!(block.insts.iter().any(|i| matches!(i.kind, InstKind::Undef(_))), | |
| 1165 | - "Undef sentinel should be inserted"); | |
| 1368 | + assert!( | |
| 1369 | + block | |
| 1370 | + .insts | |
| 1371 | + .iter() | |
| 1372 | + .any(|i| matches!(i.kind, InstKind::Undef(_))), | |
| 1373 | + "Undef sentinel should be inserted" | |
| 1374 | + ); | |
| 1166 | 1375 | } |
| 1167 | 1376 | |
| 1168 | 1377 | // ============================================================= |
@@ -1178,19 +1387,21 @@ mod tests { | ||
| 1178 | 1387 | let entry = f.entry; |
| 1179 | 1388 | |
| 1180 | 1389 | // bag: a Ptr<Ptr<i32>> slot that we'll write the escape into. |
| 1181 | - let bag = push_inst(&mut f, entry, | |
| 1390 | + let bag = push_inst( | |
| 1391 | + &mut f, | |
| 1392 | + entry, | |
| 1182 | 1393 | InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 1183 | 1394 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 1184 | 1395 | ); |
| 1185 | 1396 | // escapee: the alloca whose address we leak. |
| 1186 | - let escapee = push_inst(&mut f, entry, | |
| 1397 | + let escapee = push_inst( | |
| 1398 | + &mut f, | |
| 1399 | + entry, | |
| 1187 | 1400 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1188 | 1401 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1189 | 1402 | ); |
| 1190 | 1403 | // Store the escapee POINTER into bag — escapee escapes. |
| 1191 | - push_inst(&mut f, entry, | |
| 1192 | - InstKind::Store(escapee, bag), IrType::Void, | |
| 1193 | - ); | |
| 1404 | + push_inst(&mut f, entry, InstKind::Store(escapee, bag), IrType::Void); | |
| 1194 | 1405 | f.block_mut(entry).terminator = Some(Terminator::Return(None)); |
| 1195 | 1406 | m.add_function(f); |
| 1196 | 1407 | |
@@ -1202,7 +1413,9 @@ mod tests { | ||
| 1202 | 1413 | assert!(errs.is_empty(), "post-mem2reg IR invalid: {:?}", errs); |
| 1203 | 1414 | |
| 1204 | 1415 | let block = &m.functions[0].blocks[0]; |
| 1205 | - let surviving_allocas = block.insts.iter() | |
| 1416 | + let surviving_allocas = block | |
| 1417 | + .insts | |
| 1418 | + .iter() | |
| 1206 | 1419 | .filter(|i| matches!(i.kind, InstKind::Alloca(_))) |
| 1207 | 1420 | .count(); |
| 1208 | 1421 | assert!( |
@@ -1222,7 +1435,9 @@ mod tests { | ||
| 1222 | 1435 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1223 | 1436 | let entry = f.entry; |
| 1224 | 1437 | |
| 1225 | - let slot = push_inst(&mut f, entry, | |
| 1438 | + let slot = push_inst( | |
| 1439 | + &mut f, | |
| 1440 | + entry, | |
| 1226 | 1441 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1227 | 1442 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1228 | 1443 | ); |
@@ -1231,31 +1446,46 @@ mod tests { | ||
| 1231 | 1446 | let else_b = f.create_block("else"); |
| 1232 | 1447 | let merge = f.create_block("merge"); |
| 1233 | 1448 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 1234 | - cond, true_dest: then_b, true_args: vec![], | |
| 1235 | - false_dest: else_b, false_args: vec![], | |
| 1449 | + cond, | |
| 1450 | + true_dest: then_b, | |
| 1451 | + true_args: vec![], | |
| 1452 | + false_dest: else_b, | |
| 1453 | + false_args: vec![], | |
| 1236 | 1454 | }); |
| 1237 | 1455 | |
| 1238 | - let c1 = push_inst(&mut f, then_b, | |
| 1239 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32), | |
| 1456 | + let c1 = push_inst( | |
| 1457 | + &mut f, | |
| 1458 | + then_b, | |
| 1459 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1460 | + IrType::Int(IntWidth::I32), | |
| 1240 | 1461 | ); |
| 1241 | 1462 | push_inst(&mut f, then_b, InstKind::Store(c1, slot), IrType::Void); |
| 1242 | 1463 | f.block_mut(then_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1243 | 1464 | |
| 1244 | - let c2 = push_inst(&mut f, else_b, | |
| 1245 | - InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32), | |
| 1465 | + let c2 = push_inst( | |
| 1466 | + &mut f, | |
| 1467 | + else_b, | |
| 1468 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 1469 | + IrType::Int(IntWidth::I32), | |
| 1246 | 1470 | ); |
| 1247 | 1471 | push_inst(&mut f, else_b, InstKind::Store(c2, slot), IrType::Void); |
| 1248 | 1472 | f.block_mut(else_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1249 | 1473 | |
| 1250 | - let loaded = push_inst(&mut f, merge, | |
| 1251 | - InstKind::Load(slot), IrType::Int(IntWidth::I32), | |
| 1474 | + let loaded = push_inst( | |
| 1475 | + &mut f, | |
| 1476 | + merge, | |
| 1477 | + InstKind::Load(slot), | |
| 1478 | + IrType::Int(IntWidth::I32), | |
| 1252 | 1479 | ); |
| 1253 | 1480 | f.block_mut(merge).terminator = Some(Terminator::Return(Some(loaded))); |
| 1254 | 1481 | m.add_function(f); |
| 1255 | 1482 | |
| 1256 | 1483 | assert!(Mem2Reg.run(&mut m), "first run should promote"); |
| 1257 | 1484 | // Second run must be a no-op: nothing left to promote. |
| 1258 | - assert!(!Mem2Reg.run(&mut m), "second run on already-promoted IR should be a no-op"); | |
| 1485 | + assert!( | |
| 1486 | + !Mem2Reg.run(&mut m), | |
| 1487 | + "second run on already-promoted IR should be a no-op" | |
| 1488 | + ); | |
| 1259 | 1489 | let errs = verify_module(&m); |
| 1260 | 1490 | assert!(errs.is_empty(), "post-mem2reg IR invalid: {:?}", errs); |
| 1261 | 1491 | } |
@@ -1271,11 +1501,15 @@ mod tests { | ||
| 1271 | 1501 | let mut f = Function::new("f".into(), vec![], IrType::Int(IntWidth::I32)); |
| 1272 | 1502 | let entry = f.entry; |
| 1273 | 1503 | |
| 1274 | - let slot_a = push_inst(&mut f, entry, | |
| 1504 | + let slot_a = push_inst( | |
| 1505 | + &mut f, | |
| 1506 | + entry, | |
| 1275 | 1507 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1276 | 1508 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1277 | 1509 | ); |
| 1278 | - let slot_b = push_inst(&mut f, entry, | |
| 1510 | + let slot_b = push_inst( | |
| 1511 | + &mut f, | |
| 1512 | + entry, | |
| 1279 | 1513 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1280 | 1514 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1281 | 1515 | ); |
@@ -1284,35 +1518,66 @@ mod tests { | ||
| 1284 | 1518 | let else_b = f.create_block("else"); |
| 1285 | 1519 | let merge = f.create_block("merge"); |
| 1286 | 1520 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 1287 | - cond, true_dest: then_b, true_args: vec![], | |
| 1288 | - false_dest: else_b, false_args: vec![], | |
| 1521 | + cond, | |
| 1522 | + true_dest: then_b, | |
| 1523 | + true_args: vec![], | |
| 1524 | + false_dest: else_b, | |
| 1525 | + false_args: vec![], | |
| 1289 | 1526 | }); |
| 1290 | 1527 | |
| 1291 | 1528 | // then: a=1, b=10 |
| 1292 | - let c1 = push_inst(&mut f, then_b, | |
| 1293 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1294 | - let c10 = push_inst(&mut f, then_b, | |
| 1295 | - InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1529 | + let c1 = push_inst( | |
| 1530 | + &mut f, | |
| 1531 | + then_b, | |
| 1532 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1533 | + IrType::Int(IntWidth::I32), | |
| 1534 | + ); | |
| 1535 | + let c10 = push_inst( | |
| 1536 | + &mut f, | |
| 1537 | + then_b, | |
| 1538 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 1539 | + IrType::Int(IntWidth::I32), | |
| 1540 | + ); | |
| 1296 | 1541 | push_inst(&mut f, then_b, InstKind::Store(c1, slot_a), IrType::Void); |
| 1297 | 1542 | push_inst(&mut f, then_b, InstKind::Store(c10, slot_b), IrType::Void); |
| 1298 | 1543 | f.block_mut(then_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1299 | 1544 | |
| 1300 | 1545 | // else: a=2, b=20 |
| 1301 | - let c2 = push_inst(&mut f, else_b, | |
| 1302 | - InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1303 | - let c20 = push_inst(&mut f, else_b, | |
| 1304 | - InstKind::ConstInt(20, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1546 | + let c2 = push_inst( | |
| 1547 | + &mut f, | |
| 1548 | + else_b, | |
| 1549 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 1550 | + IrType::Int(IntWidth::I32), | |
| 1551 | + ); | |
| 1552 | + let c20 = push_inst( | |
| 1553 | + &mut f, | |
| 1554 | + else_b, | |
| 1555 | + InstKind::ConstInt(20, IntWidth::I32), | |
| 1556 | + IrType::Int(IntWidth::I32), | |
| 1557 | + ); | |
| 1305 | 1558 | push_inst(&mut f, else_b, InstKind::Store(c2, slot_a), IrType::Void); |
| 1306 | 1559 | push_inst(&mut f, else_b, InstKind::Store(c20, slot_b), IrType::Void); |
| 1307 | 1560 | f.block_mut(else_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1308 | 1561 | |
| 1309 | 1562 | // merge: result = a + b |
| 1310 | - let la = push_inst(&mut f, merge, | |
| 1311 | - InstKind::Load(slot_a), IrType::Int(IntWidth::I32)); | |
| 1312 | - let lb = push_inst(&mut f, merge, | |
| 1313 | - InstKind::Load(slot_b), IrType::Int(IntWidth::I32)); | |
| 1314 | - let sum = push_inst(&mut f, merge, | |
| 1315 | - InstKind::IAdd(la, lb), IrType::Int(IntWidth::I32)); | |
| 1563 | + let la = push_inst( | |
| 1564 | + &mut f, | |
| 1565 | + merge, | |
| 1566 | + InstKind::Load(slot_a), | |
| 1567 | + IrType::Int(IntWidth::I32), | |
| 1568 | + ); | |
| 1569 | + let lb = push_inst( | |
| 1570 | + &mut f, | |
| 1571 | + merge, | |
| 1572 | + InstKind::Load(slot_b), | |
| 1573 | + IrType::Int(IntWidth::I32), | |
| 1574 | + ); | |
| 1575 | + let sum = push_inst( | |
| 1576 | + &mut f, | |
| 1577 | + merge, | |
| 1578 | + InstKind::IAdd(la, lb), | |
| 1579 | + IrType::Int(IntWidth::I32), | |
| 1580 | + ); | |
| 1316 | 1581 | f.block_mut(merge).terminator = Some(Terminator::Return(Some(sum))); |
| 1317 | 1582 | m.add_function(f); |
| 1318 | 1583 | |
@@ -1322,14 +1587,21 @@ mod tests { | ||
| 1322 | 1587 | |
| 1323 | 1588 | let f = &m.functions[0]; |
| 1324 | 1589 | let merge_block = f.block(merge); |
| 1325 | - assert_eq!(merge_block.params.len(), 2, | |
| 1326 | - "merge should have 2 block params, one per promoted alloca"); | |
| 1590 | + assert_eq!( | |
| 1591 | + merge_block.params.len(), | |
| 1592 | + 2, | |
| 1593 | + "merge should have 2 block params, one per promoted alloca" | |
| 1594 | + ); | |
| 1327 | 1595 | // Each predecessor must now carry 2 branch args. |
| 1328 | 1596 | for pred in [then_b, else_b] { |
| 1329 | 1597 | let term = f.block(pred).terminator.as_ref().unwrap(); |
| 1330 | 1598 | match term { |
| 1331 | - Terminator::Branch(_, args) => assert_eq!(args.len(), 2, | |
| 1332 | - "predecessor {:?} should pass 2 args to merge", pred), | |
| 1599 | + Terminator::Branch(_, args) => assert_eq!( | |
| 1600 | + args.len(), | |
| 1601 | + 2, | |
| 1602 | + "predecessor {:?} should pass 2 args to merge", | |
| 1603 | + pred | |
| 1604 | + ), | |
| 1333 | 1605 | _ => panic!("predecessor terminator should be Branch"), |
| 1334 | 1606 | } |
| 1335 | 1607 | } |
@@ -1411,14 +1683,24 @@ mod tests { | ||
| 1411 | 1683 | InstKind::Load(save_slot), |
| 1412 | 1684 | IrType::Int(IntWidth::I32), |
| 1413 | 1685 | ); |
| 1414 | - push_inst(&mut f, then_b, InstKind::Store(then_load, tmp_slot), IrType::Void); | |
| 1686 | + push_inst( | |
| 1687 | + &mut f, | |
| 1688 | + then_b, | |
| 1689 | + InstKind::Store(then_load, tmp_slot), | |
| 1690 | + IrType::Void, | |
| 1691 | + ); | |
| 1415 | 1692 | let then_tmp = push_inst( |
| 1416 | 1693 | &mut f, |
| 1417 | 1694 | then_b, |
| 1418 | 1695 | InstKind::Load(tmp_slot), |
| 1419 | 1696 | IrType::Int(IntWidth::I32), |
| 1420 | 1697 | ); |
| 1421 | - push_inst(&mut f, then_b, InstKind::Store(then_tmp, result_slot), IrType::Void); | |
| 1698 | + push_inst( | |
| 1699 | + &mut f, | |
| 1700 | + then_b, | |
| 1701 | + InstKind::Store(then_tmp, result_slot), | |
| 1702 | + IrType::Void, | |
| 1703 | + ); | |
| 1422 | 1704 | f.block_mut(then_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1423 | 1705 | |
| 1424 | 1706 | let else_load = push_inst( |
@@ -1427,7 +1709,12 @@ mod tests { | ||
| 1427 | 1709 | InstKind::Load(save_slot), |
| 1428 | 1710 | IrType::Int(IntWidth::I32), |
| 1429 | 1711 | ); |
| 1430 | - push_inst(&mut f, else_b, InstKind::Store(else_load, tmp_slot), IrType::Void); | |
| 1712 | + push_inst( | |
| 1713 | + &mut f, | |
| 1714 | + else_b, | |
| 1715 | + InstKind::Store(else_load, tmp_slot), | |
| 1716 | + IrType::Void, | |
| 1717 | + ); | |
| 1431 | 1718 | let else_tmp = push_inst( |
| 1432 | 1719 | &mut f, |
| 1433 | 1720 | else_b, |
@@ -1446,7 +1733,12 @@ mod tests { | ||
| 1446 | 1733 | InstKind::IAdd(else_tmp, one), |
| 1447 | 1734 | IrType::Int(IntWidth::I32), |
| 1448 | 1735 | ); |
| 1449 | - push_inst(&mut f, else_b, InstKind::Store(bumped, result_slot), IrType::Void); | |
| 1736 | + push_inst( | |
| 1737 | + &mut f, | |
| 1738 | + else_b, | |
| 1739 | + InstKind::Store(bumped, result_slot), | |
| 1740 | + IrType::Void, | |
| 1741 | + ); | |
| 1450 | 1742 | f.block_mut(else_b).terminator = Some(Terminator::Branch(merge, vec![])); |
| 1451 | 1743 | |
| 1452 | 1744 | let merged = push_inst( |
@@ -1470,7 +1762,10 @@ mod tests { | ||
| 1470 | 1762 | for block in &f.blocks { |
| 1471 | 1763 | for inst in &block.insts { |
| 1472 | 1764 | assert!( |
| 1473 | - !matches!(inst.kind, InstKind::Alloca(_) | InstKind::Load(_) | InstKind::Store(_, _)), | |
| 1765 | + !matches!( | |
| 1766 | + inst.kind, | |
| 1767 | + InstKind::Alloca(_) | InstKind::Load(_) | InstKind::Store(_, _) | |
| 1768 | + ), | |
| 1474 | 1769 | "promoted branchy scalar should leave no memory traffic, found {:?}", |
| 1475 | 1770 | inst.kind |
| 1476 | 1771 | ); |
@@ -1489,12 +1784,18 @@ mod tests { | ||
| 1489 | 1784 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 1490 | 1785 | let entry = f.entry; |
| 1491 | 1786 | |
| 1492 | - let slot = push_inst(&mut f, entry, | |
| 1787 | + let slot = push_inst( | |
| 1788 | + &mut f, | |
| 1789 | + entry, | |
| 1493 | 1790 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1494 | 1791 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1495 | 1792 | ); |
| 1496 | - let c0 = push_inst(&mut f, entry, | |
| 1497 | - InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1793 | + let c0 = push_inst( | |
| 1794 | + &mut f, | |
| 1795 | + entry, | |
| 1796 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 1797 | + IrType::Int(IntWidth::I32), | |
| 1798 | + ); | |
| 1498 | 1799 | push_inst(&mut f, entry, InstKind::Store(c0, slot), IrType::Void); |
| 1499 | 1800 | |
| 1500 | 1801 | let header = f.create_block("header"); |
@@ -1503,30 +1804,65 @@ mod tests { | ||
| 1503 | 1804 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![])); |
| 1504 | 1805 | |
| 1505 | 1806 | // header: i = load slot; cmp i < 5 |
| 1506 | - let cur = push_inst(&mut f, header, | |
| 1507 | - InstKind::Load(slot), IrType::Int(IntWidth::I32)); | |
| 1508 | - let c5 = push_inst(&mut f, header, | |
| 1509 | - InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1510 | - let cmp = push_inst(&mut f, header, | |
| 1511 | - InstKind::ICmp(CmpOp::Lt, cur, c5), IrType::Bool); | |
| 1807 | + let cur = push_inst( | |
| 1808 | + &mut f, | |
| 1809 | + header, | |
| 1810 | + InstKind::Load(slot), | |
| 1811 | + IrType::Int(IntWidth::I32), | |
| 1812 | + ); | |
| 1813 | + let c5 = push_inst( | |
| 1814 | + &mut f, | |
| 1815 | + header, | |
| 1816 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 1817 | + IrType::Int(IntWidth::I32), | |
| 1818 | + ); | |
| 1819 | + let cmp = push_inst( | |
| 1820 | + &mut f, | |
| 1821 | + header, | |
| 1822 | + InstKind::ICmp(CmpOp::Lt, cur, c5), | |
| 1823 | + IrType::Bool, | |
| 1824 | + ); | |
| 1512 | 1825 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 1513 | - cond: cmp, true_dest: body, true_args: vec![], | |
| 1514 | - false_dest: exit, false_args: vec![], | |
| 1826 | + cond: cmp, | |
| 1827 | + true_dest: body, | |
| 1828 | + true_args: vec![], | |
| 1829 | + false_dest: exit, | |
| 1830 | + false_args: vec![], | |
| 1515 | 1831 | }); |
| 1516 | 1832 | |
| 1517 | 1833 | // body: store cur+1 to slot, then store (cur+1)+10 to slot. |
| 1518 | 1834 | // The SECOND store is the one that should flow to header. |
| 1519 | - let c1 = push_inst(&mut f, body, | |
| 1520 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1521 | - let cur2 = push_inst(&mut f, body, | |
| 1522 | - InstKind::Load(slot), IrType::Int(IntWidth::I32)); | |
| 1523 | - let plus1 = push_inst(&mut f, body, | |
| 1524 | - InstKind::IAdd(cur2, c1), IrType::Int(IntWidth::I32)); | |
| 1835 | + let c1 = push_inst( | |
| 1836 | + &mut f, | |
| 1837 | + body, | |
| 1838 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1839 | + IrType::Int(IntWidth::I32), | |
| 1840 | + ); | |
| 1841 | + let cur2 = push_inst( | |
| 1842 | + &mut f, | |
| 1843 | + body, | |
| 1844 | + InstKind::Load(slot), | |
| 1845 | + IrType::Int(IntWidth::I32), | |
| 1846 | + ); | |
| 1847 | + let plus1 = push_inst( | |
| 1848 | + &mut f, | |
| 1849 | + body, | |
| 1850 | + InstKind::IAdd(cur2, c1), | |
| 1851 | + IrType::Int(IntWidth::I32), | |
| 1852 | + ); | |
| 1525 | 1853 | push_inst(&mut f, body, InstKind::Store(plus1, slot), IrType::Void); |
| 1526 | - let c10 = push_inst(&mut f, body, | |
| 1527 | - InstKind::ConstInt(10, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1528 | - let plus10 = push_inst(&mut f, body, | |
| 1529 | - InstKind::IAdd(plus1, c10), IrType::Int(IntWidth::I32)); | |
| 1854 | + let c10 = push_inst( | |
| 1855 | + &mut f, | |
| 1856 | + body, | |
| 1857 | + InstKind::ConstInt(10, IntWidth::I32), | |
| 1858 | + IrType::Int(IntWidth::I32), | |
| 1859 | + ); | |
| 1860 | + let plus10 = push_inst( | |
| 1861 | + &mut f, | |
| 1862 | + body, | |
| 1863 | + InstKind::IAdd(plus1, c10), | |
| 1864 | + IrType::Int(IntWidth::I32), | |
| 1865 | + ); | |
| 1530 | 1866 | push_inst(&mut f, body, InstKind::Store(plus10, slot), IrType::Void); |
| 1531 | 1867 | f.block_mut(body).terminator = Some(Terminator::Branch(header, vec![])); |
| 1532 | 1868 | |
@@ -1541,8 +1877,14 @@ mod tests { | ||
| 1541 | 1877 | // No loads/stores/allocas anywhere. |
| 1542 | 1878 | for b in &f.blocks { |
| 1543 | 1879 | for i in &b.insts { |
| 1544 | - assert!(!matches!(i.kind, InstKind::Load(_) | InstKind::Store(..) | InstKind::Alloca(_)), | |
| 1545 | - "should be promoted away: {:?}", i.kind); | |
| 1880 | + assert!( | |
| 1881 | + !matches!( | |
| 1882 | + i.kind, | |
| 1883 | + InstKind::Load(_) | InstKind::Store(..) | InstKind::Alloca(_) | |
| 1884 | + ), | |
| 1885 | + "should be promoted away: {:?}", | |
| 1886 | + i.kind | |
| 1887 | + ); | |
| 1546 | 1888 | } |
| 1547 | 1889 | } |
| 1548 | 1890 | // body's branch back to header should pass `plus10` (the |
@@ -1551,8 +1893,10 @@ mod tests { | ||
| 1551 | 1893 | match body_term { |
| 1552 | 1894 | Terminator::Branch(_, args) => { |
| 1553 | 1895 | assert_eq!(args.len(), 1, "body should pass 1 arg to header"); |
| 1554 | - assert_eq!(args[0], plus10, | |
| 1555 | - "header arg should be the LAST store value, not the first"); | |
| 1896 | + assert_eq!( | |
| 1897 | + args[0], plus10, | |
| 1898 | + "header arg should be the LAST store value, not the first" | |
| 1899 | + ); | |
| 1556 | 1900 | } |
| 1557 | 1901 | _ => panic!("body terminator should be Branch"), |
| 1558 | 1902 | } |
@@ -1569,12 +1913,18 @@ mod tests { | ||
| 1569 | 1913 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 1570 | 1914 | let entry = f.entry; |
| 1571 | 1915 | |
| 1572 | - let slot = push_inst(&mut f, entry, | |
| 1916 | + let slot = push_inst( | |
| 1917 | + &mut f, | |
| 1918 | + entry, | |
| 1573 | 1919 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1574 | 1920 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1575 | 1921 | ); |
| 1576 | - let c0 = push_inst(&mut f, entry, | |
| 1577 | - InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1922 | + let c0 = push_inst( | |
| 1923 | + &mut f, | |
| 1924 | + entry, | |
| 1925 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 1926 | + IrType::Int(IntWidth::I32), | |
| 1927 | + ); | |
| 1578 | 1928 | push_inst(&mut f, entry, InstKind::Store(c0, slot), IrType::Void); |
| 1579 | 1929 | |
| 1580 | 1930 | let header = f.create_block("header"); |
@@ -1585,44 +1935,94 @@ mod tests { | ||
| 1585 | 1935 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![])); |
| 1586 | 1936 | |
| 1587 | 1937 | // header: i = load; cmp i < 100; cond br body / exit |
| 1588 | - let cur = push_inst(&mut f, header, | |
| 1589 | - InstKind::Load(slot), IrType::Int(IntWidth::I32)); | |
| 1590 | - let c100 = push_inst(&mut f, header, | |
| 1591 | - InstKind::ConstInt(100, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1592 | - let cmp_top = push_inst(&mut f, header, | |
| 1593 | - InstKind::ICmp(CmpOp::Lt, cur, c100), IrType::Bool); | |
| 1938 | + let cur = push_inst( | |
| 1939 | + &mut f, | |
| 1940 | + header, | |
| 1941 | + InstKind::Load(slot), | |
| 1942 | + IrType::Int(IntWidth::I32), | |
| 1943 | + ); | |
| 1944 | + let c100 = push_inst( | |
| 1945 | + &mut f, | |
| 1946 | + header, | |
| 1947 | + InstKind::ConstInt(100, IntWidth::I32), | |
| 1948 | + IrType::Int(IntWidth::I32), | |
| 1949 | + ); | |
| 1950 | + let cmp_top = push_inst( | |
| 1951 | + &mut f, | |
| 1952 | + header, | |
| 1953 | + InstKind::ICmp(CmpOp::Lt, cur, c100), | |
| 1954 | + IrType::Bool, | |
| 1955 | + ); | |
| 1594 | 1956 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 1595 | - cond: cmp_top, true_dest: body, true_args: vec![], | |
| 1596 | - false_dest: exit, false_args: vec![], | |
| 1957 | + cond: cmp_top, | |
| 1958 | + true_dest: body, | |
| 1959 | + true_args: vec![], | |
| 1960 | + false_dest: exit, | |
| 1961 | + false_args: vec![], | |
| 1597 | 1962 | }); |
| 1598 | 1963 | |
| 1599 | 1964 | // body: branch to latch_a or latch_b based on cur. |
| 1600 | - let c50 = push_inst(&mut f, body, | |
| 1601 | - InstKind::ConstInt(50, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1602 | - let cmp_mid = push_inst(&mut f, body, | |
| 1603 | - InstKind::ICmp(CmpOp::Lt, cur, c50), IrType::Bool); | |
| 1965 | + let c50 = push_inst( | |
| 1966 | + &mut f, | |
| 1967 | + body, | |
| 1968 | + InstKind::ConstInt(50, IntWidth::I32), | |
| 1969 | + IrType::Int(IntWidth::I32), | |
| 1970 | + ); | |
| 1971 | + let cmp_mid = push_inst( | |
| 1972 | + &mut f, | |
| 1973 | + body, | |
| 1974 | + InstKind::ICmp(CmpOp::Lt, cur, c50), | |
| 1975 | + IrType::Bool, | |
| 1976 | + ); | |
| 1604 | 1977 | f.block_mut(body).terminator = Some(Terminator::CondBranch { |
| 1605 | - cond: cmp_mid, true_dest: latch_a, true_args: vec![], | |
| 1606 | - false_dest: latch_b, false_args: vec![], | |
| 1978 | + cond: cmp_mid, | |
| 1979 | + true_dest: latch_a, | |
| 1980 | + true_args: vec![], | |
| 1981 | + false_dest: latch_b, | |
| 1982 | + false_args: vec![], | |
| 1607 | 1983 | }); |
| 1608 | 1984 | |
| 1609 | 1985 | // latch_a: store cur+1; jump header |
| 1610 | - let c1a = push_inst(&mut f, latch_a, | |
| 1611 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1612 | - let curla = push_inst(&mut f, latch_a, | |
| 1613 | - InstKind::Load(slot), IrType::Int(IntWidth::I32)); | |
| 1614 | - let nexta = push_inst(&mut f, latch_a, | |
| 1615 | - InstKind::IAdd(curla, c1a), IrType::Int(IntWidth::I32)); | |
| 1986 | + let c1a = push_inst( | |
| 1987 | + &mut f, | |
| 1988 | + latch_a, | |
| 1989 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 1990 | + IrType::Int(IntWidth::I32), | |
| 1991 | + ); | |
| 1992 | + let curla = push_inst( | |
| 1993 | + &mut f, | |
| 1994 | + latch_a, | |
| 1995 | + InstKind::Load(slot), | |
| 1996 | + IrType::Int(IntWidth::I32), | |
| 1997 | + ); | |
| 1998 | + let nexta = push_inst( | |
| 1999 | + &mut f, | |
| 2000 | + latch_a, | |
| 2001 | + InstKind::IAdd(curla, c1a), | |
| 2002 | + IrType::Int(IntWidth::I32), | |
| 2003 | + ); | |
| 1616 | 2004 | push_inst(&mut f, latch_a, InstKind::Store(nexta, slot), IrType::Void); |
| 1617 | 2005 | f.block_mut(latch_a).terminator = Some(Terminator::Branch(header, vec![])); |
| 1618 | 2006 | |
| 1619 | 2007 | // latch_b: store cur+2; jump header |
| 1620 | - let c2b = push_inst(&mut f, latch_b, | |
| 1621 | - InstKind::ConstInt(2, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1622 | - let curlb = push_inst(&mut f, latch_b, | |
| 1623 | - InstKind::Load(slot), IrType::Int(IntWidth::I32)); | |
| 1624 | - let nextb = push_inst(&mut f, latch_b, | |
| 1625 | - InstKind::IAdd(curlb, c2b), IrType::Int(IntWidth::I32)); | |
| 2008 | + let c2b = push_inst( | |
| 2009 | + &mut f, | |
| 2010 | + latch_b, | |
| 2011 | + InstKind::ConstInt(2, IntWidth::I32), | |
| 2012 | + IrType::Int(IntWidth::I32), | |
| 2013 | + ); | |
| 2014 | + let curlb = push_inst( | |
| 2015 | + &mut f, | |
| 2016 | + latch_b, | |
| 2017 | + InstKind::Load(slot), | |
| 2018 | + IrType::Int(IntWidth::I32), | |
| 2019 | + ); | |
| 2020 | + let nextb = push_inst( | |
| 2021 | + &mut f, | |
| 2022 | + latch_b, | |
| 2023 | + InstKind::IAdd(curlb, c2b), | |
| 2024 | + IrType::Int(IntWidth::I32), | |
| 2025 | + ); | |
| 1626 | 2026 | push_inst(&mut f, latch_b, InstKind::Store(nextb, slot), IrType::Void); |
| 1627 | 2027 | f.block_mut(latch_b).terminator = Some(Terminator::Branch(header, vec![])); |
| 1628 | 2028 | |
@@ -1635,8 +2035,11 @@ mod tests { | ||
| 1635 | 2035 | |
| 1636 | 2036 | let f = &m.functions[0]; |
| 1637 | 2037 | let header_block = f.block(header); |
| 1638 | - assert_eq!(header_block.params.len(), 1, | |
| 1639 | - "header should have 1 block param for the counter"); | |
| 2038 | + assert_eq!( | |
| 2039 | + header_block.params.len(), | |
| 2040 | + 1, | |
| 2041 | + "header should have 1 block param for the counter" | |
| 2042 | + ); | |
| 1640 | 2043 | |
| 1641 | 2044 | // Each latch's branch to header should pass exactly one arg |
| 1642 | 2045 | // (its computed `next` value). |
@@ -1669,75 +2072,156 @@ mod tests { | ||
| 1669 | 2072 | let mut f = Function::new("f".into(), vec![], IrType::Void); |
| 1670 | 2073 | let entry = f.entry; |
| 1671 | 2074 | |
| 1672 | - let outer_slot = push_inst(&mut f, entry, | |
| 2075 | + let outer_slot = push_inst( | |
| 2076 | + &mut f, | |
| 2077 | + entry, | |
| 1673 | 2078 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1674 | 2079 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1675 | 2080 | ); |
| 1676 | - let inner_slot = push_inst(&mut f, entry, | |
| 2081 | + let inner_slot = push_inst( | |
| 2082 | + &mut f, | |
| 2083 | + entry, | |
| 1677 | 2084 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 1678 | 2085 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 1679 | 2086 | ); |
| 1680 | - let c0 = push_inst(&mut f, entry, | |
| 1681 | - InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 2087 | + let c0 = push_inst( | |
| 2088 | + &mut f, | |
| 2089 | + entry, | |
| 2090 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 2091 | + IrType::Int(IntWidth::I32), | |
| 2092 | + ); | |
| 1682 | 2093 | push_inst(&mut f, entry, InstKind::Store(c0, outer_slot), IrType::Void); |
| 1683 | 2094 | |
| 1684 | 2095 | let outer_header = f.create_block("outer_header"); |
| 1685 | - let inner_init = f.create_block("inner_init"); | |
| 2096 | + let inner_init = f.create_block("inner_init"); | |
| 1686 | 2097 | let inner_header = f.create_block("inner_header"); |
| 1687 | - let inner_body = f.create_block("inner_body"); | |
| 2098 | + let inner_body = f.create_block("inner_body"); | |
| 1688 | 2099 | let outer_latch = f.create_block("outer_latch"); |
| 1689 | 2100 | let exit = f.create_block("exit"); |
| 1690 | 2101 | |
| 1691 | 2102 | f.block_mut(entry).terminator = Some(Terminator::Branch(outer_header, vec![])); |
| 1692 | 2103 | |
| 1693 | 2104 | // outer_header: i = load outer; cmp i<3; br inner_init / exit |
| 1694 | - let i = push_inst(&mut f, outer_header, | |
| 1695 | - InstKind::Load(outer_slot), IrType::Int(IntWidth::I32)); | |
| 1696 | - let c3 = push_inst(&mut f, outer_header, | |
| 1697 | - InstKind::ConstInt(3, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1698 | - let cmpo = push_inst(&mut f, outer_header, | |
| 1699 | - InstKind::ICmp(CmpOp::Lt, i, c3), IrType::Bool); | |
| 2105 | + let i = push_inst( | |
| 2106 | + &mut f, | |
| 2107 | + outer_header, | |
| 2108 | + InstKind::Load(outer_slot), | |
| 2109 | + IrType::Int(IntWidth::I32), | |
| 2110 | + ); | |
| 2111 | + let c3 = push_inst( | |
| 2112 | + &mut f, | |
| 2113 | + outer_header, | |
| 2114 | + InstKind::ConstInt(3, IntWidth::I32), | |
| 2115 | + IrType::Int(IntWidth::I32), | |
| 2116 | + ); | |
| 2117 | + let cmpo = push_inst( | |
| 2118 | + &mut f, | |
| 2119 | + outer_header, | |
| 2120 | + InstKind::ICmp(CmpOp::Lt, i, c3), | |
| 2121 | + IrType::Bool, | |
| 2122 | + ); | |
| 1700 | 2123 | f.block_mut(outer_header).terminator = Some(Terminator::CondBranch { |
| 1701 | - cond: cmpo, true_dest: inner_init, true_args: vec![], | |
| 1702 | - false_dest: exit, false_args: vec![], | |
| 2124 | + cond: cmpo, | |
| 2125 | + true_dest: inner_init, | |
| 2126 | + true_args: vec![], | |
| 2127 | + false_dest: exit, | |
| 2128 | + false_args: vec![], | |
| 1703 | 2129 | }); |
| 1704 | 2130 | |
| 1705 | 2131 | // inner_init: store 0 to inner; br inner_header |
| 1706 | - let c0i = push_inst(&mut f, inner_init, | |
| 1707 | - InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1708 | - push_inst(&mut f, inner_init, InstKind::Store(c0i, inner_slot), IrType::Void); | |
| 2132 | + let c0i = push_inst( | |
| 2133 | + &mut f, | |
| 2134 | + inner_init, | |
| 2135 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 2136 | + IrType::Int(IntWidth::I32), | |
| 2137 | + ); | |
| 2138 | + push_inst( | |
| 2139 | + &mut f, | |
| 2140 | + inner_init, | |
| 2141 | + InstKind::Store(c0i, inner_slot), | |
| 2142 | + IrType::Void, | |
| 2143 | + ); | |
| 1709 | 2144 | f.block_mut(inner_init).terminator = Some(Terminator::Branch(inner_header, vec![])); |
| 1710 | 2145 | |
| 1711 | 2146 | // inner_header: j = load inner; cmp j<5; br inner_body / outer_latch |
| 1712 | - let j = push_inst(&mut f, inner_header, | |
| 1713 | - InstKind::Load(inner_slot), IrType::Int(IntWidth::I32)); | |
| 1714 | - let c5 = push_inst(&mut f, inner_header, | |
| 1715 | - InstKind::ConstInt(5, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1716 | - let cmpi = push_inst(&mut f, inner_header, | |
| 1717 | - InstKind::ICmp(CmpOp::Lt, j, c5), IrType::Bool); | |
| 2147 | + let j = push_inst( | |
| 2148 | + &mut f, | |
| 2149 | + inner_header, | |
| 2150 | + InstKind::Load(inner_slot), | |
| 2151 | + IrType::Int(IntWidth::I32), | |
| 2152 | + ); | |
| 2153 | + let c5 = push_inst( | |
| 2154 | + &mut f, | |
| 2155 | + inner_header, | |
| 2156 | + InstKind::ConstInt(5, IntWidth::I32), | |
| 2157 | + IrType::Int(IntWidth::I32), | |
| 2158 | + ); | |
| 2159 | + let cmpi = push_inst( | |
| 2160 | + &mut f, | |
| 2161 | + inner_header, | |
| 2162 | + InstKind::ICmp(CmpOp::Lt, j, c5), | |
| 2163 | + IrType::Bool, | |
| 2164 | + ); | |
| 1718 | 2165 | f.block_mut(inner_header).terminator = Some(Terminator::CondBranch { |
| 1719 | - cond: cmpi, true_dest: inner_body, true_args: vec![], | |
| 1720 | - false_dest: outer_latch, false_args: vec![], | |
| 2166 | + cond: cmpi, | |
| 2167 | + true_dest: inner_body, | |
| 2168 | + true_args: vec![], | |
| 2169 | + false_dest: outer_latch, | |
| 2170 | + false_args: vec![], | |
| 1721 | 2171 | }); |
| 1722 | 2172 | |
| 1723 | 2173 | // inner_body: j = j + 1; store inner; br inner_header |
| 1724 | - let c1i = push_inst(&mut f, inner_body, | |
| 1725 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1726 | - let jcur = push_inst(&mut f, inner_body, | |
| 1727 | - InstKind::Load(inner_slot), IrType::Int(IntWidth::I32)); | |
| 1728 | - let jnext = push_inst(&mut f, inner_body, | |
| 1729 | - InstKind::IAdd(jcur, c1i), IrType::Int(IntWidth::I32)); | |
| 1730 | - push_inst(&mut f, inner_body, InstKind::Store(jnext, inner_slot), IrType::Void); | |
| 2174 | + let c1i = push_inst( | |
| 2175 | + &mut f, | |
| 2176 | + inner_body, | |
| 2177 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 2178 | + IrType::Int(IntWidth::I32), | |
| 2179 | + ); | |
| 2180 | + let jcur = push_inst( | |
| 2181 | + &mut f, | |
| 2182 | + inner_body, | |
| 2183 | + InstKind::Load(inner_slot), | |
| 2184 | + IrType::Int(IntWidth::I32), | |
| 2185 | + ); | |
| 2186 | + let jnext = push_inst( | |
| 2187 | + &mut f, | |
| 2188 | + inner_body, | |
| 2189 | + InstKind::IAdd(jcur, c1i), | |
| 2190 | + IrType::Int(IntWidth::I32), | |
| 2191 | + ); | |
| 2192 | + push_inst( | |
| 2193 | + &mut f, | |
| 2194 | + inner_body, | |
| 2195 | + InstKind::Store(jnext, inner_slot), | |
| 2196 | + IrType::Void, | |
| 2197 | + ); | |
| 1731 | 2198 | f.block_mut(inner_body).terminator = Some(Terminator::Branch(inner_header, vec![])); |
| 1732 | 2199 | |
| 1733 | 2200 | // outer_latch: i = i + 1; store outer; br outer_header |
| 1734 | - let c1o = push_inst(&mut f, outer_latch, | |
| 1735 | - InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 1736 | - let icur = push_inst(&mut f, outer_latch, | |
| 1737 | - InstKind::Load(outer_slot), IrType::Int(IntWidth::I32)); | |
| 1738 | - let inext = push_inst(&mut f, outer_latch, | |
| 1739 | - InstKind::IAdd(icur, c1o), IrType::Int(IntWidth::I32)); | |
| 1740 | - push_inst(&mut f, outer_latch, InstKind::Store(inext, outer_slot), IrType::Void); | |
| 2201 | + let c1o = push_inst( | |
| 2202 | + &mut f, | |
| 2203 | + outer_latch, | |
| 2204 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 2205 | + IrType::Int(IntWidth::I32), | |
| 2206 | + ); | |
| 2207 | + let icur = push_inst( | |
| 2208 | + &mut f, | |
| 2209 | + outer_latch, | |
| 2210 | + InstKind::Load(outer_slot), | |
| 2211 | + IrType::Int(IntWidth::I32), | |
| 2212 | + ); | |
| 2213 | + let inext = push_inst( | |
| 2214 | + &mut f, | |
| 2215 | + outer_latch, | |
| 2216 | + InstKind::IAdd(icur, c1o), | |
| 2217 | + IrType::Int(IntWidth::I32), | |
| 2218 | + ); | |
| 2219 | + push_inst( | |
| 2220 | + &mut f, | |
| 2221 | + outer_latch, | |
| 2222 | + InstKind::Store(inext, outer_slot), | |
| 2223 | + IrType::Void, | |
| 2224 | + ); | |
| 1741 | 2225 | f.block_mut(outer_latch).terminator = Some(Terminator::Branch(outer_header, vec![])); |
| 1742 | 2226 | |
| 1743 | 2227 | f.block_mut(exit).terminator = Some(Terminator::Return(None)); |
@@ -1764,14 +2248,24 @@ mod tests { | ||
| 1764 | 2248 | assert_eq!(f.block(outer_header).params.len(), 2, |
| 1765 | 2249 | "outer_header should have exactly 2 params (outer + inner counter via back-edge IDF), got {}", |
| 1766 | 2250 | f.block(outer_header).params.len()); |
| 1767 | - assert_eq!(f.block(inner_header).params.len(), 1, | |
| 2251 | + assert_eq!( | |
| 2252 | + f.block(inner_header).params.len(), | |
| 2253 | + 1, | |
| 1768 | 2254 | "inner_header should have exactly 1 param for inner counter, got {}", |
| 1769 | - f.block(inner_header).params.len()); | |
| 2255 | + f.block(inner_header).params.len() | |
| 2256 | + ); | |
| 1770 | 2257 | // No loads/stores/allocas anywhere — both slots fully promoted. |
| 1771 | 2258 | for b in &f.blocks { |
| 1772 | 2259 | for i in &b.insts { |
| 1773 | - assert!(!matches!(i.kind, InstKind::Load(_) | InstKind::Store(..) | InstKind::Alloca(_)), | |
| 1774 | - "{:?}: kind {:?} should be promoted away", b.id, i.kind); | |
| 2260 | + assert!( | |
| 2261 | + !matches!( | |
| 2262 | + i.kind, | |
| 2263 | + InstKind::Load(_) | InstKind::Store(..) | InstKind::Alloca(_) | |
| 2264 | + ), | |
| 2265 | + "{:?}: kind {:?} should be promoted away", | |
| 2266 | + b.id, | |
| 2267 | + i.kind | |
| 2268 | + ); | |
| 1775 | 2269 | } |
| 1776 | 2270 | } |
| 1777 | 2271 | } |
src/opt/mod.rsmodified@@ -7,42 +7,42 @@ | ||
| 7 | 7 | //! invocation is determined by the `OptLevel` selected on the command |
| 8 | 8 | //! line (`-O0` through `-Ofast`). |
| 9 | 9 | |
| 10 | -pub mod pass; | |
| 11 | -pub mod pipeline; | |
| 12 | -pub mod util; | |
| 10 | +pub mod alias; | |
| 11 | +pub mod bce; | |
| 12 | +pub mod call_resolve; | |
| 13 | +pub mod callgraph; | |
| 14 | +pub mod const_arg; | |
| 13 | 15 | pub mod const_fold; |
| 14 | 16 | pub mod const_prop; |
| 15 | -pub mod dce; | |
| 16 | 17 | pub mod cse; |
| 17 | -pub mod strength_reduce; | |
| 18 | -pub mod licm; | |
| 19 | -pub mod mem2reg; | |
| 20 | -pub mod dse; | |
| 21 | -pub mod lsf; | |
| 22 | -pub mod unroll; | |
| 23 | -pub mod loop_tree; | |
| 24 | -pub mod loop_utils; | |
| 25 | -pub mod preheader; | |
| 26 | -pub mod unswitch; | |
| 27 | -pub mod interchange; | |
| 18 | +pub mod dce; | |
| 19 | +pub mod dead_arg; | |
| 20 | +pub mod dead_func; | |
| 28 | 21 | pub mod dep_analysis; |
| 29 | -pub mod peel; | |
| 22 | +pub mod dse; | |
| 23 | +pub mod fast_math; | |
| 30 | 24 | pub mod fission; |
| 31 | 25 | pub mod fusion; |
| 32 | -pub mod call_resolve; | |
| 33 | -pub mod callgraph; | |
| 26 | +pub mod global_lsf; | |
| 27 | +pub mod gvn; | |
| 34 | 28 | pub mod inline; |
| 35 | -pub mod simplify_cfg; | |
| 36 | -pub mod dead_func; | |
| 37 | -pub mod dead_arg; | |
| 38 | -pub mod const_arg; | |
| 29 | +pub mod interchange; | |
| 30 | +pub mod licm; | |
| 31 | +pub mod loop_tree; | |
| 32 | +pub mod loop_utils; | |
| 33 | +pub mod lsf; | |
| 34 | +pub mod mem2reg; | |
| 35 | +pub mod pass; | |
| 36 | +pub mod peel; | |
| 37 | +pub mod pipeline; | |
| 38 | +pub mod preheader; | |
| 39 | 39 | pub mod return_prop; |
| 40 | -pub mod fast_math; | |
| 40 | +pub mod simplify_cfg; | |
| 41 | 41 | pub mod sroa; |
| 42 | -pub mod alias; | |
| 43 | -pub mod gvn; | |
| 44 | -pub mod global_lsf; | |
| 45 | -pub mod bce; | |
| 42 | +pub mod strength_reduce; | |
| 43 | +pub mod unroll; | |
| 44 | +pub mod unswitch; | |
| 45 | +pub mod util; | |
| 46 | 46 | pub mod vectorize; |
| 47 | 47 | |
| 48 | 48 | #[cfg(test)] |
@@ -52,7 +52,7 @@ mod audit_tests; | ||
| 52 | 52 | // driver actually uses. Audit Cos-2: previously every pass was |
| 53 | 53 | // re-exported behind `#[allow(unused_imports)]`, which masked any |
| 54 | 54 | // future regressions that orphaned a re-export. |
| 55 | -pub use pipeline::{OptLevel, build_i128_pipeline, build_pipeline}; | |
| 55 | +pub use pipeline::{build_i128_pipeline, build_pipeline, OptLevel}; | |
| 56 | 56 | |
| 57 | 57 | // Test-only re-export so audit_tests can refer to passes by their |
| 58 | 58 | // short name without the full module path. |
src/opt/pass.rsmodified@@ -95,7 +95,10 @@ impl PassManager { | ||
| 95 | 95 | } |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | - PassRunResult { change_count, iterations } | |
| 98 | + PassRunResult { | |
| 99 | + change_count, | |
| 100 | + iterations, | |
| 101 | + } | |
| 99 | 102 | } |
| 100 | 103 | |
| 101 | 104 | fn verify_or_panic(&self, module: &Module, after: &str) { |
@@ -104,10 +107,7 @@ impl PassManager { | ||
| 104 | 107 | } |
| 105 | 108 | let errors: Vec<VerifyError> = verify_module(module); |
| 106 | 109 | if !errors.is_empty() { |
| 107 | - let mut msg = format!( | |
| 108 | - "IR verifier failed after pass `{}`:\n", | |
| 109 | - after | |
| 110 | - ); | |
| 110 | + let mut msg = format!("IR verifier failed after pass `{}`:\n", after); | |
| 111 | 111 | for e in &errors { |
| 112 | 112 | msg.push_str(" - "); |
| 113 | 113 | msg.push_str(&e.msg); |
@@ -133,8 +133,12 @@ mod tests { | ||
| 133 | 133 | /// A pass that does nothing — used to test infrastructure plumbing. |
| 134 | 134 | struct NoopPass; |
| 135 | 135 | impl Pass for NoopPass { |
| 136 | - fn name(&self) -> &'static str { "noop" } | |
| 137 | - fn run(&self, _module: &mut Module) -> bool { false } | |
| 136 | + fn name(&self) -> &'static str { | |
| 137 | + "noop" | |
| 138 | + } | |
| 139 | + fn run(&self, _module: &mut Module) -> bool { | |
| 140 | + false | |
| 141 | + } | |
| 138 | 142 | } |
| 139 | 143 | |
| 140 | 144 | /// A pass that claims it changed once, then is idle. |
@@ -142,9 +146,16 @@ mod tests { | ||
| 142 | 146 | fired: std::cell::Cell<bool>, |
| 143 | 147 | } |
| 144 | 148 | impl Pass for OneShotPass { |
| 145 | - fn name(&self) -> &'static str { "oneshot" } | |
| 149 | + fn name(&self) -> &'static str { | |
| 150 | + "oneshot" | |
| 151 | + } | |
| 146 | 152 | fn run(&self, _module: &mut Module) -> bool { |
| 147 | - if self.fired.get() { false } else { self.fired.set(true); true } | |
| 153 | + if self.fired.get() { | |
| 154 | + false | |
| 155 | + } else { | |
| 156 | + self.fired.set(true); | |
| 157 | + true | |
| 158 | + } | |
| 148 | 159 | } |
| 149 | 160 | } |
| 150 | 161 | |
@@ -182,7 +193,9 @@ mod tests { | ||
| 182 | 193 | #[test] |
| 183 | 194 | fn oneshot_runs_then_terminates() { |
| 184 | 195 | let mut pm = PassManager::new(); |
| 185 | - pm.add(Box::new(OneShotPass { fired: std::cell::Cell::new(false) })); | |
| 196 | + pm.add(Box::new(OneShotPass { | |
| 197 | + fired: std::cell::Cell::new(false), | |
| 198 | + })); | |
| 186 | 199 | let mut m = empty_module(); |
| 187 | 200 | let r = pm.run(&mut m); |
| 188 | 201 | assert_eq!(r.change_count, 1); |
src/opt/peel.rsmodified@@ -12,22 +12,26 @@ | ||
| 12 | 12 | //! peeled iteration. Later const-prop folds `iv=init_const` through the |
| 13 | 13 | //! clone, turning `if (i==1)` into `if (true)` and eliminating dead code. |
| 14 | 14 | |
| 15 | -use std::collections::HashSet; | |
| 15 | +use super::loop_utils::{clone_loop, find_preheader, loop_defined_values, resolve_const_int}; | |
| 16 | +use super::pass::Pass; | |
| 16 | 17 | use crate::ir::inst::*; |
| 17 | 18 | use crate::ir::types::IrType; |
| 18 | 19 | use crate::ir::walk::{find_natural_loops, predecessors}; |
| 19 | -use super::loop_utils::{find_preheader, resolve_const_int, loop_defined_values, clone_loop}; | |
| 20 | -use super::pass::Pass; | |
| 20 | +use std::collections::HashSet; | |
| 21 | 21 | |
| 22 | 22 | pub struct LoopPeel; |
| 23 | 23 | |
| 24 | 24 | impl Pass for LoopPeel { |
| 25 | - fn name(&self) -> &'static str { "loop-peel" } | |
| 25 | + fn name(&self) -> &'static str { | |
| 26 | + "loop-peel" | |
| 27 | + } | |
| 26 | 28 | |
| 27 | 29 | fn run(&self, module: &mut Module) -> bool { |
| 28 | 30 | let mut changed = false; |
| 29 | 31 | for func in &mut module.functions { |
| 30 | - if peel_in_function(func) { changed = true; } | |
| 32 | + if peel_in_function(func) { | |
| 33 | + changed = true; | |
| 34 | + } | |
| 31 | 35 | } |
| 32 | 36 | changed |
| 33 | 37 | } |
@@ -38,31 +42,41 @@ fn peel_in_function(func: &mut Function) -> bool { | ||
| 38 | 42 | let preds = predecessors(func); |
| 39 | 43 | |
| 40 | 44 | for lp in &loops { |
| 41 | - let Some(ph_id) = find_preheader(func, lp, &preds) else { continue }; | |
| 45 | + let Some(ph_id) = find_preheader(func, lp, &preds) else { | |
| 46 | + continue; | |
| 47 | + }; | |
| 42 | 48 | |
| 43 | 49 | // Header must have exactly 1 block param (the IV). |
| 44 | 50 | let hdr = func.block(lp.header); |
| 45 | - if hdr.params.len() != 1 { continue; } | |
| 51 | + if hdr.params.len() != 1 { | |
| 52 | + continue; | |
| 53 | + } | |
| 46 | 54 | let iv = hdr.params[0].id; |
| 47 | 55 | |
| 48 | 56 | // Get the init value from the preheader's branch to header. |
| 49 | 57 | let init_val = match &func.block(ph_id).terminator { |
| 50 | - Some(Terminator::Branch(dest, args)) if *dest == lp.header && args.len() == 1 => | |
| 51 | - args[0], | |
| 58 | + Some(Terminator::Branch(dest, args)) if *dest == lp.header && args.len() == 1 => { | |
| 59 | + args[0] | |
| 60 | + } | |
| 52 | 61 | _ => continue, |
| 53 | 62 | }; |
| 54 | 63 | |
| 55 | 64 | // Init must be a compile-time constant. |
| 56 | - let Some(init_const) = resolve_const_int(func, init_val) else { continue }; | |
| 65 | + let Some(init_const) = resolve_const_int(func, init_val) else { | |
| 66 | + continue; | |
| 67 | + }; | |
| 57 | 68 | |
| 58 | 69 | // Must have a single latch. |
| 59 | - if lp.latches.len() != 1 { continue; } | |
| 70 | + if lp.latches.len() != 1 { | |
| 71 | + continue; | |
| 72 | + } | |
| 60 | 73 | let latch_id = lp.latches[0]; |
| 61 | 74 | |
| 62 | 75 | // Latch must branch back to header with one arg. |
| 63 | 76 | let next_iv = match &func.block(latch_id).terminator { |
| 64 | - Some(Terminator::Branch(dest, args)) if *dest == lp.header && args.len() == 1 => | |
| 65 | - args[0], | |
| 77 | + Some(Terminator::Branch(dest, args)) if *dest == lp.header && args.len() == 1 => { | |
| 78 | + args[0] | |
| 79 | + } | |
| 66 | 80 | _ => continue, |
| 67 | 81 | }; |
| 68 | 82 | |
@@ -72,8 +86,11 @@ fn peel_in_function(func: &mut Function) -> bool { | ||
| 72 | 86 | for inst in &func.block(latch_id).insts { |
| 73 | 87 | if inst.id == next_iv { |
| 74 | 88 | if let InstKind::IAdd(a, b) = &inst.kind { |
| 75 | - if *a == iv { found = resolve_const_int(func, *b); } | |
| 76 | - else if *b == iv { found = resolve_const_int(func, *a); } | |
| 89 | + if *a == iv { | |
| 90 | + found = resolve_const_int(func, *b); | |
| 91 | + } else if *b == iv { | |
| 92 | + found = resolve_const_int(func, *a); | |
| 93 | + } | |
| 77 | 94 | } |
| 78 | 95 | break; |
| 79 | 96 | } |
@@ -116,19 +133,29 @@ fn has_first_iter_conditional( | ||
| 116 | 133 | for inst in &block.insts { |
| 117 | 134 | if let InstKind::ICmp(CmpOp::Eq, a, b) = &inst.kind { |
| 118 | 135 | // One operand must be the IV. |
| 119 | - let (is_iv, other) = if *a == iv { (true, *b) } | |
| 120 | - else if *b == iv { (true, *a) } | |
| 121 | - else { (false, ValueId(0)) }; | |
| 122 | - if !is_iv { continue; } | |
| 136 | + let (is_iv, other) = if *a == iv { | |
| 137 | + (true, *b) | |
| 138 | + } else if *b == iv { | |
| 139 | + (true, *a) | |
| 140 | + } else { | |
| 141 | + (false, ValueId(0)) | |
| 142 | + }; | |
| 143 | + if !is_iv { | |
| 144 | + continue; | |
| 145 | + } | |
| 123 | 146 | |
| 124 | 147 | // The other operand must resolve to the init constant |
| 125 | 148 | // and be loop-invariant. |
| 126 | - if loop_defs.contains(&other) { continue; } | |
| 149 | + if loop_defs.contains(&other) { | |
| 150 | + continue; | |
| 151 | + } | |
| 127 | 152 | if let Some(c) = resolve_const_int(func, other) { |
| 128 | 153 | if c == init_const { |
| 129 | 154 | // Must feed a CondBranch in this block. |
| 130 | 155 | if let Some(Terminator::CondBranch { cond, .. }) = &block.terminator { |
| 131 | - if *cond == inst.id { return true; } | |
| 156 | + if *cond == inst.id { | |
| 157 | + return true; | |
| 158 | + } | |
| 132 | 159 | } |
| 133 | 160 | } |
| 134 | 161 | } |
@@ -152,10 +179,19 @@ fn find_loop_exit(func: &Function, lp: &crate::ir::walk::NaturalLoop) -> Option< | ||
| 152 | 179 | } |
| 153 | 180 | |
| 154 | 181 | /// Get the args passed to the exit block from the cmp's false-branch. |
| 155 | -fn find_exit_args(func: &Function, lp: &crate::ir::walk::NaturalLoop, exit_id: BlockId) -> Vec<ValueId> { | |
| 182 | +fn find_exit_args( | |
| 183 | + func: &Function, | |
| 184 | + lp: &crate::ir::walk::NaturalLoop, | |
| 185 | + exit_id: BlockId, | |
| 186 | +) -> Vec<ValueId> { | |
| 156 | 187 | for &bid in &lp.body { |
| 157 | 188 | let block = func.block(bid); |
| 158 | - if let Some(Terminator::CondBranch { false_dest, false_args, .. }) = &block.terminator { | |
| 189 | + if let Some(Terminator::CondBranch { | |
| 190 | + false_dest, | |
| 191 | + false_args, | |
| 192 | + .. | |
| 193 | + }) = &block.terminator | |
| 194 | + { | |
| 159 | 195 | if *false_dest == exit_id { |
| 160 | 196 | return false_args.clone(); |
| 161 | 197 | } |
@@ -186,7 +222,10 @@ fn do_peel( | ||
| 186 | 222 | // The IV type (needed to emit the init+stride constant). |
| 187 | 223 | let hdr = func.block(lp.header); |
| 188 | 224 | let iv_ty = hdr.params[0].ty.clone(); |
| 189 | - let iv_width = match &iv_ty { IrType::Int(w) => *w, _ => return }; | |
| 225 | + let iv_width = match &iv_ty { | |
| 226 | + IrType::Int(w) => *w, | |
| 227 | + _ => return, | |
| 228 | + }; | |
| 190 | 229 | let init_const = resolve_const_int(func, init_val).unwrap(); |
| 191 | 230 | |
| 192 | 231 | // Emit init+stride constant in a helper block so it's available. |
@@ -247,8 +286,7 @@ fn do_peel( | ||
| 247 | 286 | |
| 248 | 287 | // --- Wire preheader → clone's header --- |
| 249 | 288 | let clone_header = block_map[&lp.header]; |
| 250 | - func.block_mut(ph_id).terminator = | |
| 251 | - Some(Terminator::Branch(clone_header, vec![init_val])); | |
| 289 | + func.block_mut(ph_id).terminator = Some(Terminator::Branch(clone_header, vec![init_val])); | |
| 252 | 290 | } |
| 253 | 291 | |
| 254 | 292 | // --------------------------------------------------------------------------- |
@@ -258,14 +296,18 @@ fn do_peel( | ||
| 258 | 296 | #[cfg(test)] |
| 259 | 297 | mod tests { |
| 260 | 298 | use super::*; |
| 261 | - use crate::ir::types::{IrType, IntWidth}; | |
| 262 | 299 | use crate::ir::inst::*; |
| 300 | + use crate::ir::types::{IntWidth, IrType}; | |
| 301 | + use crate::lexer::{Position, Span}; | |
| 263 | 302 | use crate::opt::pass::Pass; |
| 264 | - use crate::lexer::{Span, Position}; | |
| 265 | 303 | |
| 266 | 304 | fn span() -> Span { |
| 267 | 305 | let pos = Position { line: 0, col: 0 }; |
| 268 | - Span { file_id: 0, start: pos, end: pos } | |
| 306 | + Span { | |
| 307 | + file_id: 0, | |
| 308 | + start: pos, | |
| 309 | + end: pos, | |
| 310 | + } | |
| 269 | 311 | } |
| 270 | 312 | |
| 271 | 313 | #[test] |
@@ -295,45 +337,60 @@ mod tests { | ||
| 295 | 337 | let c1 = f.next_value_id(); |
| 296 | 338 | f.register_type(c1, IrType::Int(IntWidth::I32)); |
| 297 | 339 | f.block_mut(entry).insts.push(Inst { |
| 298 | - id: c1, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 340 | + id: c1, | |
| 341 | + ty: IrType::Int(IntWidth::I32), | |
| 342 | + span: span(), | |
| 299 | 343 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 300 | 344 | }); |
| 301 | 345 | let c10 = f.next_value_id(); |
| 302 | 346 | f.register_type(c10, IrType::Int(IntWidth::I32)); |
| 303 | 347 | f.block_mut(entry).insts.push(Inst { |
| 304 | - id: c10, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 348 | + id: c10, | |
| 349 | + ty: IrType::Int(IntWidth::I32), | |
| 350 | + span: span(), | |
| 305 | 351 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 306 | 352 | }); |
| 307 | 353 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![c1])); |
| 308 | 354 | |
| 309 | 355 | let iv = f.next_value_id(); |
| 310 | 356 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 311 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 357 | + f.block_mut(header).params.push(BlockParam { | |
| 358 | + id: iv, | |
| 359 | + ty: IrType::Int(IntWidth::I32), | |
| 360 | + }); | |
| 312 | 361 | f.block_mut(header).terminator = Some(Terminator::Branch(cmp, vec![])); |
| 313 | 362 | |
| 314 | 363 | let cmp_v = f.next_value_id(); |
| 315 | 364 | f.register_type(cmp_v, IrType::Bool); |
| 316 | 365 | f.block_mut(cmp).insts.push(Inst { |
| 317 | - id: cmp_v, ty: IrType::Bool, span: span(), | |
| 366 | + id: cmp_v, | |
| 367 | + ty: IrType::Bool, | |
| 368 | + span: span(), | |
| 318 | 369 | kind: InstKind::ICmp(CmpOp::Le, iv, c10), |
| 319 | 370 | }); |
| 320 | 371 | f.block_mut(cmp).terminator = Some(Terminator::CondBranch { |
| 321 | 372 | cond: cmp_v, |
| 322 | - true_dest: body, true_args: vec![], | |
| 323 | - false_dest: exit, false_args: vec![], | |
| 373 | + true_dest: body, | |
| 374 | + true_args: vec![], | |
| 375 | + false_dest: exit, | |
| 376 | + false_args: vec![], | |
| 324 | 377 | }); |
| 325 | 378 | |
| 326 | 379 | // Body has NO equality check — just a store. |
| 327 | 380 | let alloca = f.next_value_id(); |
| 328 | 381 | f.register_type(alloca, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 329 | 382 | f.block_mut(body).insts.push(Inst { |
| 330 | - id: alloca, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 383 | + id: alloca, | |
| 384 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 385 | + span: span(), | |
| 331 | 386 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 332 | 387 | }); |
| 333 | 388 | let store_id = f.next_value_id(); |
| 334 | 389 | f.register_type(store_id, IrType::Void); |
| 335 | 390 | f.block_mut(body).insts.push(Inst { |
| 336 | - id: store_id, ty: IrType::Void, span: span(), | |
| 391 | + id: store_id, | |
| 392 | + ty: IrType::Void, | |
| 393 | + span: span(), | |
| 337 | 394 | kind: InstKind::Store(iv, alloca), |
| 338 | 395 | }); |
| 339 | 396 | f.block_mut(body).terminator = Some(Terminator::Branch(latch, vec![])); |
@@ -341,7 +398,9 @@ mod tests { | ||
| 341 | 398 | let nxt = f.next_value_id(); |
| 342 | 399 | f.register_type(nxt, IrType::Int(IntWidth::I32)); |
| 343 | 400 | f.block_mut(latch).insts.push(Inst { |
| 344 | - id: nxt, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 401 | + id: nxt, | |
| 402 | + ty: IrType::Int(IntWidth::I32), | |
| 403 | + span: span(), | |
| 345 | 404 | kind: InstKind::IAdd(iv, c1), |
| 346 | 405 | }); |
| 347 | 406 | f.block_mut(latch).terminator = Some(Terminator::Branch(header, vec![nxt])); |
src/opt/pipeline.rsmodified@@ -4,35 +4,35 @@ | ||
| 4 | 4 | //! configured `PassManager`. Adding a new pass to a level is a one-line |
| 5 | 5 | //! change here, which keeps the dispatch logic in one place. |
| 6 | 6 | |
| 7 | -use super::pass::PassManager; | |
| 7 | +use super::bce::Bce; | |
| 8 | +use super::call_resolve::CallResolve; | |
| 9 | +use super::const_arg::ConstArgSpecialize; | |
| 8 | 10 | use super::const_fold::ConstFold; |
| 9 | 11 | use super::const_prop::ConstProp; |
| 10 | -use super::dce::Dce; | |
| 11 | 12 | use super::cse::LocalCse; |
| 12 | -use super::strength_reduce::StrengthReduce; | |
| 13 | -use super::licm::Licm; | |
| 14 | -use super::mem2reg::Mem2Reg; | |
| 13 | +use super::dce::Dce; | |
| 14 | +use super::dead_arg::DeadArgElim; | |
| 15 | +use super::dead_func::DeadFuncElim; | |
| 15 | 16 | use super::dse::Dse; |
| 16 | -use super::lsf::LocalLsf; | |
| 17 | -use super::unroll::LoopUnroll; | |
| 18 | -use super::preheader::PreheaderInsert; | |
| 19 | -use super::unswitch::LoopUnswitch; | |
| 17 | +use super::fast_math::FastMathReassoc; | |
| 18 | +use super::fission::LoopFission; | |
| 19 | +use super::fusion::LoopFusion; | |
| 20 | +use super::global_lsf::GlobalLsf; | |
| 21 | +use super::gvn::Gvn; | |
| 22 | +use super::inline::Inline; | |
| 20 | 23 | use super::interchange::LoopInterchange; |
| 24 | +use super::licm::Licm; | |
| 25 | +use super::lsf::LocalLsf; | |
| 26 | +use super::mem2reg::Mem2Reg; | |
| 27 | +use super::pass::PassManager; | |
| 21 | 28 | use super::peel::LoopPeel; |
| 22 | -use super::call_resolve::CallResolve; | |
| 23 | -use super::inline::Inline; | |
| 24 | -use super::simplify_cfg::SimplifyCfg; | |
| 25 | -use super::dead_func::DeadFuncElim; | |
| 26 | -use super::dead_arg::DeadArgElim; | |
| 27 | -use super::const_arg::ConstArgSpecialize; | |
| 29 | +use super::preheader::PreheaderInsert; | |
| 28 | 30 | use super::return_prop::ReturnPropagate; |
| 29 | -use super::fast_math::FastMathReassoc; | |
| 31 | +use super::simplify_cfg::SimplifyCfg; | |
| 30 | 32 | use super::sroa::Sroa; |
| 31 | -use super::gvn::Gvn; | |
| 32 | -use super::global_lsf::GlobalLsf; | |
| 33 | -use super::bce::Bce; | |
| 34 | -use super::fission::LoopFission; | |
| 35 | -use super::fusion::LoopFusion; | |
| 33 | +use super::strength_reduce::StrengthReduce; | |
| 34 | +use super::unroll::LoopUnroll; | |
| 35 | +use super::unswitch::LoopUnswitch; | |
| 36 | 36 | use super::vectorize::Vectorize; |
| 37 | 37 | |
| 38 | 38 | /// Compiler optimization levels. |
@@ -90,7 +90,10 @@ impl OptLevel { | ||
| 90 | 90 | /// builder below will gate registration on this. Same for the |
| 91 | 91 | /// other two predicates. |
| 92 | 92 | pub fn inlining(self) -> bool { |
| 93 | - matches!(self, Self::O1 | Self::O2 | Self::O3 | Self::Os | Self::Ofast) | |
| 93 | + matches!( | |
| 94 | + self, | |
| 95 | + Self::O1 | Self::O2 | Self::O3 | Self::Os | Self::Ofast | |
| 96 | + ) | |
| 94 | 97 | } |
| 95 | 98 | |
| 96 | 99 | /// Does this level enable loop vectorization (NEON)? |
@@ -145,7 +148,7 @@ pub fn build_pipeline(level: OptLevel) -> PassManager { | ||
| 145 | 148 | pm.add(Box::new(CallResolve)); |
| 146 | 149 | pm.add(Box::new(Mem2Reg)); |
| 147 | 150 | pm.add(Box::new(ConstFold)); |
| 148 | - pm.add(Box::new(Sroa)); // after SSA + const fold (GCC pattern) | |
| 151 | + pm.add(Box::new(Sroa)); // after SSA + const fold (GCC pattern) | |
| 149 | 152 | pm.add(Box::new(Mem2Reg)); // re-promote SROA-created scalar allocas |
| 150 | 153 | pm.add(Box::new(Inline::for_level(OptLevel::O2))); |
| 151 | 154 | pm.add(Box::new(ConstArgSpecialize)); |
@@ -168,7 +171,7 @@ pub fn build_pipeline(level: OptLevel) -> PassManager { | ||
| 168 | 171 | pm.add(Box::new(LoopFission)); |
| 169 | 172 | pm.add(Box::new(LoopFusion)); |
| 170 | 173 | pm.add(Box::new(LoopUnroll)); |
| 171 | - pm.add(Box::new(Gvn)); // after loop passes to avoid SSA conflicts | |
| 174 | + pm.add(Box::new(Gvn)); // after loop passes to avoid SSA conflicts | |
| 172 | 175 | pm.add(Box::new(Dce)); |
| 173 | 176 | } |
| 174 | 177 | OptLevel::Os => { |
@@ -314,9 +317,19 @@ mod tests { | ||
| 314 | 317 | fn pipelines_build() { |
| 315 | 318 | // O0 has no passes; every other level has at least one. |
| 316 | 319 | assert!(build_pipeline(OptLevel::O0).is_empty()); |
| 317 | - for lvl in [OptLevel::O1, OptLevel::O2, OptLevel::O3, OptLevel::Os, OptLevel::Ofast] { | |
| 320 | + for lvl in [ | |
| 321 | + OptLevel::O1, | |
| 322 | + OptLevel::O2, | |
| 323 | + OptLevel::O3, | |
| 324 | + OptLevel::Os, | |
| 325 | + OptLevel::Ofast, | |
| 326 | + ] { | |
| 318 | 327 | let pm = build_pipeline(lvl); |
| 319 | - assert!(!pm.is_empty(), "pipeline {:?} should have at least one pass", lvl); | |
| 328 | + assert!( | |
| 329 | + !pm.is_empty(), | |
| 330 | + "pipeline {:?} should have at least one pass", | |
| 331 | + lvl | |
| 332 | + ); | |
| 320 | 333 | } |
| 321 | 334 | } |
| 322 | 335 | |
src/opt/preheader.rsmodified@@ -7,21 +7,25 @@ | ||
| 7 | 7 | //! Run early at O2+ so downstream passes (LICM, unswitching, interchange) |
| 8 | 8 | //! can assume every loop has a preheader. |
| 9 | 9 | |
| 10 | -use crate::ir::inst::*; | |
| 11 | -use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 12 | 10 | use super::loop_utils::find_preheader; |
| 13 | 11 | use super::pass::Pass; |
| 12 | +use crate::ir::inst::*; | |
| 13 | +use crate::ir::walk::{find_natural_loops, predecessors}; | |
| 14 | 14 | |
| 15 | 15 | /// Preheader insertion pass. |
| 16 | 16 | pub struct PreheaderInsert; |
| 17 | 17 | |
| 18 | 18 | impl Pass for PreheaderInsert { |
| 19 | - fn name(&self) -> &'static str { "preheader-insert" } | |
| 19 | + fn name(&self) -> &'static str { | |
| 20 | + "preheader-insert" | |
| 21 | + } | |
| 20 | 22 | |
| 21 | 23 | fn run(&self, module: &mut Module) -> bool { |
| 22 | 24 | let mut changed = false; |
| 23 | 25 | for func in &mut module.functions { |
| 24 | - if insert_preheaders(func) { changed = true; } | |
| 26 | + if insert_preheaders(func) { | |
| 27 | + changed = true; | |
| 28 | + } | |
| 25 | 29 | } |
| 26 | 30 | changed |
| 27 | 31 | } |
@@ -47,14 +51,17 @@ fn insert_preheaders(func: &mut Function) -> bool { | ||
| 47 | 51 | Some(p) => p, |
| 48 | 52 | None => continue, |
| 49 | 53 | }; |
| 50 | - let mut outside: Vec<BlockId> = header_preds.iter() | |
| 54 | + let mut outside: Vec<BlockId> = header_preds | |
| 55 | + .iter() | |
| 51 | 56 | .copied() |
| 52 | 57 | .filter(|p| !lp.body.contains(p)) |
| 53 | 58 | .collect(); |
| 54 | 59 | outside.sort_by_key(|b| b.0); |
| 55 | 60 | outside.dedup(); |
| 56 | 61 | |
| 57 | - if outside.is_empty() { continue; } | |
| 62 | + if outside.is_empty() { | |
| 63 | + continue; | |
| 64 | + } | |
| 58 | 65 | |
| 59 | 66 | // Create the preheader block. It receives the same block params |
| 60 | 67 | // as the header and unconditionally branches to the header, |
@@ -77,8 +84,7 @@ fn insert_preheaders(func: &mut Function) -> bool { | ||
| 77 | 84 | |
| 78 | 85 | // Preheader terminates with unconditional branch to header, |
| 79 | 86 | // passing its params through. |
| 80 | - func.block_mut(ph_id).terminator = | |
| 81 | - Some(Terminator::Branch(lp.header, ph_param_ids)); | |
| 87 | + func.block_mut(ph_id).terminator = Some(Terminator::Branch(lp.header, ph_param_ids)); | |
| 82 | 88 | |
| 83 | 89 | // Redirect each out-of-loop predecessor to branch to the |
| 84 | 90 | // preheader instead of the header. |
@@ -103,16 +109,30 @@ fn redirect_terminator(func: &mut Function, block: BlockId, old_dest: BlockId, n | ||
| 103 | 109 | }; |
| 104 | 110 | match term { |
| 105 | 111 | Terminator::Branch(dest, _) => { |
| 106 | - if *dest == old_dest { *dest = new_dest; } | |
| 112 | + if *dest == old_dest { | |
| 113 | + *dest = new_dest; | |
| 114 | + } | |
| 107 | 115 | } |
| 108 | - Terminator::CondBranch { true_dest, false_dest, .. } => { | |
| 109 | - if *true_dest == old_dest { *true_dest = new_dest; } | |
| 110 | - if *false_dest == old_dest { *false_dest = new_dest; } | |
| 116 | + Terminator::CondBranch { | |
| 117 | + true_dest, | |
| 118 | + false_dest, | |
| 119 | + .. | |
| 120 | + } => { | |
| 121 | + if *true_dest == old_dest { | |
| 122 | + *true_dest = new_dest; | |
| 123 | + } | |
| 124 | + if *false_dest == old_dest { | |
| 125 | + *false_dest = new_dest; | |
| 126 | + } | |
| 111 | 127 | } |
| 112 | 128 | Terminator::Switch { default, cases, .. } => { |
| 113 | - if *default == old_dest { *default = new_dest; } | |
| 129 | + if *default == old_dest { | |
| 130 | + *default = new_dest; | |
| 131 | + } | |
| 114 | 132 | for (_, dest) in cases.iter_mut() { |
| 115 | - if *dest == old_dest { *dest = new_dest; } | |
| 133 | + if *dest == old_dest { | |
| 134 | + *dest = new_dest; | |
| 135 | + } | |
| 116 | 136 | } |
| 117 | 137 | } |
| 118 | 138 | _ => {} |
@@ -126,13 +146,17 @@ fn redirect_terminator(func: &mut Function, block: BlockId, old_dest: BlockId, n | ||
| 126 | 146 | #[cfg(test)] |
| 127 | 147 | mod tests { |
| 128 | 148 | use super::*; |
| 129 | - use crate::ir::types::{IrType, IntWidth}; | |
| 149 | + use crate::ir::types::{IntWidth, IrType}; | |
| 130 | 150 | use crate::ir::walk::predecessors; |
| 131 | - use crate::lexer::{Span, Position}; | |
| 151 | + use crate::lexer::{Position, Span}; | |
| 132 | 152 | |
| 133 | 153 | fn span() -> Span { |
| 134 | 154 | let pos = Position { line: 0, col: 0 }; |
| 135 | - Span { file_id: 0, start: pos, end: pos } | |
| 155 | + Span { | |
| 156 | + file_id: 0, | |
| 157 | + start: pos, | |
| 158 | + end: pos, | |
| 159 | + } | |
| 136 | 160 | } |
| 137 | 161 | |
| 138 | 162 | /// Build a function where the loop header has TWO out-of-loop |
@@ -142,36 +166,44 @@ mod tests { | ||
| 142 | 166 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 143 | 167 | |
| 144 | 168 | let header = f.create_block("header"); |
| 145 | - let body = f.create_block("body"); | |
| 146 | - let latch = f.create_block("latch"); | |
| 147 | - let exit = f.create_block("exit"); | |
| 148 | - let alt = f.create_block("alt_entry"); | |
| 149 | - let entry = f.entry; | |
| 169 | + let body = f.create_block("body"); | |
| 170 | + let latch = f.create_block("latch"); | |
| 171 | + let exit = f.create_block("exit"); | |
| 172 | + let alt = f.create_block("alt_entry"); | |
| 173 | + let entry = f.entry; | |
| 150 | 174 | |
| 151 | 175 | // Entry: const 1, condBr → header(1) or alt_entry |
| 152 | 176 | let c1 = f.next_value_id(); |
| 153 | 177 | f.register_type(c1, IrType::Int(IntWidth::I32)); |
| 154 | 178 | f.block_mut(entry).insts.push(Inst { |
| 155 | - id: c1, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 179 | + id: c1, | |
| 180 | + ty: IrType::Int(IntWidth::I32), | |
| 181 | + span: span(), | |
| 156 | 182 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 157 | 183 | }); |
| 158 | 184 | let cond = f.next_value_id(); |
| 159 | 185 | f.register_type(cond, IrType::Bool); |
| 160 | 186 | f.block_mut(entry).insts.push(Inst { |
| 161 | - id: cond, ty: IrType::Bool, span: span(), | |
| 187 | + id: cond, | |
| 188 | + ty: IrType::Bool, | |
| 189 | + span: span(), | |
| 162 | 190 | kind: InstKind::ConstBool(true), |
| 163 | 191 | }); |
| 164 | 192 | f.block_mut(entry).terminator = Some(Terminator::CondBranch { |
| 165 | 193 | cond, |
| 166 | - true_dest: header, true_args: vec![c1], | |
| 167 | - false_dest: alt, false_args: vec![], | |
| 194 | + true_dest: header, | |
| 195 | + true_args: vec![c1], | |
| 196 | + false_dest: alt, | |
| 197 | + false_args: vec![], | |
| 168 | 198 | }); |
| 169 | 199 | |
| 170 | 200 | // Alt entry: const 5, br header(5) |
| 171 | 201 | let c5 = f.next_value_id(); |
| 172 | 202 | f.register_type(c5, IrType::Int(IntWidth::I32)); |
| 173 | 203 | f.block_mut(alt).insts.push(Inst { |
| 174 | - id: c5, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 204 | + id: c5, | |
| 205 | + ty: IrType::Int(IntWidth::I32), | |
| 206 | + span: span(), | |
| 175 | 207 | kind: InstKind::ConstInt(5, IntWidth::I32), |
| 176 | 208 | }); |
| 177 | 209 | f.block_mut(alt).terminator = Some(Terminator::Branch(header, vec![c5])); |
@@ -180,24 +212,31 @@ mod tests { | ||
| 180 | 212 | let iv = f.next_value_id(); |
| 181 | 213 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 182 | 214 | f.block_mut(header).params.push(BlockParam { |
| 183 | - id: iv, ty: IrType::Int(IntWidth::I32), | |
| 215 | + id: iv, | |
| 216 | + ty: IrType::Int(IntWidth::I32), | |
| 184 | 217 | }); |
| 185 | 218 | let c10 = f.next_value_id(); |
| 186 | 219 | f.register_type(c10, IrType::Int(IntWidth::I32)); |
| 187 | 220 | f.block_mut(header).insts.push(Inst { |
| 188 | - id: c10, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 221 | + id: c10, | |
| 222 | + ty: IrType::Int(IntWidth::I32), | |
| 223 | + span: span(), | |
| 189 | 224 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 190 | 225 | }); |
| 191 | 226 | let cmp = f.next_value_id(); |
| 192 | 227 | f.register_type(cmp, IrType::Bool); |
| 193 | 228 | f.block_mut(header).insts.push(Inst { |
| 194 | - id: cmp, ty: IrType::Bool, span: span(), | |
| 229 | + id: cmp, | |
| 230 | + ty: IrType::Bool, | |
| 231 | + span: span(), | |
| 195 | 232 | kind: InstKind::ICmp(CmpOp::Le, iv, c10), |
| 196 | 233 | }); |
| 197 | 234 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 198 | 235 | cond: cmp, |
| 199 | - true_dest: body, true_args: vec![], | |
| 200 | - false_dest: exit, false_args: vec![], | |
| 236 | + true_dest: body, | |
| 237 | + true_args: vec![], | |
| 238 | + false_dest: exit, | |
| 239 | + false_args: vec![], | |
| 201 | 240 | }); |
| 202 | 241 | |
| 203 | 242 | // Body → latch |
@@ -207,13 +246,17 @@ mod tests { | ||
| 207 | 246 | let one_l = f.next_value_id(); |
| 208 | 247 | f.register_type(one_l, IrType::Int(IntWidth::I32)); |
| 209 | 248 | f.block_mut(latch).insts.push(Inst { |
| 210 | - id: one_l, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 249 | + id: one_l, | |
| 250 | + ty: IrType::Int(IntWidth::I32), | |
| 251 | + span: span(), | |
| 211 | 252 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 212 | 253 | }); |
| 213 | 254 | let nxt = f.next_value_id(); |
| 214 | 255 | f.register_type(nxt, IrType::Int(IntWidth::I32)); |
| 215 | 256 | f.block_mut(latch).insts.push(Inst { |
| 216 | - id: nxt, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 257 | + id: nxt, | |
| 258 | + ty: IrType::Int(IntWidth::I32), | |
| 259 | + span: span(), | |
| 217 | 260 | kind: InstKind::IAdd(iv, one_l), |
| 218 | 261 | }); |
| 219 | 262 | f.block_mut(latch).terminator = Some(Terminator::Branch(header, vec![nxt])); |
@@ -234,8 +277,10 @@ mod tests { | ||
| 234 | 277 | let loops = find_natural_loops(f); |
| 235 | 278 | let preds = predecessors(f); |
| 236 | 279 | assert_eq!(loops.len(), 1); |
| 237 | - assert!(find_preheader(f, &loops[0], &preds).is_none(), | |
| 238 | - "should have no preheader before insertion"); | |
| 280 | + assert!( | |
| 281 | + find_preheader(f, &loops[0], &preds).is_none(), | |
| 282 | + "should have no preheader before insertion" | |
| 283 | + ); | |
| 239 | 284 | |
| 240 | 285 | let pass = PreheaderInsert; |
| 241 | 286 | let changed = pass.run(&mut m); |
src/opt/return_prop.rsmodified@@ -17,7 +17,9 @@ use super::util::substitute_uses; | ||
| 17 | 17 | pub struct ReturnPropagate; |
| 18 | 18 | |
| 19 | 19 | impl Pass for ReturnPropagate { |
| 20 | - fn name(&self) -> &'static str { "return-prop" } | |
| 20 | + fn name(&self) -> &'static str { | |
| 21 | + "return-prop" | |
| 22 | + } | |
| 21 | 23 | |
| 22 | 24 | fn run(&self, module: &mut Module) -> bool { |
| 23 | 25 | let plans = collect_plans(module); |
@@ -101,11 +103,19 @@ fn const_value_of(func: &Function, value: ValueId) -> Option<ScalarConst> { | ||
| 101 | 103 | } |
| 102 | 104 | |
| 103 | 105 | fn side_effect_free(kind: &InstKind) -> bool { |
| 104 | - !matches!(kind, InstKind::Store(..) | InstKind::Call(..) | InstKind::RuntimeCall(..)) | |
| 106 | + !matches!( | |
| 107 | + kind, | |
| 108 | + InstKind::Store(..) | InstKind::Call(..) | InstKind::RuntimeCall(..) | |
| 109 | + ) | |
| 105 | 110 | } |
| 106 | 111 | |
| 107 | 112 | fn classify_return_source(func: &Function, value: ValueId) -> Option<ReturnSource> { |
| 108 | - if let Some((idx, param)) = func.params.iter().enumerate().find(|(_, param)| param.id == value) { | |
| 113 | + if let Some((idx, param)) = func | |
| 114 | + .params | |
| 115 | + .iter() | |
| 116 | + .enumerate() | |
| 117 | + .find(|(_, param)| param.id == value) | |
| 118 | + { | |
| 109 | 119 | if !param.ty.is_ptr() && is_scalar_type(¶m.ty) { |
| 110 | 120 | return Some(ReturnSource::Param(idx)); |
| 111 | 121 | } |
@@ -115,7 +125,11 @@ fn classify_return_source(func: &Function, value: ValueId) -> Option<ReturnSourc | ||
| 115 | 125 | let inst = defs.get(&value)?; |
| 116 | 126 | match inst.kind { |
| 117 | 127 | InstKind::Load(addr) => { |
| 118 | - let (idx, param) = func.params.iter().enumerate().find(|(_, param)| param.id == addr)?; | |
| 128 | + let (idx, param) = func | |
| 129 | + .params | |
| 130 | + .iter() | |
| 131 | + .enumerate() | |
| 132 | + .find(|(_, param)| param.id == addr)?; | |
| 119 | 133 | let IrType::Ptr(inner) = ¶m.ty else { |
| 120 | 134 | return None; |
| 121 | 135 | }; |
@@ -135,7 +149,12 @@ fn collect_plans(module: &Module) -> Vec<Option<ReturnSource>> { | ||
| 135 | 149 | if !func.internal_only || matches!(func.return_type, IrType::Void) { |
| 136 | 150 | continue; |
| 137 | 151 | } |
| 138 | - if func.blocks.iter().flat_map(|block| block.insts.iter()).any(|inst| !side_effect_free(&inst.kind)) { | |
| 152 | + if func | |
| 153 | + .blocks | |
| 154 | + .iter() | |
| 155 | + .flat_map(|block| block.insts.iter()) | |
| 156 | + .any(|inst| !side_effect_free(&inst.kind)) | |
| 157 | + { | |
| 139 | 158 | continue; |
| 140 | 159 | } |
| 141 | 160 | |
@@ -211,7 +230,11 @@ fn default_span(func: &Function) -> Span { | ||
| 211 | 230 | span |
| 212 | 231 | } else { |
| 213 | 232 | let pos = Position { line: 0, col: 0 }; |
| 214 | - Span { file_id: 0, start: pos, end: pos } | |
| 233 | + Span { | |
| 234 | + file_id: 0, | |
| 235 | + start: pos, | |
| 236 | + end: pos, | |
| 237 | + } | |
| 215 | 238 | } |
| 216 | 239 | } |
| 217 | 240 | |
@@ -279,11 +302,10 @@ fn rewrite_caller(module: &mut Module, caller_idx: usize, plans: &[Option<Return | ||
| 279 | 302 | for (call_id, args, plan) in scheduled { |
| 280 | 303 | let replacement = match plan { |
| 281 | 304 | ReturnSource::Param(param_idx) => args.get(param_idx).copied(), |
| 282 | - ReturnSource::LoadParam(param_idx) => { | |
| 283 | - args.get(param_idx) | |
| 284 | - .copied() | |
| 285 | - .and_then(|ptr| wrapper_value_for_call(caller, ptr, call_id)) | |
| 286 | - } | |
| 305 | + ReturnSource::LoadParam(param_idx) => args | |
| 306 | + .get(param_idx) | |
| 307 | + .copied() | |
| 308 | + .and_then(|ptr| wrapper_value_for_call(caller, ptr, call_id)), | |
| 287 | 309 | ReturnSource::Const(value) => Some(ensure_const_in_entry(caller, &mut cache, value)), |
| 288 | 310 | }; |
| 289 | 311 | let Some(replacement) = replacement else { |
@@ -327,13 +349,22 @@ mod tests { | ||
| 327 | 349 | |
| 328 | 350 | fn span() -> Span { |
| 329 | 351 | let pos = Position { line: 0, col: 0 }; |
| 330 | - Span { file_id: 0, start: pos, end: pos } | |
| 352 | + Span { | |
| 353 | + file_id: 0, | |
| 354 | + start: pos, | |
| 355 | + end: pos, | |
| 356 | + } | |
| 331 | 357 | } |
| 332 | 358 | |
| 333 | 359 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 334 | 360 | let id = f.next_value_id(); |
| 335 | 361 | let entry = f.entry; |
| 336 | - f.block_mut(entry).insts.push(Inst { id, kind, ty: ty.clone(), span: span() }); | |
| 362 | + f.block_mut(entry).insts.push(Inst { | |
| 363 | + id, | |
| 364 | + kind, | |
| 365 | + ty: ty.clone(), | |
| 366 | + span: span(), | |
| 367 | + }); | |
| 337 | 368 | f.register_type(id, ty); |
| 338 | 369 | id |
| 339 | 370 | } |
@@ -350,13 +381,21 @@ mod tests { | ||
| 350 | 381 | }]; |
| 351 | 382 | let mut callee = Function::new("passthrough".into(), params, IrType::Int(IntWidth::I32)); |
| 352 | 383 | callee.internal_only = true; |
| 353 | - let loaded = push(&mut callee, InstKind::Load(ValueId(0)), IrType::Int(IntWidth::I32)); | |
| 384 | + let loaded = push( | |
| 385 | + &mut callee, | |
| 386 | + InstKind::Load(ValueId(0)), | |
| 387 | + IrType::Int(IntWidth::I32), | |
| 388 | + ); | |
| 354 | 389 | let callee_entry = callee.entry; |
| 355 | 390 | callee.block_mut(callee_entry).terminator = Some(Terminator::Return(Some(loaded))); |
| 356 | 391 | module.add_function(callee); |
| 357 | 392 | |
| 358 | 393 | let mut caller = Function::new("main".into(), vec![], IrType::Int(IntWidth::I32)); |
| 359 | - let c7 = push(&mut caller, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 394 | + let c7 = push( | |
| 395 | + &mut caller, | |
| 396 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 397 | + IrType::Int(IntWidth::I32), | |
| 398 | + ); | |
| 360 | 399 | let slot = push( |
| 361 | 400 | &mut caller, |
| 362 | 401 | InstKind::Alloca(IrType::Int(IntWidth::I32)), |
@@ -375,7 +414,9 @@ mod tests { | ||
| 375 | 414 | assert!(ReturnPropagate.run(&mut module)); |
| 376 | 415 | let caller_insts = &module.functions[1].blocks[0].insts; |
| 377 | 416 | assert!( |
| 378 | - !caller_insts.iter().any(|inst| matches!(inst.kind, InstKind::Call(..))), | |
| 417 | + !caller_insts | |
| 418 | + .iter() | |
| 419 | + .any(|inst| matches!(inst.kind, InstKind::Call(..))), | |
| 379 | 420 | "call should be removed after return propagation" |
| 380 | 421 | ); |
| 381 | 422 | assert!( |
@@ -390,7 +431,11 @@ mod tests { | ||
| 390 | 431 | |
| 391 | 432 | let mut callee = Function::new("const_fn".into(), vec![], IrType::Int(IntWidth::I32)); |
| 392 | 433 | callee.internal_only = true; |
| 393 | - let c42 = push(&mut callee, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 434 | + let c42 = push( | |
| 435 | + &mut callee, | |
| 436 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 437 | + IrType::Int(IntWidth::I32), | |
| 438 | + ); | |
| 394 | 439 | let callee_entry = callee.entry; |
| 395 | 440 | callee.block_mut(callee_entry).terminator = Some(Terminator::Return(Some(c42))); |
| 396 | 441 | module.add_function(callee); |
@@ -408,11 +453,15 @@ mod tests { | ||
| 408 | 453 | assert!(ReturnPropagate.run(&mut module)); |
| 409 | 454 | let caller_insts = &module.functions[1].blocks[0].insts; |
| 410 | 455 | assert!( |
| 411 | - !caller_insts.iter().any(|inst| matches!(inst.kind, InstKind::Call(..))), | |
| 456 | + !caller_insts | |
| 457 | + .iter() | |
| 458 | + .any(|inst| matches!(inst.kind, InstKind::Call(..))), | |
| 412 | 459 | "constant helper call should be removed" |
| 413 | 460 | ); |
| 414 | 461 | assert!( |
| 415 | - caller_insts.iter().any(|inst| matches!(inst.kind, InstKind::ConstInt(42, IntWidth::I32))), | |
| 462 | + caller_insts | |
| 463 | + .iter() | |
| 464 | + .any(|inst| matches!(inst.kind, InstKind::ConstInt(42, IntWidth::I32))), | |
| 416 | 465 | "caller should materialize the propagated constant" |
| 417 | 466 | ); |
| 418 | 467 | } |
src/opt/simplify_cfg.rsmodified@@ -16,19 +16,23 @@ | ||
| 16 | 16 | //! |
| 17 | 17 | //! This eliminates the empty block chains left by inlining. |
| 18 | 18 | |
| 19 | +use super::pass::Pass; | |
| 19 | 20 | use crate::ir::inst::*; |
| 20 | 21 | use crate::ir::walk::predecessors; |
| 21 | -use super::pass::Pass; | |
| 22 | 22 | |
| 23 | 23 | pub struct SimplifyCfg; |
| 24 | 24 | |
| 25 | 25 | impl Pass for SimplifyCfg { |
| 26 | - fn name(&self) -> &'static str { "simplify-cfg" } | |
| 26 | + fn name(&self) -> &'static str { | |
| 27 | + "simplify-cfg" | |
| 28 | + } | |
| 27 | 29 | |
| 28 | 30 | fn run(&self, module: &mut Module) -> bool { |
| 29 | 31 | let mut changed = false; |
| 30 | 32 | for func in &mut module.functions { |
| 31 | - if simplify_function(func) { changed = true; } | |
| 33 | + if simplify_function(func) { | |
| 34 | + changed = true; | |
| 35 | + } | |
| 32 | 36 | } |
| 33 | 37 | changed |
| 34 | 38 | } |
@@ -44,9 +48,15 @@ fn simplify_function(func: &mut Function) -> bool { | ||
| 44 | 48 | let mut redirect: Option<(BlockId, BlockId)> = None; |
| 45 | 49 | |
| 46 | 50 | for block in &func.blocks { |
| 47 | - if block.id == func.entry { continue; } | |
| 48 | - if !block.insts.is_empty() { continue; } | |
| 49 | - if !block.params.is_empty() { continue; } | |
| 51 | + if block.id == func.entry { | |
| 52 | + continue; | |
| 53 | + } | |
| 54 | + if !block.insts.is_empty() { | |
| 55 | + continue; | |
| 56 | + } | |
| 57 | + if !block.params.is_empty() { | |
| 58 | + continue; | |
| 59 | + } | |
| 50 | 60 | if let Some(Terminator::Branch(target, args)) = &block.terminator { |
| 51 | 61 | if args.is_empty() { |
| 52 | 62 | // This block is empty and just forwards to target. |
@@ -56,11 +66,15 @@ fn simplify_function(func: &mut Function) -> bool { | ||
| 56 | 66 | } |
| 57 | 67 | } |
| 58 | 68 | |
| 59 | - let Some((empty_block, target)) = redirect else { break }; | |
| 69 | + let Some((empty_block, target)) = redirect else { | |
| 70 | + break; | |
| 71 | + }; | |
| 60 | 72 | |
| 61 | 73 | // Redirect all predecessors of empty_block to target instead. |
| 62 | 74 | for block in &mut func.blocks { |
| 63 | - if block.id == empty_block { continue; } // don't redirect self | |
| 75 | + if block.id == empty_block { | |
| 76 | + continue; | |
| 77 | + } // don't redirect self | |
| 64 | 78 | let term = match &mut block.terminator { |
| 65 | 79 | Some(t) => t, |
| 66 | 80 | None => continue, |
@@ -97,7 +111,9 @@ fn simplify_function(func: &mut Function) -> bool { | ||
| 97 | 111 | } |
| 98 | 112 | } |
| 99 | 113 | |
| 100 | - let Some((pred_id, succ_id)) = merge else { break }; | |
| 114 | + let Some((pred_id, succ_id)) = merge else { | |
| 115 | + break; | |
| 116 | + }; | |
| 101 | 117 | |
| 102 | 118 | // Merge: append successor's instructions and terminator to predecessor. |
| 103 | 119 | let succ_insts = func.block(succ_id).insts.clone(); |
@@ -124,16 +140,30 @@ fn simplify_function(func: &mut Function) -> bool { | ||
| 124 | 140 | fn redirect_terminator(term: &mut Terminator, old: BlockId, new: BlockId) { |
| 125 | 141 | match term { |
| 126 | 142 | Terminator::Branch(dest, _) => { |
| 127 | - if *dest == old { *dest = new; } | |
| 143 | + if *dest == old { | |
| 144 | + *dest = new; | |
| 145 | + } | |
| 128 | 146 | } |
| 129 | - Terminator::CondBranch { true_dest, false_dest, .. } => { | |
| 130 | - if *true_dest == old { *true_dest = new; } | |
| 131 | - if *false_dest == old { *false_dest = new; } | |
| 147 | + Terminator::CondBranch { | |
| 148 | + true_dest, | |
| 149 | + false_dest, | |
| 150 | + .. | |
| 151 | + } => { | |
| 152 | + if *true_dest == old { | |
| 153 | + *true_dest = new; | |
| 154 | + } | |
| 155 | + if *false_dest == old { | |
| 156 | + *false_dest = new; | |
| 157 | + } | |
| 132 | 158 | } |
| 133 | 159 | Terminator::Switch { default, cases, .. } => { |
| 134 | - if *default == old { *default = new; } | |
| 160 | + if *default == old { | |
| 161 | + *default = new; | |
| 162 | + } | |
| 135 | 163 | for (_, dest) in cases.iter_mut() { |
| 136 | - if *dest == old { *dest = new; } | |
| 164 | + if *dest == old { | |
| 165 | + *dest = new; | |
| 166 | + } | |
| 137 | 167 | } |
| 138 | 168 | } |
| 139 | 169 | _ => {} |
@@ -143,7 +173,7 @@ fn redirect_terminator(term: &mut Terminator, old: BlockId, new: BlockId) { | ||
| 143 | 173 | #[cfg(test)] |
| 144 | 174 | mod tests { |
| 145 | 175 | use super::*; |
| 146 | - use crate::ir::types::{IrType, IntWidth}; | |
| 176 | + use crate::ir::types::{IntWidth, IrType}; | |
| 147 | 177 | use crate::opt::pass::Pass; |
| 148 | 178 | |
| 149 | 179 | #[test] |
@@ -166,7 +196,11 @@ mod tests { | ||
| 166 | 196 | // contain the ret directly). |
| 167 | 197 | let f = &m.functions[0]; |
| 168 | 198 | // The chain should be collapsed. |
| 169 | - assert!(f.blocks.len() <= 2, "should merge empty chain, got {} blocks", f.blocks.len()); | |
| 199 | + assert!( | |
| 200 | + f.blocks.len() <= 2, | |
| 201 | + "should merge empty chain, got {} blocks", | |
| 202 | + f.blocks.len() | |
| 203 | + ); | |
| 170 | 204 | } |
| 171 | 205 | |
| 172 | 206 | #[test] |
@@ -179,7 +213,8 @@ mod tests { | ||
| 179 | 213 | let id = f.next_value_id(); |
| 180 | 214 | f.register_type(id, IrType::Int(IntWidth::I32)); |
| 181 | 215 | f.block_mut(b).insts.push(Inst { |
| 182 | - id, ty: IrType::Int(IntWidth::I32), | |
| 216 | + id, | |
| 217 | + ty: IrType::Int(IntWidth::I32), | |
| 183 | 218 | span: crate::lexer::Span { |
| 184 | 219 | file_id: 0, |
| 185 | 220 | start: crate::lexer::Position { line: 0, col: 0 }, |
@@ -195,8 +230,11 @@ mod tests { | ||
| 195 | 230 | // Body block may be merged into entry (single pred + unconditional branch). |
| 196 | 231 | // But the instruction should be preserved. |
| 197 | 232 | let f = &m.functions[0]; |
| 198 | - let has_const = f.blocks.iter().any(|bl| | |
| 199 | - bl.insts.iter().any(|i| matches!(i.kind, InstKind::ConstInt(42, _)))); | |
| 233 | + let has_const = f.blocks.iter().any(|bl| { | |
| 234 | + bl.insts | |
| 235 | + .iter() | |
| 236 | + .any(|i| matches!(i.kind, InstKind::ConstInt(42, _))) | |
| 237 | + }); | |
| 200 | 238 | assert!(has_const, "const instruction must be preserved"); |
| 201 | 239 | } |
| 202 | 240 | } |
src/opt/sroa.rsmodified@@ -11,24 +11,28 @@ | ||
| 11 | 11 | //! |
| 12 | 12 | //! After SROA, a second Mem2Reg pass promotes the new scalar allocas. |
| 13 | 13 | |
| 14 | -use std::collections::HashMap; | |
| 14 | +use super::loop_utils::resolve_const_int; | |
| 15 | +use super::pass::Pass; | |
| 15 | 16 | use crate::ir::inst::*; |
| 16 | 17 | use crate::ir::types::IrType; |
| 17 | 18 | use crate::ir::walk::inst_uses; |
| 18 | -use super::loop_utils::resolve_const_int; | |
| 19 | -use super::pass::Pass; | |
| 19 | +use std::collections::HashMap; | |
| 20 | 20 | |
| 21 | 21 | const SROA_MAX_FIELDS: u64 = 8; |
| 22 | 22 | |
| 23 | 23 | pub struct Sroa; |
| 24 | 24 | |
| 25 | 25 | impl Pass for Sroa { |
| 26 | - fn name(&self) -> &'static str { "sroa" } | |
| 26 | + fn name(&self) -> &'static str { | |
| 27 | + "sroa" | |
| 28 | + } | |
| 27 | 29 | |
| 28 | 30 | fn run(&self, module: &mut Module) -> bool { |
| 29 | 31 | let mut changed = false; |
| 30 | 32 | for func in &mut module.functions { |
| 31 | - if sroa_function(func) { changed = true; } | |
| 33 | + if sroa_function(func) { | |
| 34 | + changed = true; | |
| 35 | + } | |
| 32 | 36 | } |
| 33 | 37 | changed |
| 34 | 38 | } |
@@ -37,7 +41,9 @@ impl Pass for Sroa { | ||
| 37 | 41 | fn sroa_function(func: &mut Function) -> bool { |
| 38 | 42 | // Collect candidate allocas: Array type, small, all constant-index GEPs. |
| 39 | 43 | let candidates = find_candidates(func); |
| 40 | - if candidates.is_empty() { return false; } | |
| 44 | + if candidates.is_empty() { | |
| 45 | + return false; | |
| 46 | + } | |
| 41 | 47 | |
| 42 | 48 | let mut changed = false; |
| 43 | 49 | for cand in candidates { |
@@ -85,14 +91,18 @@ fn is_eligible(func: &Function, alloca_id: ValueId) -> bool { | ||
| 85 | 91 | for block in &func.blocks { |
| 86 | 92 | for inst in &block.insts { |
| 87 | 93 | let uses = inst_uses(&inst.kind); |
| 88 | - if !uses.contains(&alloca_id) { continue; } | |
| 94 | + if !uses.contains(&alloca_id) { | |
| 95 | + continue; | |
| 96 | + } | |
| 89 | 97 | |
| 90 | 98 | // Classify this use of the alloca. |
| 91 | 99 | match &inst.kind { |
| 92 | 100 | // GEP with the alloca as base: OK if single constant index |
| 93 | 101 | // AND the result type matches the element type (not byte-level). |
| 94 | 102 | InstKind::GetElementPtr(base, indices) if *base == alloca_id => { |
| 95 | - if indices.len() != 1 { return false; } | |
| 103 | + if indices.len() != 1 { | |
| 104 | + return false; | |
| 105 | + } | |
| 96 | 106 | if resolve_const_int(func, indices[0]).is_none() { |
| 97 | 107 | return false; |
| 98 | 108 | } |
@@ -150,8 +160,16 @@ fn gep_result_escapes(func: &Function, gep_id: ValueId) -> bool { | ||
| 150 | 160 | match term { |
| 151 | 161 | Terminator::Return(Some(v)) if *v == gep_id => return true, |
| 152 | 162 | Terminator::Branch(_, args) if args.contains(&gep_id) => return true, |
| 153 | - Terminator::CondBranch { cond, true_args, false_args, .. } => { | |
| 154 | - if *cond == gep_id || true_args.contains(&gep_id) || false_args.contains(&gep_id) { | |
| 163 | + Terminator::CondBranch { | |
| 164 | + cond, | |
| 165 | + true_args, | |
| 166 | + false_args, | |
| 167 | + .. | |
| 168 | + } => { | |
| 169 | + if *cond == gep_id | |
| 170 | + || true_args.contains(&gep_id) | |
| 171 | + || false_args.contains(&gep_id) | |
| 172 | + { | |
| 155 | 173 | return true; |
| 156 | 174 | } |
| 157 | 175 | } |
@@ -180,12 +198,15 @@ fn decompose_alloca(func: &mut Function, cand: &SroaCandidate) -> bool { | ||
| 180 | 198 | let new_id = func.next_value_id(); |
| 181 | 199 | let ptr_ty = IrType::Ptr(Box::new(cand.elem_ty.clone())); |
| 182 | 200 | func.register_type(new_id, ptr_ty.clone()); |
| 183 | - func.block_mut(cand.alloca_block).insts.insert(insert_pos + i as usize, Inst { | |
| 184 | - id: new_id, | |
| 185 | - kind: InstKind::Alloca(cand.elem_ty.clone()), | |
| 186 | - ty: ptr_ty, | |
| 187 | - span, | |
| 188 | - }); | |
| 201 | + func.block_mut(cand.alloca_block).insts.insert( | |
| 202 | + insert_pos + i as usize, | |
| 203 | + Inst { | |
| 204 | + id: new_id, | |
| 205 | + kind: InstKind::Alloca(cand.elem_ty.clone()), | |
| 206 | + ty: ptr_ty, | |
| 207 | + span, | |
| 208 | + }, | |
| 209 | + ); | |
| 189 | 210 | field_allocas.push(new_id); |
| 190 | 211 | } |
| 191 | 212 | |
@@ -207,7 +228,9 @@ fn decompose_alloca(func: &mut Function, cand: &SroaCandidate) -> bool { | ||
| 207 | 228 | } |
| 208 | 229 | } |
| 209 | 230 | |
| 210 | - if gep_to_field.is_empty() { return false; } | |
| 231 | + if gep_to_field.is_empty() { | |
| 232 | + return false; | |
| 233 | + } | |
| 211 | 234 | |
| 212 | 235 | // Rewrite all uses of GEP results to use the field allocas. |
| 213 | 236 | for block in &mut func.blocks { |
@@ -243,12 +266,16 @@ fn decompose_alloca(func: &mut Function, cand: &SroaCandidate) -> bool { | ||
| 243 | 266 | mod tests { |
| 244 | 267 | use super::*; |
| 245 | 268 | use crate::ir::types::IntWidth; |
| 269 | + use crate::lexer::{Position, Span}; | |
| 246 | 270 | use crate::opt::pass::Pass; |
| 247 | - use crate::lexer::{Span, Position}; | |
| 248 | 271 | |
| 249 | 272 | fn span() -> Span { |
| 250 | 273 | let pos = Position { line: 0, col: 0 }; |
| 251 | - Span { file_id: 0, start: pos, end: pos } | |
| 274 | + Span { | |
| 275 | + file_id: 0, | |
| 276 | + start: pos, | |
| 277 | + end: pos, | |
| 278 | + } | |
| 252 | 279 | } |
| 253 | 280 | |
| 254 | 281 | #[test] |
@@ -258,7 +285,9 @@ mod tests { | ||
| 258 | 285 | let a = f.next_value_id(); |
| 259 | 286 | f.register_type(a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 260 | 287 | f.block_mut(f.entry).insts.push(Inst { |
| 261 | - id: a, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), span: span(), | |
| 288 | + id: a, | |
| 289 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 290 | + span: span(), | |
| 262 | 291 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 263 | 292 | }); |
| 264 | 293 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
@@ -297,7 +326,10 @@ mod tests { | ||
| 297 | 326 | }); |
| 298 | 327 | |
| 299 | 328 | let gep = f.next_value_id(); |
| 300 | - f.register_type(gep, IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))))); | |
| 329 | + f.register_type( | |
| 330 | + gep, | |
| 331 | + IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))))), | |
| 332 | + ); | |
| 301 | 333 | f.block_mut(f.entry).insts.push(Inst { |
| 302 | 334 | id: gep, |
| 303 | 335 | ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))))), |
@@ -306,12 +338,21 @@ mod tests { | ||
| 306 | 338 | }); |
| 307 | 339 | |
| 308 | 340 | let sink = f.next_value_id(); |
| 309 | - f.register_type(sink, IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))))))); | |
| 341 | + f.register_type( | |
| 342 | + sink, | |
| 343 | + IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Ptr(Box::new( | |
| 344 | + IrType::Int(IntWidth::I8), | |
| 345 | + )))))), | |
| 346 | + ); | |
| 310 | 347 | f.block_mut(f.entry).insts.push(Inst { |
| 311 | 348 | id: sink, |
| 312 | - ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))))))), | |
| 349 | + ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Ptr(Box::new( | |
| 350 | + IrType::Int(IntWidth::I8), | |
| 351 | + )))))), | |
| 313 | 352 | span: span(), |
| 314 | - kind: InstKind::Alloca(IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))))), | |
| 353 | + kind: InstKind::Alloca(IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int( | |
| 354 | + IntWidth::I8, | |
| 355 | + )))))), | |
| 315 | 356 | }); |
| 316 | 357 | |
| 317 | 358 | let escape_store = f.next_value_id(); |
@@ -326,6 +367,9 @@ mod tests { | ||
| 326 | 367 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 327 | 368 | m.add_function(f); |
| 328 | 369 | |
| 329 | - assert!(!Sroa.run(&mut m), "GEP addresses that escape should block SROA"); | |
| 370 | + assert!( | |
| 371 | + !Sroa.run(&mut m), | |
| 372 | + "GEP addresses that escape should block SROA" | |
| 373 | + ); | |
| 330 | 374 | } |
| 331 | 375 | } |
src/opt/strength_reduce.rsmodified@@ -46,7 +46,7 @@ | ||
| 46 | 46 | use super::pass::Pass; |
| 47 | 47 | use super::util::{for_each_operand_mut, for_each_terminator_operand_mut}; |
| 48 | 48 | use crate::ir::inst::*; |
| 49 | -use crate::ir::types::{IrType, IntWidth}; | |
| 49 | +use crate::ir::types::{IntWidth, IrType}; | |
| 50 | 50 | use std::collections::HashMap; |
| 51 | 51 | |
| 52 | 52 | /// Apply `rewrites` to every operand slot in the function in a |
@@ -101,8 +101,9 @@ fn collect_int_consts(func: &Function) -> HashMap<ValueId, (i64, IntWidth)> { | ||
| 101 | 101 | /// Sign-extend a stored i64 to its declared bit width. Used so that |
| 102 | 102 | /// "is this -1?" checks survive narrow types. |
| 103 | 103 | fn sext(v: i64, bits: u32) -> i64 { |
| 104 | - if bits >= 64 { v } | |
| 105 | - else { | |
| 104 | + if bits >= 64 { | |
| 105 | + v | |
| 106 | + } else { | |
| 106 | 107 | let shift = 64 - bits; |
| 107 | 108 | (v << shift) >> shift |
| 108 | 109 | } |
@@ -112,7 +113,9 @@ fn sext(v: i64, bits: u32) -> i64 { | ||
| 112 | 113 | /// when `v == 1 << k` and `k > 0`. (`v == 1` is handled separately as |
| 113 | 114 | /// "multiply by 1 → identity".) |
| 114 | 115 | fn pow2_exponent(v: i64) -> Option<u32> { |
| 115 | - if v <= 0 { return None; } | |
| 116 | + if v <= 0 { | |
| 117 | + return None; | |
| 118 | + } | |
| 116 | 119 | let u = v as u64; |
| 117 | 120 | if u.is_power_of_two() && u != 1 { |
| 118 | 121 | Some(u.trailing_zeros()) |
@@ -138,11 +141,12 @@ enum Rewrite { | ||
| 138 | 141 | |
| 139 | 142 | /// Try to derive a strength-reduction rewrite for one instruction. |
| 140 | 143 | /// Returns `None` if no rewrite applies. |
| 141 | -fn rewrite_for( | |
| 142 | - inst: &Inst, | |
| 143 | - consts: &HashMap<ValueId, (i64, IntWidth)>, | |
| 144 | -) -> Option<Rewrite> { | |
| 145 | - let int_w = if let IrType::Int(w) = inst.ty { w } else { return None; }; | |
| 144 | +fn rewrite_for(inst: &Inst, consts: &HashMap<ValueId, (i64, IntWidth)>) -> Option<Rewrite> { | |
| 145 | + let int_w = if let IrType::Int(w) = inst.ty { | |
| 146 | + w | |
| 147 | + } else { | |
| 148 | + return None; | |
| 149 | + }; | |
| 146 | 150 | // Audit m4-1: after the M4-4 normalization in `collect_int_consts`, |
| 147 | 151 | // every `kv` returned from `const_int` is already sign-extended |
| 148 | 152 | // at its source width. The earlier code re-sext'd at `int_w` (the |
@@ -168,7 +172,9 @@ fn rewrite_for( | ||
| 168 | 172 | return Some(Rewrite::KindOnly(InstKind::ConstInt(0, int_w))); |
| 169 | 173 | } |
| 170 | 174 | if kv == 1 { |
| 171 | - return Some(Rewrite::Identity { pass_through: var_id }); | |
| 175 | + return Some(Rewrite::Identity { | |
| 176 | + pass_through: var_id, | |
| 177 | + }); | |
| 172 | 178 | } |
| 173 | 179 | if kv == -1 { |
| 174 | 180 | return Some(Rewrite::KindOnly(InstKind::INeg(var_id))); |
@@ -278,7 +284,9 @@ fn rewrite_for( | ||
| 278 | 284 | pub struct StrengthReduce; |
| 279 | 285 | |
| 280 | 286 | impl Pass for StrengthReduce { |
| 281 | - fn name(&self) -> &'static str { "strength-reduce" } | |
| 287 | + fn name(&self) -> &'static str { | |
| 288 | + "strength-reduce" | |
| 289 | + } | |
| 282 | 290 | |
| 283 | 291 | fn run(&self, module: &mut Module) -> bool { |
| 284 | 292 | let mut changed = false; |
@@ -298,7 +306,9 @@ impl Pass for StrengthReduce { | ||
| 298 | 306 | } |
| 299 | 307 | } |
| 300 | 308 | } |
| 301 | - if rewrites.is_empty() { continue; } | |
| 309 | + if rewrites.is_empty() { | |
| 310 | + continue; | |
| 311 | + } | |
| 302 | 312 | |
| 303 | 313 | // Second pass: apply rewrites. We process them in reverse |
| 304 | 314 | // block-then-instruction order so that inserting a new |
@@ -317,15 +327,22 @@ impl Pass for StrengthReduce { | ||
| 317 | 327 | for (bi, ii, rw, old_id, ty) in rewrites { |
| 318 | 328 | match rw { |
| 319 | 329 | Rewrite::ShlByConst { var, k } => { |
| 320 | - let int_w = if let IrType::Int(w) = ty { w } else { unreachable!() }; | |
| 330 | + let int_w = if let IrType::Int(w) = ty { | |
| 331 | + w | |
| 332 | + } else { | |
| 333 | + unreachable!() | |
| 334 | + }; | |
| 321 | 335 | let kid = func.next_value_id(); |
| 322 | 336 | let span = func.blocks[bi].insts[ii].span; |
| 323 | - func.blocks[bi].insts.insert(ii, Inst { | |
| 324 | - id: kid, | |
| 325 | - kind: InstKind::ConstInt(k as i128, int_w), | |
| 326 | - ty: IrType::Int(int_w), | |
| 327 | - span, | |
| 328 | - }); | |
| 337 | + func.blocks[bi].insts.insert( | |
| 338 | + ii, | |
| 339 | + Inst { | |
| 340 | + id: kid, | |
| 341 | + kind: InstKind::ConstInt(k as i128, int_w), | |
| 342 | + ty: IrType::Int(int_w), | |
| 343 | + span, | |
| 344 | + }, | |
| 345 | + ); | |
| 329 | 346 | // The original instruction shifted to ii+1. |
| 330 | 347 | func.blocks[bi].insts[ii + 1].kind = InstKind::Shl(var, kid); |
| 331 | 348 | } |
@@ -376,7 +393,9 @@ impl Pass for StrengthReduce { | ||
| 376 | 393 | std::collections::HashSet::new(); |
| 377 | 394 | visited.insert(k); |
| 378 | 395 | while let Some(&next) = identity_rewrites.get(&cur) { |
| 379 | - if !visited.insert(next) { break; } | |
| 396 | + if !visited.insert(next) { | |
| 397 | + break; | |
| 398 | + } | |
| 380 | 399 | cur = next; |
| 381 | 400 | } |
| 382 | 401 | identity_rewrites.insert(k, cur); |
@@ -392,17 +411,26 @@ impl Pass for StrengthReduce { | ||
| 392 | 411 | mod tests { |
| 393 | 412 | use super::*; |
| 394 | 413 | use crate::ir::types::IrType; |
| 395 | - use crate::lexer::{Span, Position}; | |
| 414 | + use crate::lexer::{Position, Span}; | |
| 396 | 415 | |
| 397 | 416 | fn dummy_span() -> Span { |
| 398 | 417 | let p = Position { line: 1, col: 1 }; |
| 399 | - Span { start: p, end: p, file_id: 0 } | |
| 418 | + Span { | |
| 419 | + start: p, | |
| 420 | + end: p, | |
| 421 | + file_id: 0, | |
| 422 | + } | |
| 400 | 423 | } |
| 401 | 424 | |
| 402 | 425 | fn push(f: &mut Function, kind: InstKind, ty: IrType) -> ValueId { |
| 403 | 426 | let id = f.next_value_id(); |
| 404 | 427 | let entry = f.entry; |
| 405 | - f.block_mut(entry).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 428 | + f.block_mut(entry).insts.push(Inst { | |
| 429 | + id, | |
| 430 | + kind, | |
| 431 | + ty, | |
| 432 | + span: dummy_span(), | |
| 433 | + }); | |
| 406 | 434 | id |
| 407 | 435 | } |
| 408 | 436 | |
@@ -415,8 +443,16 @@ mod tests { | ||
| 415 | 443 | #[test] |
| 416 | 444 | fn imul_by_pow2_becomes_shl() { |
| 417 | 445 | let (mut m, mut f) = make_fn(); |
| 418 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 419 | - let k = push(&mut f, InstKind::ConstInt(8, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 446 | + let x = push( | |
| 447 | + &mut f, | |
| 448 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 449 | + IrType::Int(IntWidth::I32), | |
| 450 | + ); | |
| 451 | + let k = push( | |
| 452 | + &mut f, | |
| 453 | + InstKind::ConstInt(8, IntWidth::I32), | |
| 454 | + IrType::Int(IntWidth::I32), | |
| 455 | + ); | |
| 420 | 456 | let y = push(&mut f, InstKind::IMul(x, k), IrType::Int(IntWidth::I32)); |
| 421 | 457 | let entry = f.entry; |
| 422 | 458 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -425,21 +461,37 @@ mod tests { | ||
| 425 | 461 | assert!(StrengthReduce.run(&mut m)); |
| 426 | 462 | // The Shl should reference x and a freshly inserted const(3). |
| 427 | 463 | let block = &m.functions[0].blocks[0]; |
| 428 | - let shl = block.insts.iter().find_map(|i| match &i.kind { | |
| 429 | - InstKind::Shl(var, kid) => Some((*var, *kid)), | |
| 430 | - _ => None, | |
| 431 | - }).expect("expected Shl"); | |
| 464 | + let shl = block | |
| 465 | + .insts | |
| 466 | + .iter() | |
| 467 | + .find_map(|i| match &i.kind { | |
| 468 | + InstKind::Shl(var, kid) => Some((*var, *kid)), | |
| 469 | + _ => None, | |
| 470 | + }) | |
| 471 | + .expect("expected Shl"); | |
| 432 | 472 | assert_eq!(shl.0, x); |
| 433 | 473 | // The shift count should be a const(3). |
| 434 | - let kconst = block.insts.iter().find(|i| i.id == shl.1).expect("shift const"); | |
| 474 | + let kconst = block | |
| 475 | + .insts | |
| 476 | + .iter() | |
| 477 | + .find(|i| i.id == shl.1) | |
| 478 | + .expect("shift const"); | |
| 435 | 479 | assert!(matches!(kconst.kind, InstKind::ConstInt(3, IntWidth::I32))); |
| 436 | 480 | } |
| 437 | 481 | |
| 438 | 482 | #[test] |
| 439 | 483 | fn imul_by_zero_becomes_const_zero() { |
| 440 | 484 | let (mut m, mut f) = make_fn(); |
| 441 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 442 | - let z = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 485 | + let x = push( | |
| 486 | + &mut f, | |
| 487 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 488 | + IrType::Int(IntWidth::I32), | |
| 489 | + ); | |
| 490 | + let z = push( | |
| 491 | + &mut f, | |
| 492 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 493 | + IrType::Int(IntWidth::I32), | |
| 494 | + ); | |
| 443 | 495 | let y = push(&mut f, InstKind::IMul(x, z), IrType::Int(IntWidth::I32)); |
| 444 | 496 | let entry = f.entry; |
| 445 | 497 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -447,15 +499,27 @@ mod tests { | ||
| 447 | 499 | |
| 448 | 500 | assert!(StrengthReduce.run(&mut m)); |
| 449 | 501 | // y now defines const(0) |
| 450 | - let y_inst = m.functions[0].blocks[0].insts.iter().find(|i| i.id == y).unwrap(); | |
| 502 | + let y_inst = m.functions[0].blocks[0] | |
| 503 | + .insts | |
| 504 | + .iter() | |
| 505 | + .find(|i| i.id == y) | |
| 506 | + .unwrap(); | |
| 451 | 507 | assert!(matches!(y_inst.kind, InstKind::ConstInt(0, IntWidth::I32))); |
| 452 | 508 | } |
| 453 | 509 | |
| 454 | 510 | #[test] |
| 455 | 511 | fn imul_by_one_becomes_identity() { |
| 456 | 512 | let (mut m, mut f) = make_fn(); |
| 457 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 458 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 513 | + let x = push( | |
| 514 | + &mut f, | |
| 515 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 516 | + IrType::Int(IntWidth::I32), | |
| 517 | + ); | |
| 518 | + let one = push( | |
| 519 | + &mut f, | |
| 520 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 521 | + IrType::Int(IntWidth::I32), | |
| 522 | + ); | |
| 459 | 523 | let y = push(&mut f, InstKind::IMul(x, one), IrType::Int(IntWidth::I32)); |
| 460 | 524 | let entry = f.entry; |
| 461 | 525 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -472,15 +536,27 @@ mod tests { | ||
| 472 | 536 | #[test] |
| 473 | 537 | fn imul_by_neg_one_becomes_neg() { |
| 474 | 538 | let (mut m, mut f) = make_fn(); |
| 475 | - let x = push(&mut f, InstKind::ConstInt(7, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 476 | - let neg = push(&mut f, InstKind::ConstInt(-1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 539 | + let x = push( | |
| 540 | + &mut f, | |
| 541 | + InstKind::ConstInt(7, IntWidth::I32), | |
| 542 | + IrType::Int(IntWidth::I32), | |
| 543 | + ); | |
| 544 | + let neg = push( | |
| 545 | + &mut f, | |
| 546 | + InstKind::ConstInt(-1, IntWidth::I32), | |
| 547 | + IrType::Int(IntWidth::I32), | |
| 548 | + ); | |
| 477 | 549 | let y = push(&mut f, InstKind::IMul(x, neg), IrType::Int(IntWidth::I32)); |
| 478 | 550 | let entry = f.entry; |
| 479 | 551 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
| 480 | 552 | m.add_function(f); |
| 481 | 553 | |
| 482 | 554 | assert!(StrengthReduce.run(&mut m)); |
| 483 | - let y_inst = m.functions[0].blocks[0].insts.iter().find(|i| i.id == y).unwrap(); | |
| 555 | + let y_inst = m.functions[0].blocks[0] | |
| 556 | + .insts | |
| 557 | + .iter() | |
| 558 | + .find(|i| i.id == y) | |
| 559 | + .unwrap(); | |
| 484 | 560 | match y_inst.kind { |
| 485 | 561 | InstKind::INeg(v) => assert_eq!(v, x), |
| 486 | 562 | ref other => panic!("expected INeg, got {:?}", other), |
@@ -490,8 +566,16 @@ mod tests { | ||
| 490 | 566 | #[test] |
| 491 | 567 | fn iadd_zero_becomes_identity() { |
| 492 | 568 | let (mut m, mut f) = make_fn(); |
| 493 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 494 | - let z = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 569 | + let x = push( | |
| 570 | + &mut f, | |
| 571 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 572 | + IrType::Int(IntWidth::I32), | |
| 573 | + ); | |
| 574 | + let z = push( | |
| 575 | + &mut f, | |
| 576 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 577 | + IrType::Int(IntWidth::I32), | |
| 578 | + ); | |
| 495 | 579 | let y = push(&mut f, InstKind::IAdd(x, z), IrType::Int(IntWidth::I32)); |
| 496 | 580 | let entry = f.entry; |
| 497 | 581 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -507,15 +591,27 @@ mod tests { | ||
| 507 | 591 | #[test] |
| 508 | 592 | fn isub_from_zero_becomes_neg() { |
| 509 | 593 | let (mut m, mut f) = make_fn(); |
| 510 | - let z = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 511 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 594 | + let z = push( | |
| 595 | + &mut f, | |
| 596 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 597 | + IrType::Int(IntWidth::I32), | |
| 598 | + ); | |
| 599 | + let x = push( | |
| 600 | + &mut f, | |
| 601 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 602 | + IrType::Int(IntWidth::I32), | |
| 603 | + ); | |
| 512 | 604 | let y = push(&mut f, InstKind::ISub(z, x), IrType::Int(IntWidth::I32)); |
| 513 | 605 | let entry = f.entry; |
| 514 | 606 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
| 515 | 607 | m.add_function(f); |
| 516 | 608 | |
| 517 | 609 | assert!(StrengthReduce.run(&mut m)); |
| 518 | - let y_inst = m.functions[0].blocks[0].insts.iter().find(|i| i.id == y).unwrap(); | |
| 610 | + let y_inst = m.functions[0].blocks[0] | |
| 611 | + .insts | |
| 612 | + .iter() | |
| 613 | + .find(|i| i.id == y) | |
| 614 | + .unwrap(); | |
| 519 | 615 | match y_inst.kind { |
| 520 | 616 | InstKind::INeg(v) => assert_eq!(v, x), |
| 521 | 617 | _ => panic!(), |
@@ -525,8 +621,16 @@ mod tests { | ||
| 525 | 621 | #[test] |
| 526 | 622 | fn idiv_by_one_becomes_identity() { |
| 527 | 623 | let (mut m, mut f) = make_fn(); |
| 528 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 529 | - let one = push(&mut f, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 624 | + let x = push( | |
| 625 | + &mut f, | |
| 626 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 627 | + IrType::Int(IntWidth::I32), | |
| 628 | + ); | |
| 629 | + let one = push( | |
| 630 | + &mut f, | |
| 631 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 632 | + IrType::Int(IntWidth::I32), | |
| 633 | + ); | |
| 530 | 634 | let y = push(&mut f, InstKind::IDiv(x, one), IrType::Int(IntWidth::I32)); |
| 531 | 635 | let entry = f.entry; |
| 532 | 636 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -542,9 +646,21 @@ mod tests { | ||
| 542 | 646 | #[test] |
| 543 | 647 | fn band_with_neg_one_becomes_identity() { |
| 544 | 648 | let (mut m, mut f) = make_fn(); |
| 545 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 546 | - let mask = push(&mut f, InstKind::ConstInt(-1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 547 | - let y = push(&mut f, InstKind::BitAnd(x, mask), IrType::Int(IntWidth::I32)); | |
| 649 | + let x = push( | |
| 650 | + &mut f, | |
| 651 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 652 | + IrType::Int(IntWidth::I32), | |
| 653 | + ); | |
| 654 | + let mask = push( | |
| 655 | + &mut f, | |
| 656 | + InstKind::ConstInt(-1, IntWidth::I32), | |
| 657 | + IrType::Int(IntWidth::I32), | |
| 658 | + ); | |
| 659 | + let y = push( | |
| 660 | + &mut f, | |
| 661 | + InstKind::BitAnd(x, mask), | |
| 662 | + IrType::Int(IntWidth::I32), | |
| 663 | + ); | |
| 548 | 664 | let entry = f.entry; |
| 549 | 665 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
| 550 | 666 | m.add_function(f); |
@@ -559,23 +675,43 @@ mod tests { | ||
| 559 | 675 | #[test] |
| 560 | 676 | fn band_with_zero_becomes_const_zero() { |
| 561 | 677 | let (mut m, mut f) = make_fn(); |
| 562 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 563 | - let z = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 678 | + let x = push( | |
| 679 | + &mut f, | |
| 680 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 681 | + IrType::Int(IntWidth::I32), | |
| 682 | + ); | |
| 683 | + let z = push( | |
| 684 | + &mut f, | |
| 685 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 686 | + IrType::Int(IntWidth::I32), | |
| 687 | + ); | |
| 564 | 688 | let y = push(&mut f, InstKind::BitAnd(x, z), IrType::Int(IntWidth::I32)); |
| 565 | 689 | let entry = f.entry; |
| 566 | 690 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
| 567 | 691 | m.add_function(f); |
| 568 | 692 | |
| 569 | 693 | assert!(StrengthReduce.run(&mut m)); |
| 570 | - let y_inst = m.functions[0].blocks[0].insts.iter().find(|i| i.id == y).unwrap(); | |
| 694 | + let y_inst = m.functions[0].blocks[0] | |
| 695 | + .insts | |
| 696 | + .iter() | |
| 697 | + .find(|i| i.id == y) | |
| 698 | + .unwrap(); | |
| 571 | 699 | assert!(matches!(y_inst.kind, InstKind::ConstInt(0, IntWidth::I32))); |
| 572 | 700 | } |
| 573 | 701 | |
| 574 | 702 | #[test] |
| 575 | 703 | fn shift_by_zero_becomes_identity() { |
| 576 | 704 | let (mut m, mut f) = make_fn(); |
| 577 | - let x = push(&mut f, InstKind::ConstInt(42, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 578 | - let z = push(&mut f, InstKind::ConstInt(0, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 705 | + let x = push( | |
| 706 | + &mut f, | |
| 707 | + InstKind::ConstInt(42, IntWidth::I32), | |
| 708 | + IrType::Int(IntWidth::I32), | |
| 709 | + ); | |
| 710 | + let z = push( | |
| 711 | + &mut f, | |
| 712 | + InstKind::ConstInt(0, IntWidth::I32), | |
| 713 | + IrType::Int(IntWidth::I32), | |
| 714 | + ); | |
| 579 | 715 | let y = push(&mut f, InstKind::Shl(x, z), IrType::Int(IntWidth::I32)); |
| 580 | 716 | let entry = f.entry; |
| 581 | 717 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
@@ -607,7 +743,11 @@ mod tests { | ||
| 607 | 743 | }, |
| 608 | 744 | ]; |
| 609 | 745 | let mut f = Function::new("f".into(), params, IrType::Int(IntWidth::I32)); |
| 610 | - let y = push(&mut f, InstKind::IMul(ValueId(0), ValueId(1)), IrType::Int(IntWidth::I32)); | |
| 746 | + let y = push( | |
| 747 | + &mut f, | |
| 748 | + InstKind::IMul(ValueId(0), ValueId(1)), | |
| 749 | + IrType::Int(IntWidth::I32), | |
| 750 | + ); | |
| 611 | 751 | let entry = f.entry; |
| 612 | 752 | f.block_mut(entry).terminator = Some(Terminator::Return(Some(y))); |
| 613 | 753 | m.add_function(f); |
src/opt/unroll.rsmodified@@ -58,14 +58,18 @@ | ||
| 58 | 58 | use super::pass::Pass; |
| 59 | 59 | use super::util::{find_natural_loops, predecessors, NaturalLoop}; |
| 60 | 60 | use crate::ir::inst::*; |
| 61 | -use crate::ir::types::{IrType, IntWidth}; | |
| 61 | +use crate::ir::types::{IntWidth, IrType}; | |
| 62 | 62 | use crate::ir::walk::prune_unreachable; |
| 63 | -use crate::lexer::{Span, Position}; | |
| 63 | +use crate::lexer::{Position, Span}; | |
| 64 | 64 | use std::collections::{HashMap, HashSet}; |
| 65 | 65 | |
| 66 | 66 | fn dummy_span() -> Span { |
| 67 | 67 | let pos = Position { line: 0, col: 0 }; |
| 68 | - Span { file_id: 0, start: pos, end: pos } | |
| 68 | + Span { | |
| 69 | + file_id: 0, | |
| 70 | + start: pos, | |
| 71 | + end: pos, | |
| 72 | + } | |
| 69 | 73 | } |
| 70 | 74 | |
| 71 | 75 | // --------------------------------------------------------------------------- |
@@ -94,7 +98,9 @@ const BODY_SIZE_MAX: usize = 30; | ||
| 94 | 98 | pub struct LoopUnroll; |
| 95 | 99 | |
| 96 | 100 | impl Pass for LoopUnroll { |
| 97 | - fn name(&self) -> &'static str { "loop-unroll" } | |
| 101 | + fn name(&self) -> &'static str { | |
| 102 | + "loop-unroll" | |
| 103 | + } | |
| 98 | 104 | |
| 99 | 105 | fn run(&self, module: &mut Module) -> bool { |
| 100 | 106 | let mut changed = false; |
@@ -135,23 +141,23 @@ fn unroll_in_function(func: &mut Function) -> bool { | ||
| 135 | 141 | |
| 136 | 142 | #[derive(Debug)] |
| 137 | 143 | struct LoopShape { |
| 138 | - preheader: BlockId, | |
| 139 | - header: BlockId, // block with IV block param; branches to cmp_block | |
| 140 | - cmp_block: BlockId, // block with icmp + condBr to body/exit | |
| 144 | + preheader: BlockId, | |
| 145 | + header: BlockId, // block with IV block param; branches to cmp_block | |
| 146 | + cmp_block: BlockId, // block with icmp + condBr to body/exit | |
| 141 | 147 | /// The computation blocks (everything that is not header, cmp_block, or |
| 142 | 148 | /// latch). Ordered in CFG traversal order from cmp_block's true-successor |
| 143 | 149 | /// to latch's predecessor. For most Fortran DO loops this is a single |
| 144 | 150 | /// block ("do_body"). Multi-block bodies are supported as long as the |
| 145 | 151 | /// sub-CFG is a linear chain with no branches to outside. |
| 146 | 152 | body_blocks: Vec<BlockId>, |
| 147 | - latch: BlockId, // iadd IV, 1 + br header(IV_next) | |
| 148 | - exit: BlockId, // cond_br false target | |
| 149 | - iv_param: ValueId, | |
| 150 | - iv_ty: IrType, | |
| 151 | - iv_init: i64, | |
| 152 | - iv_bound: i64, // inclusive upper bound | |
| 153 | - trip_count: usize, | |
| 154 | - exit_args: Vec<ValueId>, // args from cmp_block's false branch to exit | |
| 153 | + latch: BlockId, // iadd IV, 1 + br header(IV_next) | |
| 154 | + exit: BlockId, // cond_br false target | |
| 155 | + iv_param: ValueId, | |
| 156 | + iv_ty: IrType, | |
| 157 | + iv_init: i64, | |
| 158 | + iv_bound: i64, // inclusive upper bound | |
| 159 | + trip_count: usize, | |
| 160 | + exit_args: Vec<ValueId>, // args from cmp_block's false branch to exit | |
| 155 | 161 | } |
| 156 | 162 | |
| 157 | 163 | fn is_do_concurrent_loop( |
@@ -178,37 +184,51 @@ fn detect_simple_loop( | ||
| 178 | 184 | preds: &HashMap<BlockId, Vec<BlockId>>, |
| 179 | 185 | ) -> Option<LoopShape> { |
| 180 | 186 | // ---- structural requirements ---------------------------------------- |
| 181 | - if nl.latches.len() != 1 { return None; } | |
| 187 | + if nl.latches.len() != 1 { | |
| 188 | + return None; | |
| 189 | + } | |
| 182 | 190 | let latch = nl.latches[0]; |
| 183 | - if latch == nl.header { return None; } // no self-loop | |
| 184 | - // Body must have between 2 (header+latch) and 5 (header+cmp+body×N+latch) blocks. | |
| 185 | - if nl.body.len() < 2 || nl.body.len() > 5 { return None; } | |
| 191 | + if latch == nl.header { | |
| 192 | + return None; | |
| 193 | + } // no self-loop | |
| 194 | + // Body must have between 2 (header+latch) and 5 (header+cmp+body×N+latch) blocks. | |
| 195 | + if nl.body.len() < 2 || nl.body.len() > 5 { | |
| 196 | + return None; | |
| 197 | + } | |
| 186 | 198 | |
| 187 | 199 | let header = nl.header; |
| 188 | 200 | |
| 189 | 201 | // ---- header must have exactly 1 block param (the IV) ---------------- |
| 190 | 202 | let hdr = func.block(header); |
| 191 | - if hdr.params.len() != 1 { return None; } | |
| 203 | + if hdr.params.len() != 1 { | |
| 204 | + return None; | |
| 205 | + } | |
| 192 | 206 | let iv_param = hdr.params[0].id; |
| 193 | - let iv_ty = hdr.params[0].ty.clone(); | |
| 194 | - if !matches!(iv_ty, IrType::Int(_)) { return None; } | |
| 207 | + let iv_ty = hdr.params[0].ty.clone(); | |
| 208 | + if !matches!(iv_ty, IrType::Int(_)) { | |
| 209 | + return None; | |
| 210 | + } | |
| 195 | 211 | |
| 196 | 212 | // ---- find preheader ------------------------------------------------- |
| 197 | 213 | let header_preds = preds.get(&header)?; |
| 198 | - let mut outside: Vec<BlockId> = header_preds.iter() | |
| 214 | + let mut outside: Vec<BlockId> = header_preds | |
| 215 | + .iter() | |
| 199 | 216 | .copied() |
| 200 | 217 | .filter(|p| !nl.body.contains(p)) |
| 201 | 218 | .collect(); |
| 202 | 219 | outside.sort_by_key(|b| b.0); |
| 203 | 220 | outside.dedup(); |
| 204 | - if outside.len() != 1 { return None; } | |
| 221 | + if outside.len() != 1 { | |
| 222 | + return None; | |
| 223 | + } | |
| 205 | 224 | let preheader = outside[0]; |
| 206 | 225 | |
| 207 | 226 | // Preheader must branch to header with exactly 1 arg (the initial IV). |
| 208 | 227 | let ph_blk = func.block(preheader); |
| 209 | 228 | let iv_init = match &ph_blk.terminator { |
| 210 | - Some(Terminator::Branch(dest, args)) if *dest == header && args.len() == 1 => | |
| 211 | - resolve_const_int(func, args[0])?, | |
| 229 | + Some(Terminator::Branch(dest, args)) if *dest == header && args.len() == 1 => { | |
| 230 | + resolve_const_int(func, args[0])? | |
| 231 | + } | |
| 212 | 232 | _ => return None, |
| 213 | 233 | }; |
| 214 | 234 | |
@@ -229,21 +249,29 @@ fn detect_simple_loop( | ||
| 229 | 249 | } else { |
| 230 | 250 | // Case (b): header must be a transparent relay. |
| 231 | 251 | let hdr_blk = func.block(header); |
| 232 | - if !hdr_blk.insts.is_empty() { return None; } // must have 0 instructions | |
| 252 | + if !hdr_blk.insts.is_empty() { | |
| 253 | + return None; | |
| 254 | + } // must have 0 instructions | |
| 233 | 255 | let relay_target = match &hdr_blk.terminator { |
| 234 | 256 | Some(Terminator::Branch(t, args)) if args.is_empty() => *t, |
| 235 | 257 | _ => return None, |
| 236 | 258 | }; |
| 237 | - if !nl.body.contains(&relay_target) { return None; } | |
| 259 | + if !nl.body.contains(&relay_target) { | |
| 260 | + return None; | |
| 261 | + } | |
| 238 | 262 | let (bound, ex, args) = detect_bound_and_exit(func, relay_target, iv_param)?; |
| 239 | 263 | (relay_target, bound, ex, args) |
| 240 | 264 | } |
| 241 | 265 | }; |
| 242 | 266 | |
| 243 | - if nl.body.contains(&exit) { return None; } | |
| 267 | + if nl.body.contains(&exit) { | |
| 268 | + return None; | |
| 269 | + } | |
| 244 | 270 | |
| 245 | 271 | // ---- latch: stride-1 increment, passes only iv back to header ------ |
| 246 | - if !check_latch(func, latch, header, iv_param) { return None; } | |
| 272 | + if !check_latch(func, latch, header, iv_param) { | |
| 273 | + return None; | |
| 274 | + } | |
| 247 | 275 | |
| 248 | 276 | // ---- compute body_blocks: body − {header, cmp_block, latch} -------- |
| 249 | 277 | // |
@@ -264,19 +292,22 @@ fn detect_simple_loop( | ||
| 264 | 292 | // mem2reg may have threaded into OUTER loop latches via dominance-frontier |
| 265 | 293 | // placement. If that param is used outside nl.body, the header's block param |
| 266 | 294 | // becomes undefined after we discard the header block. |
| 267 | - for &bb in body_blocks.iter() | |
| 295 | + for &bb in body_blocks | |
| 296 | + .iter() | |
| 268 | 297 | .chain(std::iter::once(&header)) |
| 269 | 298 | .chain(std::iter::once(&cmp_block)) |
| 270 | 299 | .chain(std::iter::once(&latch)) |
| 271 | 300 | { |
| 272 | - if has_escaping_values(func, bb, &nl.body, preds) { return None; } | |
| 301 | + if has_escaping_values(func, bb, &nl.body, preds) { | |
| 302 | + return None; | |
| 303 | + } | |
| 273 | 304 | } |
| 274 | 305 | |
| 275 | 306 | // ---- size check ------------------------------------------------------ |
| 276 | - let total_insts: usize = body_blocks.iter() | |
| 277 | - .map(|&b| func.block(b).insts.len()) | |
| 278 | - .sum(); | |
| 279 | - if total_insts > BODY_SIZE_MAX { return None; } | |
| 307 | + let total_insts: usize = body_blocks.iter().map(|&b| func.block(b).insts.len()).sum(); | |
| 308 | + if total_insts > BODY_SIZE_MAX { | |
| 309 | + return None; | |
| 310 | + } | |
| 280 | 311 | |
| 281 | 312 | let full_unroll_max = if is_do_concurrent { |
| 282 | 313 | DO_CONCURRENT_FULL_UNROLL_MAX |
@@ -286,8 +317,12 @@ fn detect_simple_loop( | ||
| 286 | 317 | |
| 287 | 318 | // ---- trip count ------------------------------------------------------ |
| 288 | 319 | let trip_count_i64 = iv_bound - iv_init + 1; |
| 289 | - if trip_count_i64 <= 0 { return None; } | |
| 290 | - if trip_count_i64 > full_unroll_max { return None; } | |
| 320 | + if trip_count_i64 <= 0 { | |
| 321 | + return None; | |
| 322 | + } | |
| 323 | + if trip_count_i64 > full_unroll_max { | |
| 324 | + return None; | |
| 325 | + } | |
| 291 | 326 | |
| 292 | 327 | Some(LoopShape { |
| 293 | 328 | preheader, |
@@ -333,7 +368,9 @@ fn collect_body_chain( | ||
| 333 | 368 | Some(Terminator::CondBranch { true_dest, .. }) => *true_dest, |
| 334 | 369 | _ => return None, |
| 335 | 370 | }; |
| 336 | - if !loop_body.contains(&first) { return None; } | |
| 371 | + if !loop_body.contains(&first) { | |
| 372 | + return None; | |
| 373 | + } | |
| 337 | 374 | |
| 338 | 375 | if first == latch { |
| 339 | 376 | // Case (a): the cmp's true-successor is the latch itself. |
@@ -347,12 +384,18 @@ fn collect_body_chain( | ||
| 347 | 384 | let mut chain = Vec::new(); |
| 348 | 385 | let mut cur = first; |
| 349 | 386 | loop { |
| 350 | - if !loop_body.contains(&cur) { return None; } | |
| 387 | + if !loop_body.contains(&cur) { | |
| 388 | + return None; | |
| 389 | + } | |
| 351 | 390 | chain.push(cur); |
| 352 | - if cur == latch { break; } | |
| 391 | + if cur == latch { | |
| 392 | + break; | |
| 393 | + } | |
| 353 | 394 | let blk = func.block(cur); |
| 354 | 395 | // Each block in the chain must have no params (no join point). |
| 355 | - if !blk.params.is_empty() { return None; } | |
| 396 | + if !blk.params.is_empty() { | |
| 397 | + return None; | |
| 398 | + } | |
| 356 | 399 | // Its terminator must be an unconditional branch to the next block. |
| 357 | 400 | match &blk.terminator { |
| 358 | 401 | Some(Terminator::Branch(next, args)) if args.is_empty() => { |
@@ -360,7 +403,9 @@ fn collect_body_chain( | ||
| 360 | 403 | } |
| 361 | 404 | _ => return None, |
| 362 | 405 | } |
| 363 | - if chain.len() > 4 { return None; } // safety limit | |
| 406 | + if chain.len() > 4 { | |
| 407 | + return None; | |
| 408 | + } // safety limit | |
| 364 | 409 | } |
| 365 | 410 | Some(chain) |
| 366 | 411 | } |
@@ -388,8 +433,8 @@ fn detect_bound_and_exit( | ||
| 388 | 433 | |
| 389 | 434 | // Find the single ICmp that involves the IV. |
| 390 | 435 | let mut cmp_id: Option<ValueId> = None; |
| 391 | - let mut bound: Option<i64> = None; | |
| 392 | - let mut is_lt = false; // true when we need bound-1 | |
| 436 | + let mut bound: Option<i64> = None; | |
| 437 | + let mut is_lt = false; // true when we need bound-1 | |
| 393 | 438 | |
| 394 | 439 | for inst in &hdr.insts { |
| 395 | 440 | if let InstKind::ICmp(op, a, b) = &inst.kind { |
@@ -402,10 +447,22 @@ fn detect_bound_and_exit( | ||
| 402 | 447 | }; |
| 403 | 448 | let c = resolve_const_int(func, other)?; |
| 404 | 449 | match op { |
| 405 | - CmpOp::Le => { bound = Some(c); is_lt = false; } | |
| 406 | - CmpOp::Lt => { bound = Some(c); is_lt = true; } | |
| 407 | - CmpOp::Ge => { bound = Some(c); is_lt = false; } // iv >= c means upper = c when commuted | |
| 408 | - CmpOp::Gt => { bound = Some(c); is_lt = true; } // iv > c means upper = c-1 commuted | |
| 450 | + CmpOp::Le => { | |
| 451 | + bound = Some(c); | |
| 452 | + is_lt = false; | |
| 453 | + } | |
| 454 | + CmpOp::Lt => { | |
| 455 | + bound = Some(c); | |
| 456 | + is_lt = true; | |
| 457 | + } | |
| 458 | + CmpOp::Ge => { | |
| 459 | + bound = Some(c); | |
| 460 | + is_lt = false; | |
| 461 | + } // iv >= c means upper = c when commuted | |
| 462 | + CmpOp::Gt => { | |
| 463 | + bound = Some(c); | |
| 464 | + is_lt = true; | |
| 465 | + } // iv > c means upper = c-1 commuted | |
| 409 | 466 | _ => return None, |
| 410 | 467 | } |
| 411 | 468 | cmp_id = Some(inst.id); |
@@ -418,9 +475,13 @@ fn detect_bound_and_exit( | ||
| 418 | 475 | |
| 419 | 476 | // Terminator must be CondBranch on that cmp. |
| 420 | 477 | match &hdr.terminator { |
| 421 | - Some(Terminator::CondBranch { cond, true_dest, true_args, false_dest, false_args }) | |
| 422 | - if *cond == cmp_id => | |
| 423 | - { | |
| 478 | + Some(Terminator::CondBranch { | |
| 479 | + cond, | |
| 480 | + true_dest, | |
| 481 | + true_args, | |
| 482 | + false_dest, | |
| 483 | + false_args, | |
| 484 | + }) if *cond == cmp_id => { | |
| 424 | 485 | // true → body (latch), false → exit. |
| 425 | 486 | // Check which side is the latch. We don't have the latch id here; |
| 426 | 487 | // we'll accept either ordering and return the "false" side as exit. |
@@ -445,8 +506,7 @@ fn check_latch(func: &Function, latch: BlockId, header: BlockId, iv: ValueId) -> | ||
| 445 | 506 | |
| 446 | 507 | // Terminator must be unconditional branch to header with exactly 1 arg. |
| 447 | 508 | let iv_next = match &blk.terminator { |
| 448 | - Some(Terminator::Branch(dest, args)) if *dest == header && args.len() == 1 => | |
| 449 | - args[0], | |
| 509 | + Some(Terminator::Branch(dest, args)) if *dest == header && args.len() == 1 => args[0], | |
| 450 | 510 | _ => return false, |
| 451 | 511 | }; |
| 452 | 512 | |
@@ -455,8 +515,16 @@ fn check_latch(func: &Function, latch: BlockId, header: BlockId, iv: ValueId) -> | ||
| 455 | 515 | if inst.id == iv_next { |
| 456 | 516 | match &inst.kind { |
| 457 | 517 | InstKind::IAdd(a, b) => { |
| 458 | - let (is_iv, other) = if *a == iv { (true, *b) } else if *b == iv { (true, *a) } else { (false, *a) }; | |
| 459 | - if !is_iv { return false; } | |
| 518 | + let (is_iv, other) = if *a == iv { | |
| 519 | + (true, *b) | |
| 520 | + } else if *b == iv { | |
| 521 | + (true, *a) | |
| 522 | + } else { | |
| 523 | + (false, *a) | |
| 524 | + }; | |
| 525 | + if !is_iv { | |
| 526 | + return false; | |
| 527 | + } | |
| 460 | 528 | return resolve_const_int(func, other) == Some(1); |
| 461 | 529 | } |
| 462 | 530 | _ => return false, |
@@ -484,24 +552,36 @@ fn has_escaping_values( | ||
| 484 | 552 | // latch it becomes undefined after unrolling removes the block. |
| 485 | 553 | let latch_blk = func.block(latch); |
| 486 | 554 | let mut latch_defs: HashSet<ValueId> = HashSet::new(); |
| 487 | - for bp in &latch_blk.params { latch_defs.insert(bp.id); } | |
| 488 | - for inst in &latch_blk.insts { latch_defs.insert(inst.id); } | |
| 555 | + for bp in &latch_blk.params { | |
| 556 | + latch_defs.insert(bp.id); | |
| 557 | + } | |
| 558 | + for inst in &latch_blk.insts { | |
| 559 | + latch_defs.insert(inst.id); | |
| 560 | + } | |
| 489 | 561 | |
| 490 | - if latch_defs.is_empty() { return false; } | |
| 562 | + if latch_defs.is_empty() { | |
| 563 | + return false; | |
| 564 | + } | |
| 491 | 565 | |
| 492 | 566 | // Check every block outside the loop body for uses of those values. |
| 493 | 567 | for block in &func.blocks { |
| 494 | - if body.contains(&block.id) { continue; } | |
| 568 | + if body.contains(&block.id) { | |
| 569 | + continue; | |
| 570 | + } | |
| 495 | 571 | // Check instruction operands. |
| 496 | 572 | for inst in &block.insts { |
| 497 | 573 | for use_id in inst_uses(&inst.kind) { |
| 498 | - if latch_defs.contains(&use_id) { return true; } | |
| 574 | + if latch_defs.contains(&use_id) { | |
| 575 | + return true; | |
| 576 | + } | |
| 499 | 577 | } |
| 500 | 578 | } |
| 501 | 579 | // Check terminator operands. |
| 502 | 580 | if let Some(term) = &block.terminator { |
| 503 | 581 | for &use_id in terminator_uses_vec(term).iter() { |
| 504 | - if latch_defs.contains(&use_id) { return true; } | |
| 582 | + if latch_defs.contains(&use_id) { | |
| 583 | + return true; | |
| 584 | + } | |
| 505 | 585 | } |
| 506 | 586 | } |
| 507 | 587 | // Also check block params of outside blocks that receive args from latch. |
@@ -526,15 +606,20 @@ fn block_contains_load(func: &Function, block: BlockId) -> bool { | ||
| 526 | 606 | fn terminator_uses_vec(term: &Terminator) -> Vec<ValueId> { |
| 527 | 607 | let mut out = Vec::new(); |
| 528 | 608 | match term { |
| 529 | - Terminator::Return(Some(v)) => out.push(*v), | |
| 530 | - Terminator::Branch(_, args) => out.extend(args), | |
| 531 | - Terminator::CondBranch { cond, true_args, false_args, .. } => { | |
| 609 | + Terminator::Return(Some(v)) => out.push(*v), | |
| 610 | + Terminator::Branch(_, args) => out.extend(args), | |
| 611 | + Terminator::CondBranch { | |
| 612 | + cond, | |
| 613 | + true_args, | |
| 614 | + false_args, | |
| 615 | + .. | |
| 616 | + } => { | |
| 532 | 617 | out.push(*cond); |
| 533 | 618 | out.extend(true_args); |
| 534 | 619 | out.extend(false_args); |
| 535 | 620 | } |
| 536 | - Terminator::Switch { selector, .. } => out.push(*selector), | |
| 537 | - _ => {} | |
| 621 | + Terminator::Switch { selector, .. } => out.push(*selector), | |
| 622 | + _ => {} | |
| 538 | 623 | } |
| 539 | 624 | out |
| 540 | 625 | } |
@@ -550,12 +635,17 @@ fn inst_uses(kind: &InstKind) -> Vec<ValueId> { | ||
| 550 | 635 | |
| 551 | 636 | fn do_unroll(func: &mut Function, shape: LoopShape) { |
| 552 | 637 | // Collect per-body-block instructions once (before we mutate func). |
| 553 | - let body_snapshots: Vec<Vec<Inst>> = shape.body_blocks.iter() | |
| 638 | + let body_snapshots: Vec<Vec<Inst>> = shape | |
| 639 | + .body_blocks | |
| 640 | + .iter() | |
| 554 | 641 | .map(|&b| func.block(b).insts.clone()) |
| 555 | 642 | .collect(); |
| 556 | 643 | |
| 557 | 644 | // Determine the IV width. |
| 558 | - let iv_width = match &shape.iv_ty { IrType::Int(w) => *w, _ => IntWidth::I32 }; | |
| 645 | + let iv_width = match &shape.iv_ty { | |
| 646 | + IrType::Int(w) => *w, | |
| 647 | + _ => IntWidth::I32, | |
| 648 | + }; | |
| 559 | 649 | |
| 560 | 650 | // For each iteration, we create one new block per body block, cloned |
| 561 | 651 | // with the IV substituted by a constant. The chain is: |
@@ -673,8 +763,7 @@ fn do_unroll(func: &mut Function, shape: LoopShape) { | ||
| 673 | 763 | (after_iter, exit_args.clone()) |
| 674 | 764 | }; |
| 675 | 765 | let _ = original_body_next[bi]; // consumed above |
| 676 | - func.block_mut(cur_blk).terminator = | |
| 677 | - Some(Terminator::Branch(next_blk, args)); | |
| 766 | + func.block_mut(cur_blk).terminator = Some(Terminator::Branch(next_blk, args)); | |
| 678 | 767 | } |
| 679 | 768 | } |
| 680 | 769 | } |
@@ -690,9 +779,9 @@ fn do_unroll(func: &mut Function, shape: LoopShape) { | ||
| 690 | 779 | Some(Terminator::Branch(first_block, preheader_args)); |
| 691 | 780 | |
| 692 | 781 | // Mark loop control blocks as Unreachable → prune_unreachable removes them. |
| 693 | - func.block_mut(shape.header).terminator = Some(Terminator::Unreachable); | |
| 782 | + func.block_mut(shape.header).terminator = Some(Terminator::Unreachable); | |
| 694 | 783 | func.block_mut(shape.cmp_block).terminator = Some(Terminator::Unreachable); |
| 695 | - func.block_mut(shape.latch).terminator = Some(Terminator::Unreachable); | |
| 784 | + func.block_mut(shape.latch).terminator = Some(Terminator::Unreachable); | |
| 696 | 785 | for &b in &shape.body_blocks { |
| 697 | 786 | func.block_mut(b).terminator = Some(Terminator::Unreachable); |
| 698 | 787 | } |
@@ -703,30 +792,30 @@ fn remap_kind(kind: &InstKind, subst: &HashMap<ValueId, ValueId>) -> InstKind { | ||
| 703 | 792 | let r = |v: &ValueId| *subst.get(v).unwrap_or(v); |
| 704 | 793 | match kind { |
| 705 | 794 | // Constants — no operands. |
| 706 | - InstKind::ConstInt(v, w) => InstKind::ConstInt(*v, *w), | |
| 707 | - InstKind::ConstFloat(v, w) => InstKind::ConstFloat(*v, *w), | |
| 708 | - InstKind::ConstBool(v) => InstKind::ConstBool(*v), | |
| 709 | - InstKind::ConstString(v) => InstKind::ConstString(v.clone()), | |
| 710 | - InstKind::Undef(t) => InstKind::Undef(t.clone()), | |
| 711 | - InstKind::GlobalAddr(s) => InstKind::GlobalAddr(s.clone()), | |
| 795 | + InstKind::ConstInt(v, w) => InstKind::ConstInt(*v, *w), | |
| 796 | + InstKind::ConstFloat(v, w) => InstKind::ConstFloat(*v, *w), | |
| 797 | + InstKind::ConstBool(v) => InstKind::ConstBool(*v), | |
| 798 | + InstKind::ConstString(v) => InstKind::ConstString(v.clone()), | |
| 799 | + InstKind::Undef(t) => InstKind::Undef(t.clone()), | |
| 800 | + InstKind::GlobalAddr(s) => InstKind::GlobalAddr(s.clone()), | |
| 712 | 801 | |
| 713 | 802 | // Integer arithmetic. |
| 714 | - InstKind::IAdd(a, b) => InstKind::IAdd(r(a), r(b)), | |
| 715 | - InstKind::ISub(a, b) => InstKind::ISub(r(a), r(b)), | |
| 716 | - InstKind::IMul(a, b) => InstKind::IMul(r(a), r(b)), | |
| 717 | - InstKind::IDiv(a, b) => InstKind::IDiv(r(a), r(b)), | |
| 718 | - InstKind::IMod(a, b) => InstKind::IMod(r(a), r(b)), | |
| 719 | - InstKind::INeg(a) => InstKind::INeg(r(a)), | |
| 803 | + InstKind::IAdd(a, b) => InstKind::IAdd(r(a), r(b)), | |
| 804 | + InstKind::ISub(a, b) => InstKind::ISub(r(a), r(b)), | |
| 805 | + InstKind::IMul(a, b) => InstKind::IMul(r(a), r(b)), | |
| 806 | + InstKind::IDiv(a, b) => InstKind::IDiv(r(a), r(b)), | |
| 807 | + InstKind::IMod(a, b) => InstKind::IMod(r(a), r(b)), | |
| 808 | + InstKind::INeg(a) => InstKind::INeg(r(a)), | |
| 720 | 809 | |
| 721 | 810 | // Float arithmetic. |
| 722 | - InstKind::FAdd(a, b) => InstKind::FAdd(r(a), r(b)), | |
| 723 | - InstKind::FSub(a, b) => InstKind::FSub(r(a), r(b)), | |
| 724 | - InstKind::FMul(a, b) => InstKind::FMul(r(a), r(b)), | |
| 725 | - InstKind::FDiv(a, b) => InstKind::FDiv(r(a), r(b)), | |
| 726 | - InstKind::FNeg(a) => InstKind::FNeg(r(a)), | |
| 727 | - InstKind::FAbs(a) => InstKind::FAbs(r(a)), | |
| 728 | - InstKind::FSqrt(a) => InstKind::FSqrt(r(a)), | |
| 729 | - InstKind::FPow(a, b) => InstKind::FPow(r(a), r(b)), | |
| 811 | + InstKind::FAdd(a, b) => InstKind::FAdd(r(a), r(b)), | |
| 812 | + InstKind::FSub(a, b) => InstKind::FSub(r(a), r(b)), | |
| 813 | + InstKind::FMul(a, b) => InstKind::FMul(r(a), r(b)), | |
| 814 | + InstKind::FDiv(a, b) => InstKind::FDiv(r(a), r(b)), | |
| 815 | + InstKind::FNeg(a) => InstKind::FNeg(r(a)), | |
| 816 | + InstKind::FAbs(a) => InstKind::FAbs(r(a)), | |
| 817 | + InstKind::FSqrt(a) => InstKind::FSqrt(r(a)), | |
| 818 | + InstKind::FPow(a, b) => InstKind::FPow(r(a), r(b)), | |
| 730 | 819 | |
| 731 | 820 | // Comparison. |
| 732 | 821 | InstKind::ICmp(op, a, b) => InstKind::ICmp(*op, r(a), r(b)), |
@@ -734,49 +823,50 @@ fn remap_kind(kind: &InstKind, subst: &HashMap<ValueId, ValueId>) -> InstKind { | ||
| 734 | 823 | |
| 735 | 824 | // Logic. |
| 736 | 825 | InstKind::And(a, b) => InstKind::And(r(a), r(b)), |
| 737 | - InstKind::Or(a, b) => InstKind::Or(r(a), r(b)), | |
| 738 | - InstKind::Not(a) => InstKind::Not(r(a)), | |
| 826 | + InstKind::Or(a, b) => InstKind::Or(r(a), r(b)), | |
| 827 | + InstKind::Not(a) => InstKind::Not(r(a)), | |
| 739 | 828 | |
| 740 | 829 | // Select. |
| 741 | 830 | InstKind::Select(c, t, f) => InstKind::Select(r(c), r(t), r(f)), |
| 742 | 831 | |
| 743 | 832 | // Bitwise. |
| 744 | - InstKind::BitAnd(a, b) => InstKind::BitAnd(r(a), r(b)), | |
| 745 | - InstKind::BitOr(a, b) => InstKind::BitOr(r(a), r(b)), | |
| 746 | - InstKind::BitXor(a, b) => InstKind::BitXor(r(a), r(b)), | |
| 747 | - InstKind::BitNot(a) => InstKind::BitNot(r(a)), | |
| 748 | - InstKind::Shl(a, b) => InstKind::Shl(r(a), r(b)), | |
| 749 | - InstKind::LShr(a, b) => InstKind::LShr(r(a), r(b)), | |
| 750 | - InstKind::AShr(a, b) => InstKind::AShr(r(a), r(b)), | |
| 751 | - InstKind::CountLeadingZeros(a) => InstKind::CountLeadingZeros(r(a)), | |
| 752 | - InstKind::CountTrailingZeros(a) => InstKind::CountTrailingZeros(r(a)), | |
| 753 | - InstKind::PopCount(a) => InstKind::PopCount(r(a)), | |
| 833 | + InstKind::BitAnd(a, b) => InstKind::BitAnd(r(a), r(b)), | |
| 834 | + InstKind::BitOr(a, b) => InstKind::BitOr(r(a), r(b)), | |
| 835 | + InstKind::BitXor(a, b) => InstKind::BitXor(r(a), r(b)), | |
| 836 | + InstKind::BitNot(a) => InstKind::BitNot(r(a)), | |
| 837 | + InstKind::Shl(a, b) => InstKind::Shl(r(a), r(b)), | |
| 838 | + InstKind::LShr(a, b) => InstKind::LShr(r(a), r(b)), | |
| 839 | + InstKind::AShr(a, b) => InstKind::AShr(r(a), r(b)), | |
| 840 | + InstKind::CountLeadingZeros(a) => InstKind::CountLeadingZeros(r(a)), | |
| 841 | + InstKind::CountTrailingZeros(a) => InstKind::CountTrailingZeros(r(a)), | |
| 842 | + InstKind::PopCount(a) => InstKind::PopCount(r(a)), | |
| 754 | 843 | |
| 755 | 844 | // Conversions. |
| 756 | - InstKind::IntToFloat(a, w) => InstKind::IntToFloat(r(a), *w), | |
| 757 | - InstKind::FloatToInt(a, w) => InstKind::FloatToInt(r(a), *w), | |
| 758 | - InstKind::FloatExtend(a, w) => InstKind::FloatExtend(r(a), *w), | |
| 759 | - InstKind::FloatTrunc(a, w) => InstKind::FloatTrunc(r(a), *w), | |
| 760 | - InstKind::IntExtend(a, w, s) => InstKind::IntExtend(r(a), *w, *s), | |
| 761 | - InstKind::IntTrunc(a, w) => InstKind::IntTrunc(r(a), *w), | |
| 762 | - InstKind::PtrToInt(a) => InstKind::PtrToInt(r(a)), | |
| 763 | - InstKind::IntToPtr(a, ty) => InstKind::IntToPtr(r(a), ty.clone()), | |
| 845 | + InstKind::IntToFloat(a, w) => InstKind::IntToFloat(r(a), *w), | |
| 846 | + InstKind::FloatToInt(a, w) => InstKind::FloatToInt(r(a), *w), | |
| 847 | + InstKind::FloatExtend(a, w) => InstKind::FloatExtend(r(a), *w), | |
| 848 | + InstKind::FloatTrunc(a, w) => InstKind::FloatTrunc(r(a), *w), | |
| 849 | + InstKind::IntExtend(a, w, s) => InstKind::IntExtend(r(a), *w, *s), | |
| 850 | + InstKind::IntTrunc(a, w) => InstKind::IntTrunc(r(a), *w), | |
| 851 | + InstKind::PtrToInt(a) => InstKind::PtrToInt(r(a)), | |
| 852 | + InstKind::IntToPtr(a, ty) => InstKind::IntToPtr(r(a), ty.clone()), | |
| 764 | 853 | |
| 765 | 854 | // Memory. |
| 766 | - InstKind::Alloca(t) => InstKind::Alloca(t.clone()), | |
| 767 | - InstKind::Load(a) => InstKind::Load(r(a)), | |
| 855 | + InstKind::Alloca(t) => InstKind::Alloca(t.clone()), | |
| 856 | + InstKind::Load(a) => InstKind::Load(r(a)), | |
| 768 | 857 | InstKind::Store(v, p) => InstKind::Store(r(v), r(p)), |
| 769 | - InstKind::GetElementPtr(base, idxs) => | |
| 770 | - InstKind::GetElementPtr(r(base), idxs.iter().map(&r).collect()), | |
| 858 | + InstKind::GetElementPtr(base, idxs) => { | |
| 859 | + InstKind::GetElementPtr(r(base), idxs.iter().map(&r).collect()) | |
| 860 | + } | |
| 771 | 861 | |
| 772 | 862 | // Calls. |
| 773 | - InstKind::Call(f, args) => | |
| 774 | - InstKind::Call(f.clone(), args.iter().map(&r).collect()), | |
| 775 | - InstKind::RuntimeCall(f, args) => | |
| 776 | - InstKind::RuntimeCall(f.clone(), args.iter().map(&r).collect()), | |
| 863 | + InstKind::Call(f, args) => InstKind::Call(f.clone(), args.iter().map(&r).collect()), | |
| 864 | + InstKind::RuntimeCall(f, args) => { | |
| 865 | + InstKind::RuntimeCall(f.clone(), args.iter().map(&r).collect()) | |
| 866 | + } | |
| 777 | 867 | |
| 778 | 868 | // Aggregates. |
| 779 | - InstKind::ExtractField(v, idx) => InstKind::ExtractField(r(v), *idx), | |
| 869 | + InstKind::ExtractField(v, idx) => InstKind::ExtractField(r(v), *idx), | |
| 780 | 870 | InstKind::InsertField(v, idx, fld) => InstKind::InsertField(r(v), *idx, r(fld)), |
| 781 | 871 | } |
| 782 | 872 | } |
@@ -788,11 +878,13 @@ fn remap_kind(kind: &InstKind, subst: &HashMap<ValueId, ValueId>) -> InstKind { | ||
| 788 | 878 | #[cfg(test)] |
| 789 | 879 | mod tests { |
| 790 | 880 | use super::*; |
| 791 | - use crate::ir::types::{IrType, IntWidth}; | |
| 792 | - use crate::opt::pass::Pass; | |
| 881 | + use crate::ir::types::{IntWidth, IrType}; | |
| 793 | 882 | use crate::lexer::Span; |
| 883 | + use crate::opt::pass::Pass; | |
| 794 | 884 | |
| 795 | - fn span() -> Span { super::dummy_span() } | |
| 885 | + fn span() -> Span { | |
| 886 | + super::dummy_span() | |
| 887 | + } | |
| 796 | 888 | |
| 797 | 889 | /// Build a minimal Module with one function containing a simple |
| 798 | 890 | /// counted loop: do i = 1, N; a(i_offset) = const(42); end do |
@@ -806,77 +898,94 @@ mod tests { | ||
| 806 | 898 | |
| 807 | 899 | // Allocate block IDs. |
| 808 | 900 | let header_id = f.create_block(&format!("{}_check", prefix)); |
| 809 | - let latch_id = f.create_block(&format!("{}_body", prefix)); | |
| 810 | - let exit_id = f.create_block("exit"); | |
| 811 | - let entry_id = f.entry; // preheader | |
| 901 | + let latch_id = f.create_block(&format!("{}_body", prefix)); | |
| 902 | + let exit_id = f.create_block("exit"); | |
| 903 | + let entry_id = f.entry; // preheader | |
| 812 | 904 | |
| 813 | 905 | // ---- entry (preheader) ------------------------------------------- |
| 814 | 906 | // const lo |
| 815 | 907 | let lo_val = f.next_value_id(); |
| 816 | 908 | f.block_mut(entry_id).insts.push(Inst { |
| 817 | - id: lo_val, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 909 | + id: lo_val, | |
| 910 | + ty: IrType::Int(IntWidth::I64), | |
| 911 | + span: span(), | |
| 818 | 912 | kind: InstKind::ConstInt(lo as i128, IntWidth::I64), |
| 819 | 913 | }); |
| 820 | 914 | // alloca for the "array" (just a slot we store into) |
| 821 | 915 | let alloca_val = f.next_value_id(); |
| 822 | 916 | f.block_mut(entry_id).insts.push(Inst { |
| 823 | - id: alloca_val, ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I64))), span: span(), | |
| 917 | + id: alloca_val, | |
| 918 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I64))), | |
| 919 | + span: span(), | |
| 824 | 920 | kind: InstKind::Alloca(IrType::Int(IntWidth::I64)), |
| 825 | 921 | }); |
| 826 | 922 | // br header(lo) |
| 827 | - f.block_mut(entry_id).terminator = | |
| 828 | - Some(Terminator::Branch(header_id, vec![lo_val])); | |
| 923 | + f.block_mut(entry_id).terminator = Some(Terminator::Branch(header_id, vec![lo_val])); | |
| 829 | 924 | |
| 830 | 925 | // ---- header (%i) ------------------------------------------------- |
| 831 | 926 | let iv_param = f.next_value_id(); |
| 832 | 927 | f.block_mut(header_id).params.push(BlockParam { |
| 833 | - id: iv_param, ty: IrType::Int(IntWidth::I64), | |
| 928 | + id: iv_param, | |
| 929 | + ty: IrType::Int(IntWidth::I64), | |
| 834 | 930 | }); |
| 835 | 931 | // const hi |
| 836 | 932 | let hi_val = f.next_value_id(); |
| 837 | 933 | f.block_mut(header_id).insts.push(Inst { |
| 838 | - id: hi_val, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 934 | + id: hi_val, | |
| 935 | + ty: IrType::Int(IntWidth::I64), | |
| 936 | + span: span(), | |
| 839 | 937 | kind: InstKind::ConstInt(hi as i128, IntWidth::I64), |
| 840 | 938 | }); |
| 841 | 939 | // %cmp = icmp.sle %i, hi |
| 842 | 940 | let cmp_val = f.next_value_id(); |
| 843 | 941 | f.block_mut(header_id).insts.push(Inst { |
| 844 | - id: cmp_val, ty: IrType::Bool, span: span(), | |
| 942 | + id: cmp_val, | |
| 943 | + ty: IrType::Bool, | |
| 944 | + span: span(), | |
| 845 | 945 | kind: InstKind::ICmp(CmpOp::Le, iv_param, hi_val), |
| 846 | 946 | }); |
| 847 | 947 | // condBr %cmp, latch, exit |
| 848 | 948 | f.block_mut(header_id).terminator = Some(Terminator::CondBranch { |
| 849 | 949 | cond: cmp_val, |
| 850 | - true_dest: latch_id, true_args: vec![], | |
| 851 | - false_dest: exit_id, false_args: vec![], | |
| 950 | + true_dest: latch_id, | |
| 951 | + true_args: vec![], | |
| 952 | + false_dest: exit_id, | |
| 953 | + false_args: vec![], | |
| 852 | 954 | }); |
| 853 | 955 | |
| 854 | 956 | // ---- latch ------------------------------------------------------- |
| 855 | 957 | // Store 42 to the alloca (simulates a(i) = 42). |
| 856 | 958 | let const42 = f.next_value_id(); |
| 857 | 959 | f.block_mut(latch_id).insts.push(Inst { |
| 858 | - id: const42, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 960 | + id: const42, | |
| 961 | + ty: IrType::Int(IntWidth::I64), | |
| 962 | + span: span(), | |
| 859 | 963 | kind: InstKind::ConstInt(42, IntWidth::I64), |
| 860 | 964 | }); |
| 861 | 965 | let store_id = f.next_value_id(); |
| 862 | 966 | f.block_mut(latch_id).insts.push(Inst { |
| 863 | - id: store_id, ty: IrType::Void, span: span(), | |
| 967 | + id: store_id, | |
| 968 | + ty: IrType::Void, | |
| 969 | + span: span(), | |
| 864 | 970 | kind: InstKind::Store(const42, alloca_val), |
| 865 | 971 | }); |
| 866 | 972 | // %i_next = iadd %i, 1 |
| 867 | 973 | let one = f.next_value_id(); |
| 868 | 974 | f.block_mut(latch_id).insts.push(Inst { |
| 869 | - id: one, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 975 | + id: one, | |
| 976 | + ty: IrType::Int(IntWidth::I64), | |
| 977 | + span: span(), | |
| 870 | 978 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 871 | 979 | }); |
| 872 | 980 | let i_next = f.next_value_id(); |
| 873 | 981 | f.block_mut(latch_id).insts.push(Inst { |
| 874 | - id: i_next, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 982 | + id: i_next, | |
| 983 | + ty: IrType::Int(IntWidth::I64), | |
| 984 | + span: span(), | |
| 875 | 985 | kind: InstKind::IAdd(iv_param, one), |
| 876 | 986 | }); |
| 877 | 987 | // br header(%i_next) |
| 878 | - f.block_mut(latch_id).terminator = | |
| 879 | - Some(Terminator::Branch(header_id, vec![i_next])); | |
| 988 | + f.block_mut(latch_id).terminator = Some(Terminator::Branch(header_id, vec![i_next])); | |
| 880 | 989 | |
| 881 | 990 | // ---- exit -------------------------------------------------------- |
| 882 | 991 | f.block_mut(exit_id).terminator = Some(Terminator::Return(None)); |
@@ -902,9 +1011,17 @@ mod tests { | ||
| 902 | 1011 | assert_eq!(f.blocks.len(), 6, "expected entry + 4 iter blocks + exit"); |
| 903 | 1012 | |
| 904 | 1013 | // Each iteration block has a ConstInt for that iteration's IV value. |
| 905 | - let all_iv_consts: Vec<i128> = f.blocks.iter() | |
| 1014 | + let all_iv_consts: Vec<i128> = f | |
| 1015 | + .blocks | |
| 1016 | + .iter() | |
| 906 | 1017 | .flat_map(|b| b.insts.iter()) |
| 907 | - .filter_map(|i| if let InstKind::ConstInt(v, _) = i.kind { Some(v) } else { None }) | |
| 1018 | + .filter_map(|i| { | |
| 1019 | + if let InstKind::ConstInt(v, _) = i.kind { | |
| 1020 | + Some(v) | |
| 1021 | + } else { | |
| 1022 | + None | |
| 1023 | + } | |
| 1024 | + }) | |
| 908 | 1025 | .collect(); |
| 909 | 1026 | assert!(all_iv_consts.contains(&1), "IV=1 should appear"); |
| 910 | 1027 | assert!(all_iv_consts.contains(&2), "IV=2 should appear"); |
@@ -925,7 +1042,10 @@ mod tests { | ||
| 925 | 1042 | let mut m = build_counted_loop_with_prefix(1, 10, "doconc"); |
| 926 | 1043 | let pass = LoopUnroll; |
| 927 | 1044 | let changed = pass.run(&mut m); |
| 928 | - assert!(changed, "DO CONCURRENT trip-count-10 loop should fully unroll"); | |
| 1045 | + assert!( | |
| 1046 | + changed, | |
| 1047 | + "DO CONCURRENT trip-count-10 loop should fully unroll" | |
| 1048 | + ); | |
| 929 | 1049 | |
| 930 | 1050 | let f = &m.functions[0]; |
| 931 | 1051 | assert_eq!(f.blocks.len(), 12, "expected entry + 10 iter blocks + exit"); |
@@ -946,59 +1066,80 @@ mod tests { | ||
| 946 | 1066 | let mut m = Module::new("test".into()); |
| 947 | 1067 | let mut f = Function::new("reduce".into(), vec![], IrType::Void); |
| 948 | 1068 | let header_id = f.create_block("header"); |
| 949 | - let latch_id = f.create_block("latch"); | |
| 950 | - let exit_id = f.create_block("exit"); | |
| 951 | - let entry_id = f.entry; | |
| 1069 | + let latch_id = f.create_block("latch"); | |
| 1070 | + let exit_id = f.create_block("exit"); | |
| 1071 | + let entry_id = f.entry; | |
| 952 | 1072 | |
| 953 | 1073 | // Entry: br header(1, 0) |
| 954 | 1074 | let lo = f.next_value_id(); |
| 955 | 1075 | f.block_mut(entry_id).insts.push(Inst { |
| 956 | - id: lo, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1076 | + id: lo, | |
| 1077 | + ty: IrType::Int(IntWidth::I64), | |
| 1078 | + span: span(), | |
| 957 | 1079 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 958 | 1080 | }); |
| 959 | 1081 | let z = f.next_value_id(); |
| 960 | 1082 | f.block_mut(entry_id).insts.push(Inst { |
| 961 | - id: z, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1083 | + id: z, | |
| 1084 | + ty: IrType::Int(IntWidth::I64), | |
| 1085 | + span: span(), | |
| 962 | 1086 | kind: InstKind::ConstInt(0, IntWidth::I64), |
| 963 | 1087 | }); |
| 964 | - f.block_mut(entry_id).terminator = | |
| 965 | - Some(Terminator::Branch(header_id, vec![lo, z])); | |
| 1088 | + f.block_mut(entry_id).terminator = Some(Terminator::Branch(header_id, vec![lo, z])); | |
| 966 | 1089 | |
| 967 | 1090 | // Header: 2 params (iv, acc) |
| 968 | - let iv = f.next_value_id(); | |
| 1091 | + let iv = f.next_value_id(); | |
| 969 | 1092 | let acc = f.next_value_id(); |
| 970 | - f.block_mut(header_id).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I64) }); | |
| 971 | - f.block_mut(header_id).params.push(BlockParam { id: acc, ty: IrType::Int(IntWidth::I64) }); | |
| 1093 | + f.block_mut(header_id).params.push(BlockParam { | |
| 1094 | + id: iv, | |
| 1095 | + ty: IrType::Int(IntWidth::I64), | |
| 1096 | + }); | |
| 1097 | + f.block_mut(header_id).params.push(BlockParam { | |
| 1098 | + id: acc, | |
| 1099 | + ty: IrType::Int(IntWidth::I64), | |
| 1100 | + }); | |
| 972 | 1101 | let hi = f.next_value_id(); |
| 973 | 1102 | f.block_mut(header_id).insts.push(Inst { |
| 974 | - id: hi, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1103 | + id: hi, | |
| 1104 | + ty: IrType::Int(IntWidth::I64), | |
| 1105 | + span: span(), | |
| 975 | 1106 | kind: InstKind::ConstInt(4, IntWidth::I64), |
| 976 | 1107 | }); |
| 977 | 1108 | let cmp = f.next_value_id(); |
| 978 | 1109 | f.block_mut(header_id).insts.push(Inst { |
| 979 | - id: cmp, ty: IrType::Bool, span: span(), | |
| 1110 | + id: cmp, | |
| 1111 | + ty: IrType::Bool, | |
| 1112 | + span: span(), | |
| 980 | 1113 | kind: InstKind::ICmp(CmpOp::Le, iv, hi), |
| 981 | 1114 | }); |
| 982 | 1115 | f.block_mut(header_id).terminator = Some(Terminator::CondBranch { |
| 983 | 1116 | cond: cmp, |
| 984 | - true_dest: latch_id, true_args: vec![], | |
| 985 | - false_dest: exit_id, false_args: vec![acc], | |
| 1117 | + true_dest: latch_id, | |
| 1118 | + true_args: vec![], | |
| 1119 | + false_dest: exit_id, | |
| 1120 | + false_args: vec![acc], | |
| 986 | 1121 | }); |
| 987 | 1122 | |
| 988 | 1123 | // Latch: acc_next = acc + iv; i_next = iv + 1; br header(i_next, acc_next) |
| 989 | 1124 | let one = f.next_value_id(); |
| 990 | 1125 | f.block_mut(latch_id).insts.push(Inst { |
| 991 | - id: one, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1126 | + id: one, | |
| 1127 | + ty: IrType::Int(IntWidth::I64), | |
| 1128 | + span: span(), | |
| 992 | 1129 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 993 | 1130 | }); |
| 994 | 1131 | let acc_next = f.next_value_id(); |
| 995 | 1132 | f.block_mut(latch_id).insts.push(Inst { |
| 996 | - id: acc_next, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1133 | + id: acc_next, | |
| 1134 | + ty: IrType::Int(IntWidth::I64), | |
| 1135 | + span: span(), | |
| 997 | 1136 | kind: InstKind::IAdd(acc, iv), |
| 998 | 1137 | }); |
| 999 | 1138 | let i_next = f.next_value_id(); |
| 1000 | 1139 | f.block_mut(latch_id).insts.push(Inst { |
| 1001 | - id: i_next, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1140 | + id: i_next, | |
| 1141 | + ty: IrType::Int(IntWidth::I64), | |
| 1142 | + span: span(), | |
| 1002 | 1143 | kind: InstKind::IAdd(iv, one), |
| 1003 | 1144 | }); |
| 1004 | 1145 | f.block_mut(latch_id).terminator = |
@@ -1006,14 +1147,20 @@ mod tests { | ||
| 1006 | 1147 | |
| 1007 | 1148 | // Exit: acc param, return |
| 1008 | 1149 | let acc_out = f.next_value_id(); |
| 1009 | - f.block_mut(exit_id).params.push(BlockParam { id: acc_out, ty: IrType::Int(IntWidth::I64) }); | |
| 1150 | + f.block_mut(exit_id).params.push(BlockParam { | |
| 1151 | + id: acc_out, | |
| 1152 | + ty: IrType::Int(IntWidth::I64), | |
| 1153 | + }); | |
| 1010 | 1154 | f.block_mut(exit_id).terminator = Some(Terminator::Return(None)); |
| 1011 | 1155 | |
| 1012 | 1156 | m.add_function(f); |
| 1013 | 1157 | |
| 1014 | 1158 | let pass = LoopUnroll; |
| 1015 | 1159 | let changed = pass.run(&mut m); |
| 1016 | - assert!(!changed, "reduction loop with 2 header params should not be unrolled"); | |
| 1160 | + assert!( | |
| 1161 | + !changed, | |
| 1162 | + "reduction loop with 2 header params should not be unrolled" | |
| 1163 | + ); | |
| 1017 | 1164 | } |
| 1018 | 1165 | |
| 1019 | 1166 | #[test] |
@@ -1185,56 +1332,67 @@ mod tests { | ||
| 1185 | 1332 | let mut m = Module::new("test".into()); |
| 1186 | 1333 | let mut f = Function::new("escape_test".into(), vec![], IrType::Void); |
| 1187 | 1334 | |
| 1188 | - let header_id = f.create_block("header"); | |
| 1189 | - let latch_id = f.create_block("latch"); | |
| 1190 | - let exit_id = f.create_block("exit"); | |
| 1335 | + let header_id = f.create_block("header"); | |
| 1336 | + let latch_id = f.create_block("latch"); | |
| 1337 | + let exit_id = f.create_block("exit"); | |
| 1191 | 1338 | let outer_latch = f.create_block("outer_latch"); |
| 1192 | - let outer_dest = f.create_block("outer_dest"); | |
| 1193 | - let entry_id = f.entry; | |
| 1339 | + let outer_dest = f.create_block("outer_dest"); | |
| 1340 | + let entry_id = f.entry; | |
| 1194 | 1341 | |
| 1195 | 1342 | // Entry: const 1; br header(1) |
| 1196 | 1343 | let lo = f.next_value_id(); |
| 1197 | 1344 | f.block_mut(entry_id).insts.push(Inst { |
| 1198 | - id: lo, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1345 | + id: lo, | |
| 1346 | + ty: IrType::Int(IntWidth::I64), | |
| 1347 | + span: span(), | |
| 1199 | 1348 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 1200 | 1349 | }); |
| 1201 | - f.block_mut(entry_id).terminator = | |
| 1202 | - Some(Terminator::Branch(header_id, vec![lo])); | |
| 1350 | + f.block_mut(entry_id).terminator = Some(Terminator::Branch(header_id, vec![lo])); | |
| 1203 | 1351 | |
| 1204 | 1352 | // Header: block param %iv; icmp %iv ≤ 3; condBr latch, exit |
| 1205 | 1353 | let iv = f.next_value_id(); |
| 1206 | 1354 | f.block_mut(header_id).params.push(BlockParam { |
| 1207 | - id: iv, ty: IrType::Int(IntWidth::I64), | |
| 1355 | + id: iv, | |
| 1356 | + ty: IrType::Int(IntWidth::I64), | |
| 1208 | 1357 | }); |
| 1209 | 1358 | let hi_c = f.next_value_id(); |
| 1210 | 1359 | f.block_mut(header_id).insts.push(Inst { |
| 1211 | - id: hi_c, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1360 | + id: hi_c, | |
| 1361 | + ty: IrType::Int(IntWidth::I64), | |
| 1362 | + span: span(), | |
| 1212 | 1363 | kind: InstKind::ConstInt(3, IntWidth::I64), |
| 1213 | 1364 | }); |
| 1214 | 1365 | let cmp = f.next_value_id(); |
| 1215 | 1366 | f.block_mut(header_id).insts.push(Inst { |
| 1216 | - id: cmp, ty: IrType::Bool, span: span(), | |
| 1367 | + id: cmp, | |
| 1368 | + ty: IrType::Bool, | |
| 1369 | + span: span(), | |
| 1217 | 1370 | kind: InstKind::ICmp(CmpOp::Le, iv, hi_c), |
| 1218 | 1371 | }); |
| 1219 | 1372 | f.block_mut(header_id).terminator = Some(Terminator::CondBranch { |
| 1220 | 1373 | cond: cmp, |
| 1221 | - true_dest: latch_id, true_args: vec![], | |
| 1222 | - false_dest: exit_id, false_args: vec![], | |
| 1374 | + true_dest: latch_id, | |
| 1375 | + true_args: vec![], | |
| 1376 | + false_dest: exit_id, | |
| 1377 | + false_args: vec![], | |
| 1223 | 1378 | }); |
| 1224 | 1379 | |
| 1225 | 1380 | // Latch: %next = iadd %iv, 1; br header(%next) |
| 1226 | 1381 | let one = f.next_value_id(); |
| 1227 | 1382 | f.block_mut(latch_id).insts.push(Inst { |
| 1228 | - id: one, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1383 | + id: one, | |
| 1384 | + ty: IrType::Int(IntWidth::I64), | |
| 1385 | + span: span(), | |
| 1229 | 1386 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 1230 | 1387 | }); |
| 1231 | 1388 | let nxt = f.next_value_id(); |
| 1232 | 1389 | f.block_mut(latch_id).insts.push(Inst { |
| 1233 | - id: nxt, ty: IrType::Int(IntWidth::I64), span: span(), | |
| 1390 | + id: nxt, | |
| 1391 | + ty: IrType::Int(IntWidth::I64), | |
| 1392 | + span: span(), | |
| 1234 | 1393 | kind: InstKind::IAdd(iv, one), |
| 1235 | 1394 | }); |
| 1236 | - f.block_mut(latch_id).terminator = | |
| 1237 | - Some(Terminator::Branch(header_id, vec![nxt])); | |
| 1395 | + f.block_mut(latch_id).terminator = Some(Terminator::Branch(header_id, vec![nxt])); | |
| 1238 | 1396 | |
| 1239 | 1397 | // Exit: ret void |
| 1240 | 1398 | f.block_mut(exit_id).terminator = Some(Terminator::Return(None)); |
@@ -1242,13 +1400,13 @@ mod tests { | ||
| 1242 | 1400 | // outer_latch: br outer_dest(%iv) |
| 1243 | 1401 | // Simulates an outer-loop latch that mem2reg threaded %iv through. |
| 1244 | 1402 | // %iv is the header's block param — it escapes into this external block. |
| 1245 | - f.block_mut(outer_latch).terminator = | |
| 1246 | - Some(Terminator::Branch(outer_dest, vec![iv])); | |
| 1403 | + f.block_mut(outer_latch).terminator = Some(Terminator::Branch(outer_dest, vec![iv])); | |
| 1247 | 1404 | |
| 1248 | 1405 | // outer_dest(%x): ret void |
| 1249 | 1406 | let x = f.next_value_id(); |
| 1250 | 1407 | f.block_mut(outer_dest).params.push(BlockParam { |
| 1251 | - id: x, ty: IrType::Int(IntWidth::I64), | |
| 1408 | + id: x, | |
| 1409 | + ty: IrType::Int(IntWidth::I64), | |
| 1252 | 1410 | }); |
| 1253 | 1411 | f.block_mut(outer_dest).terminator = Some(Terminator::Return(None)); |
| 1254 | 1412 | |
src/opt/unswitch.rsmodified@@ -26,11 +26,11 @@ | ||
| 26 | 26 | //! Gated on body size (≤ UNSWITCH_MAX_BODY instructions) to prevent |
| 27 | 27 | //! code bloat. Fires at O2+. |
| 28 | 28 | |
| 29 | -use std::collections::{HashMap, HashSet}; | |
| 30 | -use crate::ir::inst::*; | |
| 31 | -use crate::ir::walk::{find_natural_loops, predecessors, prune_unreachable}; | |
| 32 | 29 | use super::loop_utils::{find_preheader, loop_defined_values}; |
| 33 | 30 | use super::pass::Pass; |
| 31 | +use crate::ir::inst::*; | |
| 32 | +use crate::ir::walk::{find_natural_loops, predecessors, prune_unreachable}; | |
| 33 | +use std::collections::{HashMap, HashSet}; | |
| 34 | 34 | |
| 35 | 35 | /// Maximum number of instructions in the loop body to consider for |
| 36 | 36 | /// unswitching. Unswitching doubles the code, so keep this tight. |
@@ -38,15 +38,26 @@ const UNSWITCH_MAX_BODY: usize = 50; | ||
| 38 | 38 | |
| 39 | 39 | pub struct LoopUnswitch; |
| 40 | 40 | |
| 41 | -type UnswitchCandidate = (BlockId, ValueId, BlockId, Vec<ValueId>, BlockId, Vec<ValueId>); | |
| 41 | +type UnswitchCandidate = ( | |
| 42 | + BlockId, | |
| 43 | + ValueId, | |
| 44 | + BlockId, | |
| 45 | + Vec<ValueId>, | |
| 46 | + BlockId, | |
| 47 | + Vec<ValueId>, | |
| 48 | +); | |
| 42 | 49 | |
| 43 | 50 | impl Pass for LoopUnswitch { |
| 44 | - fn name(&self) -> &'static str { "loop-unswitch" } | |
| 51 | + fn name(&self) -> &'static str { | |
| 52 | + "loop-unswitch" | |
| 53 | + } | |
| 45 | 54 | |
| 46 | 55 | fn run(&self, module: &mut Module) -> bool { |
| 47 | 56 | let mut changed = false; |
| 48 | 57 | for func in &mut module.functions { |
| 49 | - if unswitch_in_function(func) { changed = true; } | |
| 58 | + if unswitch_in_function(func) { | |
| 59 | + changed = true; | |
| 60 | + } | |
| 50 | 61 | } |
| 51 | 62 | changed |
| 52 | 63 | } |
@@ -60,19 +71,22 @@ fn unswitch_in_function(func: &mut Function) -> bool { | ||
| 60 | 71 | let preds = predecessors(func); |
| 61 | 72 | |
| 62 | 73 | for lp in &loops { |
| 63 | - let Some(ph_id) = find_preheader(func, lp, &preds) else { continue }; | |
| 74 | + let Some(ph_id) = find_preheader(func, lp, &preds) else { | |
| 75 | + continue; | |
| 76 | + }; | |
| 64 | 77 | |
| 65 | 78 | // Size guard. |
| 66 | - let total_insts: usize = lp.body.iter() | |
| 67 | - .map(|&b| func.block(b).insts.len()) | |
| 68 | - .sum(); | |
| 69 | - if total_insts > UNSWITCH_MAX_BODY { continue; } | |
| 79 | + let total_insts: usize = lp.body.iter().map(|&b| func.block(b).insts.len()).sum(); | |
| 80 | + if total_insts > UNSWITCH_MAX_BODY { | |
| 81 | + continue; | |
| 82 | + } | |
| 70 | 83 | |
| 71 | 84 | // Find a CondBranch inside the loop whose condition is invariant. |
| 72 | 85 | let loop_defs = loop_defined_values(func, lp); |
| 73 | 86 | |
| 74 | 87 | let candidate = find_unswitch_candidate(func, lp, &loop_defs); |
| 75 | - let Some((cond_block, cond_val, true_dest, true_args, false_dest, false_args)) = candidate else { | |
| 88 | + let Some((cond_block, cond_val, true_dest, true_args, false_dest, false_args)) = candidate | |
| 89 | + else { | |
| 76 | 90 | continue; |
| 77 | 91 | }; |
| 78 | 92 | |
@@ -91,7 +105,8 @@ fn unswitch_in_function(func: &mut Function) -> bool { | ||
| 91 | 105 | let true_cond_block = true_map[&cond_block]; |
| 92 | 106 | let true_true_dest = true_map[&true_dest]; |
| 93 | 107 | let true_val_map = build_value_map(func, lp, &true_blocks, &true_map); |
| 94 | - let remapped_true_args: Vec<ValueId> = true_args.iter() | |
| 108 | + let remapped_true_args: Vec<ValueId> = true_args | |
| 109 | + .iter() | |
| 95 | 110 | .map(|v| *true_val_map.get(v).unwrap_or(v)) |
| 96 | 111 | .collect(); |
| 97 | 112 | func.block_mut(true_cond_block).terminator = |
@@ -102,7 +117,8 @@ fn unswitch_in_function(func: &mut Function) -> bool { | ||
| 102 | 117 | let false_cond_block = false_map[&cond_block]; |
| 103 | 118 | let false_false_dest = false_map[&false_dest]; |
| 104 | 119 | let false_val_map = build_value_map(func, lp, &false_blocks, &false_map); |
| 105 | - let remapped_false_args: Vec<ValueId> = false_args.iter() | |
| 120 | + let remapped_false_args: Vec<ValueId> = false_args | |
| 121 | + .iter() | |
| 106 | 122 | .map(|v| *false_val_map.get(v).unwrap_or(v)) |
| 107 | 123 | .collect(); |
| 108 | 124 | func.block_mut(false_cond_block).terminator = |
@@ -147,14 +163,25 @@ fn find_unswitch_candidate( | ||
| 147 | 163 | for &bid in &lp.body { |
| 148 | 164 | let block = func.block(bid); |
| 149 | 165 | if let Some(Terminator::CondBranch { |
| 150 | - cond, true_dest, true_args, false_dest, false_args, | |
| 151 | - }) = &block.terminator { | |
| 166 | + cond, | |
| 167 | + true_dest, | |
| 168 | + true_args, | |
| 169 | + false_dest, | |
| 170 | + false_args, | |
| 171 | + }) = &block.terminator | |
| 172 | + { | |
| 152 | 173 | // Condition must be loop-invariant (not defined in the loop). |
| 153 | 174 | if !loop_defs.contains(cond) { |
| 154 | 175 | // Both targets must be in the loop (not the loop exit). |
| 155 | 176 | if lp.body.contains(true_dest) && lp.body.contains(false_dest) { |
| 156 | - return Some((bid, *cond, *true_dest, true_args.clone(), | |
| 157 | - *false_dest, false_args.clone())); | |
| 177 | + return Some(( | |
| 178 | + bid, | |
| 179 | + *cond, | |
| 180 | + *true_dest, | |
| 181 | + true_args.clone(), | |
| 182 | + *false_dest, | |
| 183 | + false_args.clone(), | |
| 184 | + )); | |
| 158 | 185 | } |
| 159 | 186 | } |
| 160 | 187 | } |
@@ -190,13 +217,17 @@ fn build_value_map( | ||
| 190 | 217 | #[cfg(test)] |
| 191 | 218 | mod tests { |
| 192 | 219 | use super::*; |
| 193 | - use crate::ir::types::{IrType, IntWidth}; | |
| 220 | + use crate::ir::types::{IntWidth, IrType}; | |
| 221 | + use crate::lexer::{Position, Span}; | |
| 194 | 222 | use crate::opt::pass::Pass; |
| 195 | - use crate::lexer::{Span, Position}; | |
| 196 | 223 | |
| 197 | 224 | fn span() -> Span { |
| 198 | 225 | let pos = Position { line: 0, col: 0 }; |
| 199 | - Span { file_id: 0, start: pos, end: pos } | |
| 226 | + Span { | |
| 227 | + file_id: 0, | |
| 228 | + start: pos, | |
| 229 | + end: pos, | |
| 230 | + } | |
| 200 | 231 | } |
| 201 | 232 | |
| 202 | 233 | /// Build: entry → preheader → header(%i) → cmp → body → cond_branch(flag, t_body, f_body) → |
@@ -208,32 +239,38 @@ mod tests { | ||
| 208 | 239 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 209 | 240 | |
| 210 | 241 | let preheader = f.create_block("preheader"); |
| 211 | - let header = f.create_block("header"); | |
| 212 | - let cmp_blk = f.create_block("cmp"); | |
| 213 | - let body = f.create_block("body"); | |
| 214 | - let t_body = f.create_block("t_body"); | |
| 215 | - let f_body = f.create_block("f_body"); | |
| 216 | - let latch = f.create_block("latch"); | |
| 217 | - let exit = f.create_block("exit"); | |
| 218 | - let entry = f.entry; | |
| 242 | + let header = f.create_block("header"); | |
| 243 | + let cmp_blk = f.create_block("cmp"); | |
| 244 | + let body = f.create_block("body"); | |
| 245 | + let t_body = f.create_block("t_body"); | |
| 246 | + let f_body = f.create_block("f_body"); | |
| 247 | + let latch = f.create_block("latch"); | |
| 248 | + let exit = f.create_block("exit"); | |
| 249 | + let entry = f.entry; | |
| 219 | 250 | |
| 220 | 251 | // Entry: flag = const_bool(true), c1 = const 1, c10 = const 10 |
| 221 | 252 | let flag = f.next_value_id(); |
| 222 | 253 | f.register_type(flag, IrType::Bool); |
| 223 | 254 | f.block_mut(entry).insts.push(Inst { |
| 224 | - id: flag, ty: IrType::Bool, span: span(), | |
| 255 | + id: flag, | |
| 256 | + ty: IrType::Bool, | |
| 257 | + span: span(), | |
| 225 | 258 | kind: InstKind::ConstBool(true), |
| 226 | 259 | }); |
| 227 | 260 | let c1 = f.next_value_id(); |
| 228 | 261 | f.register_type(c1, IrType::Int(IntWidth::I32)); |
| 229 | 262 | f.block_mut(entry).insts.push(Inst { |
| 230 | - id: c1, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 263 | + id: c1, | |
| 264 | + ty: IrType::Int(IntWidth::I32), | |
| 265 | + span: span(), | |
| 231 | 266 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 232 | 267 | }); |
| 233 | 268 | let c10 = f.next_value_id(); |
| 234 | 269 | f.register_type(c10, IrType::Int(IntWidth::I32)); |
| 235 | 270 | f.block_mut(entry).insts.push(Inst { |
| 236 | - id: c10, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 271 | + id: c10, | |
| 272 | + ty: IrType::Int(IntWidth::I32), | |
| 273 | + span: span(), | |
| 237 | 274 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 238 | 275 | }); |
| 239 | 276 | f.block_mut(entry).terminator = Some(Terminator::Branch(preheader, vec![])); |
@@ -244,27 +281,36 @@ mod tests { | ||
| 244 | 281 | // Header(%i) → cmp |
| 245 | 282 | let iv = f.next_value_id(); |
| 246 | 283 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 247 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 284 | + f.block_mut(header).params.push(BlockParam { | |
| 285 | + id: iv, | |
| 286 | + ty: IrType::Int(IntWidth::I32), | |
| 287 | + }); | |
| 248 | 288 | f.block_mut(header).terminator = Some(Terminator::Branch(cmp_blk, vec![])); |
| 249 | 289 | |
| 250 | 290 | // Cmp: icmp le %i, 10; condBr body, exit |
| 251 | 291 | let cmp_v = f.next_value_id(); |
| 252 | 292 | f.register_type(cmp_v, IrType::Bool); |
| 253 | 293 | f.block_mut(cmp_blk).insts.push(Inst { |
| 254 | - id: cmp_v, ty: IrType::Bool, span: span(), | |
| 294 | + id: cmp_v, | |
| 295 | + ty: IrType::Bool, | |
| 296 | + span: span(), | |
| 255 | 297 | kind: InstKind::ICmp(CmpOp::Le, iv, c10), |
| 256 | 298 | }); |
| 257 | 299 | f.block_mut(cmp_blk).terminator = Some(Terminator::CondBranch { |
| 258 | 300 | cond: cmp_v, |
| 259 | - true_dest: body, true_args: vec![], | |
| 260 | - false_dest: exit, false_args: vec![], | |
| 301 | + true_dest: body, | |
| 302 | + true_args: vec![], | |
| 303 | + false_dest: exit, | |
| 304 | + false_args: vec![], | |
| 261 | 305 | }); |
| 262 | 306 | |
| 263 | 307 | // Body: condBr flag, t_body, f_body ← the unswitchable conditional |
| 264 | 308 | f.block_mut(body).terminator = Some(Terminator::CondBranch { |
| 265 | 309 | cond: flag, |
| 266 | - true_dest: t_body, true_args: vec![], | |
| 267 | - false_dest: f_body, false_args: vec![], | |
| 310 | + true_dest: t_body, | |
| 311 | + true_args: vec![], | |
| 312 | + false_dest: f_body, | |
| 313 | + false_args: vec![], | |
| 268 | 314 | }); |
| 269 | 315 | |
| 270 | 316 | // t_body → latch |
@@ -277,7 +323,9 @@ mod tests { | ||
| 277 | 323 | let nxt = f.next_value_id(); |
| 278 | 324 | f.register_type(nxt, IrType::Int(IntWidth::I32)); |
| 279 | 325 | f.block_mut(latch).insts.push(Inst { |
| 280 | - id: nxt, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 326 | + id: nxt, | |
| 327 | + ty: IrType::Int(IntWidth::I32), | |
| 328 | + span: span(), | |
| 281 | 329 | kind: InstKind::IAdd(iv, c1), |
| 282 | 330 | }); |
| 283 | 331 | f.block_mut(latch).terminator = Some(Terminator::Branch(header, vec![nxt])); |
@@ -298,10 +346,15 @@ mod tests { | ||
| 298 | 346 | |
| 299 | 347 | // After unswitching, the preheader should have a CondBranch (not a Branch). |
| 300 | 348 | let f = &m.functions[0]; |
| 301 | - let preheader = f.blocks.iter().find(|b| b.name.contains("preheader")).unwrap(); | |
| 349 | + let preheader = f | |
| 350 | + .blocks | |
| 351 | + .iter() | |
| 352 | + .find(|b| b.name.contains("preheader")) | |
| 353 | + .unwrap(); | |
| 302 | 354 | assert!( |
| 303 | 355 | matches!(&preheader.terminator, Some(Terminator::CondBranch { .. })), |
| 304 | - "preheader should now have a CondBranch: {:?}", preheader.terminator | |
| 356 | + "preheader should now have a CondBranch: {:?}", | |
| 357 | + preheader.terminator | |
| 305 | 358 | ); |
| 306 | 359 | } |
| 307 | 360 | |
@@ -321,49 +374,66 @@ mod tests { | ||
| 321 | 374 | let c1 = f.next_value_id(); |
| 322 | 375 | f.register_type(c1, IrType::Int(IntWidth::I32)); |
| 323 | 376 | f.block_mut(entry).insts.push(Inst { |
| 324 | - id: c1, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 377 | + id: c1, | |
| 378 | + ty: IrType::Int(IntWidth::I32), | |
| 379 | + span: span(), | |
| 325 | 380 | kind: InstKind::ConstInt(1, IntWidth::I32), |
| 326 | 381 | }); |
| 327 | 382 | let c10 = f.next_value_id(); |
| 328 | 383 | f.register_type(c10, IrType::Int(IntWidth::I32)); |
| 329 | 384 | f.block_mut(entry).insts.push(Inst { |
| 330 | - id: c10, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 385 | + id: c10, | |
| 386 | + ty: IrType::Int(IntWidth::I32), | |
| 387 | + span: span(), | |
| 331 | 388 | kind: InstKind::ConstInt(10, IntWidth::I32), |
| 332 | 389 | }); |
| 333 | 390 | f.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![c1])); |
| 334 | 391 | |
| 335 | 392 | let iv = f.next_value_id(); |
| 336 | 393 | f.register_type(iv, IrType::Int(IntWidth::I32)); |
| 337 | - f.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 394 | + f.block_mut(header).params.push(BlockParam { | |
| 395 | + id: iv, | |
| 396 | + ty: IrType::Int(IntWidth::I32), | |
| 397 | + }); | |
| 338 | 398 | let cmp_v = f.next_value_id(); |
| 339 | 399 | f.register_type(cmp_v, IrType::Bool); |
| 340 | 400 | f.block_mut(header).insts.push(Inst { |
| 341 | - id: cmp_v, ty: IrType::Bool, span: span(), | |
| 401 | + id: cmp_v, | |
| 402 | + ty: IrType::Bool, | |
| 403 | + span: span(), | |
| 342 | 404 | kind: InstKind::ICmp(CmpOp::Le, iv, c10), |
| 343 | 405 | }); |
| 344 | 406 | f.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 345 | 407 | cond: cmp_v, |
| 346 | - true_dest: body, true_args: vec![], | |
| 347 | - false_dest: exit, false_args: vec![], | |
| 408 | + true_dest: body, | |
| 409 | + true_args: vec![], | |
| 410 | + false_dest: exit, | |
| 411 | + false_args: vec![], | |
| 348 | 412 | }); |
| 349 | 413 | |
| 350 | 414 | // Body: the "conditional" uses the IV (loop-variant). |
| 351 | 415 | let iv_cmp = f.next_value_id(); |
| 352 | 416 | f.register_type(iv_cmp, IrType::Bool); |
| 353 | 417 | f.block_mut(body).insts.push(Inst { |
| 354 | - id: iv_cmp, ty: IrType::Bool, span: span(), | |
| 418 | + id: iv_cmp, | |
| 419 | + ty: IrType::Bool, | |
| 420 | + span: span(), | |
| 355 | 421 | kind: InstKind::ICmp(CmpOp::Le, iv, c1), |
| 356 | 422 | }); |
| 357 | 423 | f.block_mut(body).terminator = Some(Terminator::CondBranch { |
| 358 | 424 | cond: iv_cmp, |
| 359 | - true_dest: latch, true_args: vec![], | |
| 360 | - false_dest: latch, false_args: vec![], | |
| 425 | + true_dest: latch, | |
| 426 | + true_args: vec![], | |
| 427 | + false_dest: latch, | |
| 428 | + false_args: vec![], | |
| 361 | 429 | }); |
| 362 | 430 | |
| 363 | 431 | let nxt = f.next_value_id(); |
| 364 | 432 | f.register_type(nxt, IrType::Int(IntWidth::I32)); |
| 365 | 433 | f.block_mut(latch).insts.push(Inst { |
| 366 | - id: nxt, ty: IrType::Int(IntWidth::I32), span: span(), | |
| 434 | + id: nxt, | |
| 435 | + ty: IrType::Int(IntWidth::I32), | |
| 436 | + span: span(), | |
| 367 | 437 | kind: InstKind::IAdd(iv, c1), |
| 368 | 438 | }); |
| 369 | 439 | f.block_mut(latch).terminator = Some(Terminator::Branch(header, vec![nxt])); |
src/opt/util.rsmodified@@ -14,18 +14,8 @@ | ||
| 14 | 14 | // flagged it as dead code from this module's perspective. |
| 15 | 15 | #[allow(unused_imports)] |
| 16 | 16 | pub use crate::ir::walk::{ |
| 17 | - inst_uses, | |
| 18 | - terminator_uses, | |
| 19 | - terminator_targets, | |
| 20 | - for_each_operand_mut, | |
| 21 | - for_each_terminator_operand_mut, | |
| 22 | - substitute_uses, | |
| 23 | - predecessors, | |
| 24 | - compute_dominators, | |
| 25 | - compute_immediate_dominators, | |
| 26 | - dominator_tree_children, | |
| 27 | - compute_dominance_frontiers, | |
| 28 | - find_natural_loops, | |
| 29 | - NaturalLoop, | |
| 30 | - prune_unreachable, | |
| 17 | + compute_dominance_frontiers, compute_dominators, compute_immediate_dominators, | |
| 18 | + dominator_tree_children, find_natural_loops, for_each_operand_mut, | |
| 19 | + for_each_terminator_operand_mut, inst_uses, predecessors, prune_unreachable, substitute_uses, | |
| 20 | + terminator_targets, terminator_uses, NaturalLoop, | |
| 31 | 21 | }; |
src/opt/vectorize.rsmodified@@ -20,7 +20,9 @@ use super::util::{find_natural_loops, inst_uses, predecessors, terminator_uses, | ||
| 20 | 20 | pub struct Vectorize; |
| 21 | 21 | |
| 22 | 22 | impl Pass for Vectorize { |
| 23 | - fn name(&self) -> &'static str { "vectorize" } | |
| 23 | + fn name(&self) -> &'static str { | |
| 24 | + "vectorize" | |
| 25 | + } | |
| 24 | 26 | |
| 25 | 27 | fn run(&self, module: &mut Module) -> bool { |
| 26 | 28 | let mut changed = false; |
@@ -164,14 +166,20 @@ fn detect_counted_loop( | ||
| 164 | 166 | }) => (*cond, *true_dest, *false_dest, true_args, false_args), |
| 165 | 167 | _ => return None, |
| 166 | 168 | }; |
| 167 | - if !true_args.is_empty() || !false_args.is_empty() || true_dest != body || lp.body.contains(&false_dest) { | |
| 169 | + if !true_args.is_empty() | |
| 170 | + || !false_args.is_empty() | |
| 171 | + || true_dest != body | |
| 172 | + || lp.body.contains(&false_dest) | |
| 173 | + { | |
| 168 | 174 | return None; |
| 169 | 175 | } |
| 170 | 176 | |
| 171 | 177 | let cond_inst = header_block.insts.iter().find(|inst| inst.id == cond_id)?; |
| 172 | 178 | let iv_bound = match cond_inst.kind { |
| 173 | 179 | InstKind::ICmp(CmpOp::Le, lhs, rhs) if lhs == iv_param => resolve_const_int(func, rhs)?, |
| 174 | - InstKind::ICmp(CmpOp::Lt, lhs, rhs) if lhs == iv_param => resolve_const_int(func, rhs)?.checked_sub(1)?, | |
| 180 | + InstKind::ICmp(CmpOp::Lt, lhs, rhs) if lhs == iv_param => { | |
| 181 | + resolve_const_int(func, rhs)?.checked_sub(1)? | |
| 182 | + } | |
| 175 | 183 | _ => return None, |
| 176 | 184 | }; |
| 177 | 185 | |
@@ -231,13 +239,10 @@ fn build_kernel_plan( | ||
| 231 | 239 | return None; |
| 232 | 240 | } |
| 233 | 241 | |
| 234 | - let mut stores = body | |
| 235 | - .insts | |
| 236 | - .iter() | |
| 237 | - .filter_map(|inst| match inst.kind { | |
| 238 | - InstKind::Store(value, ptr) => Some((inst.span, value, ptr)), | |
| 239 | - _ => None, | |
| 240 | - }); | |
| 242 | + let mut stores = body.insts.iter().filter_map(|inst| match inst.kind { | |
| 243 | + InstKind::Store(value, ptr) => Some((inst.span, value, ptr)), | |
| 244 | + _ => None, | |
| 245 | + }); | |
| 241 | 246 | let (span, stored_value, dest_ptr) = stores.next()?; |
| 242 | 247 | if stores.next().is_some() { |
| 243 | 248 | return None; |
@@ -248,7 +253,9 @@ fn build_kernel_plan( | ||
| 248 | 253 | return None; |
| 249 | 254 | } |
| 250 | 255 | |
| 251 | - let plan = if let Some(scalar) = classify_invariant_scalar(func, loop_defs, stored_value, &dest.elem_ty) { | |
| 256 | + let plan = if let Some(scalar) = | |
| 257 | + classify_invariant_scalar(func, loop_defs, stored_value, &dest.elem_ty) | |
| 258 | + { | |
| 252 | 259 | let kernel = fill_kernel_name(&dest.elem_ty)?; |
| 253 | 260 | KernelPlan::Fill { |
| 254 | 261 | kernel, |
@@ -382,7 +389,11 @@ fn classify_invariant_scalar( | ||
| 382 | 389 | (func.value_type(value).as_ref() == Some(elem_ty)).then_some(value) |
| 383 | 390 | } |
| 384 | 391 | |
| 385 | -fn classify_loaded_array(func: &Function, value: ValueId, iv_param: ValueId) -> Option<ArrayAccess> { | |
| 392 | +fn classify_loaded_array( | |
| 393 | + func: &Function, | |
| 394 | + value: ValueId, | |
| 395 | + iv_param: ValueId, | |
| 396 | +) -> Option<ArrayAccess> { | |
| 386 | 397 | let defs = inst_map(func); |
| 387 | 398 | let inst = defs.get(&value)?; |
| 388 | 399 | let InstKind::Load(ptr) = inst.kind else { |
@@ -442,18 +453,18 @@ fn loop_values_escape(func: &Function, lp: &NaturalLoop, loop_defs: &HashSet<Val | ||
| 442 | 453 | if lp.body.contains(&block.id) { |
| 443 | 454 | continue; |
| 444 | 455 | } |
| 445 | - if block | |
| 446 | - .insts | |
| 447 | - .iter() | |
| 448 | - .any(|inst| inst_uses(&inst.kind).into_iter().any(|value| loop_defs.contains(&value))) | |
| 449 | - { | |
| 456 | + if block.insts.iter().any(|inst| { | |
| 457 | + inst_uses(&inst.kind) | |
| 458 | + .into_iter() | |
| 459 | + .any(|value| loop_defs.contains(&value)) | |
| 460 | + }) { | |
| 450 | 461 | return true; |
| 451 | 462 | } |
| 452 | - if block | |
| 453 | - .terminator | |
| 454 | - .as_ref() | |
| 455 | - .is_some_and(|term| terminator_uses(term).into_iter().any(|value| loop_defs.contains(&value))) | |
| 456 | - { | |
| 463 | + if block.terminator.as_ref().is_some_and(|term| { | |
| 464 | + terminator_uses(term) | |
| 465 | + .into_iter() | |
| 466 | + .any(|value| loop_defs.contains(&value)) | |
| 467 | + }) { | |
| 457 | 468 | return true; |
| 458 | 469 | } |
| 459 | 470 | } |
@@ -468,14 +479,33 @@ fn apply_kernel_plan( | ||
| 468 | 479 | ) { |
| 469 | 480 | let n_id = ensure_i64_const(func, shape.preheader, kernel_len(plan) as i64, span); |
| 470 | 481 | let (kernel, args) = match plan { |
| 471 | - KernelPlan::Fill { kernel, dest, scalar, .. } => (kernel, vec![dest, n_id, scalar]), | |
| 472 | - KernelPlan::ArrayBinary { kernel, dest, lhs, rhs, .. } => (kernel, vec![dest, lhs, rhs, n_id]), | |
| 473 | - KernelPlan::ArrayScalar { kernel, dest, src, scalar, .. } => { | |
| 474 | - (kernel, vec![dest, src, scalar, n_id]) | |
| 475 | - } | |
| 476 | - KernelPlan::ScalarArray { kernel, dest, scalar, src, .. } => { | |
| 477 | - (kernel, vec![dest, scalar, src, n_id]) | |
| 478 | - } | |
| 482 | + KernelPlan::Fill { | |
| 483 | + kernel, | |
| 484 | + dest, | |
| 485 | + scalar, | |
| 486 | + .. | |
| 487 | + } => (kernel, vec![dest, n_id, scalar]), | |
| 488 | + KernelPlan::ArrayBinary { | |
| 489 | + kernel, | |
| 490 | + dest, | |
| 491 | + lhs, | |
| 492 | + rhs, | |
| 493 | + .. | |
| 494 | + } => (kernel, vec![dest, lhs, rhs, n_id]), | |
| 495 | + KernelPlan::ArrayScalar { | |
| 496 | + kernel, | |
| 497 | + dest, | |
| 498 | + src, | |
| 499 | + scalar, | |
| 500 | + .. | |
| 501 | + } => (kernel, vec![dest, src, scalar, n_id]), | |
| 502 | + KernelPlan::ScalarArray { | |
| 503 | + kernel, | |
| 504 | + dest, | |
| 505 | + scalar, | |
| 506 | + src, | |
| 507 | + .. | |
| 508 | + } => (kernel, vec![dest, scalar, src, n_id]), | |
| 479 | 509 | }; |
| 480 | 510 | |
| 481 | 511 | let id = func.next_value_id(); |
@@ -579,18 +609,27 @@ fn scalar_array_kernel_name(kind: BinaryKind, ty: &IrType) -> Option<&'static st | ||
| 579 | 609 | mod tests { |
| 580 | 610 | use super::*; |
| 581 | 611 | use crate::ir::types::IrType; |
| 582 | - use crate::opt::pass::Pass; | |
| 583 | 612 | use crate::lexer::{Position, Span}; |
| 613 | + use crate::opt::pass::Pass; | |
| 584 | 614 | |
| 585 | 615 | fn dummy_span() -> Span { |
| 586 | 616 | let p = Position { line: 0, col: 0 }; |
| 587 | - Span { file_id: 0, start: p, end: p } | |
| 617 | + Span { | |
| 618 | + file_id: 0, | |
| 619 | + start: p, | |
| 620 | + end: p, | |
| 621 | + } | |
| 588 | 622 | } |
| 589 | 623 | |
| 590 | 624 | fn push_inst(func: &mut Function, block: BlockId, kind: InstKind, ty: IrType) -> ValueId { |
| 591 | 625 | let id = func.next_value_id(); |
| 592 | 626 | func.register_type(id, ty.clone()); |
| 593 | - func.block_mut(block).insts.push(Inst { id, kind, ty, span: dummy_span() }); | |
| 627 | + func.block_mut(block).insts.push(Inst { | |
| 628 | + id, | |
| 629 | + kind, | |
| 630 | + ty, | |
| 631 | + span: dummy_span(), | |
| 632 | + }); | |
| 594 | 633 | id |
| 595 | 634 | } |
| 596 | 635 | |
@@ -608,29 +647,61 @@ mod tests { | ||
| 608 | 647 | &mut func, |
| 609 | 648 | entry, |
| 610 | 649 | InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32)), |
| 611 | - IrType::Ptr(Box::new(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32))), | |
| 650 | + IrType::Ptr(Box::new(IrType::Array( | |
| 651 | + Box::new(IrType::Int(IntWidth::I32)), | |
| 652 | + 32, | |
| 653 | + ))), | |
| 612 | 654 | ); |
| 613 | 655 | let b = push_inst( |
| 614 | 656 | &mut func, |
| 615 | 657 | entry, |
| 616 | 658 | InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32)), |
| 617 | - IrType::Ptr(Box::new(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32))), | |
| 659 | + IrType::Ptr(Box::new(IrType::Array( | |
| 660 | + Box::new(IrType::Int(IntWidth::I32)), | |
| 661 | + 32, | |
| 662 | + ))), | |
| 618 | 663 | ); |
| 619 | 664 | let c = push_inst( |
| 620 | 665 | &mut func, |
| 621 | 666 | entry, |
| 622 | 667 | InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32)), |
| 623 | - IrType::Ptr(Box::new(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 32))), | |
| 668 | + IrType::Ptr(Box::new(IrType::Array( | |
| 669 | + Box::new(IrType::Int(IntWidth::I32)), | |
| 670 | + 32, | |
| 671 | + ))), | |
| 672 | + ); | |
| 673 | + let one_i32 = push_inst( | |
| 674 | + &mut func, | |
| 675 | + entry, | |
| 676 | + InstKind::ConstInt(1, IntWidth::I32), | |
| 677 | + IrType::Int(IntWidth::I32), | |
| 678 | + ); | |
| 679 | + let hi_i32 = push_inst( | |
| 680 | + &mut func, | |
| 681 | + entry, | |
| 682 | + InstKind::ConstInt(32, IntWidth::I32), | |
| 683 | + IrType::Int(IntWidth::I32), | |
| 684 | + ); | |
| 685 | + let one_i64 = push_inst( | |
| 686 | + &mut func, | |
| 687 | + entry, | |
| 688 | + InstKind::ConstInt(1, IntWidth::I64), | |
| 689 | + IrType::Int(IntWidth::I64), | |
| 624 | 690 | ); |
| 625 | - let one_i32 = push_inst(&mut func, entry, InstKind::ConstInt(1, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 626 | - let hi_i32 = push_inst(&mut func, entry, InstKind::ConstInt(32, IntWidth::I32), IrType::Int(IntWidth::I32)); | |
| 627 | - let one_i64 = push_inst(&mut func, entry, InstKind::ConstInt(1, IntWidth::I64), IrType::Int(IntWidth::I64)); | |
| 628 | 691 | func.block_mut(entry).terminator = Some(Terminator::Branch(header, vec![one_i32])); |
| 629 | 692 | |
| 630 | 693 | let iv = func.next_value_id(); |
| 631 | 694 | func.register_type(iv, IrType::Int(IntWidth::I32)); |
| 632 | - func.block_mut(header).params.push(BlockParam { id: iv, ty: IrType::Int(IntWidth::I32) }); | |
| 633 | - let cmp = push_inst(&mut func, header, InstKind::ICmp(CmpOp::Le, iv, hi_i32), IrType::Bool); | |
| 695 | + func.block_mut(header).params.push(BlockParam { | |
| 696 | + id: iv, | |
| 697 | + ty: IrType::Int(IntWidth::I32), | |
| 698 | + }); | |
| 699 | + let cmp = push_inst( | |
| 700 | + &mut func, | |
| 701 | + header, | |
| 702 | + InstKind::ICmp(CmpOp::Le, iv, hi_i32), | |
| 703 | + IrType::Bool, | |
| 704 | + ); | |
| 634 | 705 | func.block_mut(header).terminator = Some(Terminator::CondBranch { |
| 635 | 706 | cond: cmp, |
| 636 | 707 | true_dest: body, |
@@ -639,23 +710,71 @@ mod tests { | ||
| 639 | 710 | false_args: vec![], |
| 640 | 711 | }); |
| 641 | 712 | |
| 642 | - let idx64 = push_inst(&mut func, body, InstKind::IntExtend(iv, IntWidth::I64, true), IrType::Int(IntWidth::I64)); | |
| 643 | - let offset = push_inst(&mut func, body, InstKind::ISub(idx64, one_i64), IrType::Int(IntWidth::I64)); | |
| 644 | - let a_ptr = push_inst(&mut func, body, InstKind::GetElementPtr(a, vec![offset]), IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 645 | - let a_val = push_inst(&mut func, body, InstKind::Load(a_ptr), IrType::Int(IntWidth::I32)); | |
| 646 | - let b_ptr = push_inst(&mut func, body, InstKind::GetElementPtr(b, vec![offset]), IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 647 | - let b_val = push_inst(&mut func, body, InstKind::Load(b_ptr), IrType::Int(IntWidth::I32)); | |
| 648 | - let sum = push_inst(&mut func, body, InstKind::IAdd(a_val, b_val), IrType::Int(IntWidth::I32)); | |
| 649 | - let c_ptr = push_inst(&mut func, body, InstKind::GetElementPtr(c, vec![offset]), IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); | |
| 713 | + let idx64 = push_inst( | |
| 714 | + &mut func, | |
| 715 | + body, | |
| 716 | + InstKind::IntExtend(iv, IntWidth::I64, true), | |
| 717 | + IrType::Int(IntWidth::I64), | |
| 718 | + ); | |
| 719 | + let offset = push_inst( | |
| 720 | + &mut func, | |
| 721 | + body, | |
| 722 | + InstKind::ISub(idx64, one_i64), | |
| 723 | + IrType::Int(IntWidth::I64), | |
| 724 | + ); | |
| 725 | + let a_ptr = push_inst( | |
| 726 | + &mut func, | |
| 727 | + body, | |
| 728 | + InstKind::GetElementPtr(a, vec![offset]), | |
| 729 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 730 | + ); | |
| 731 | + let a_val = push_inst( | |
| 732 | + &mut func, | |
| 733 | + body, | |
| 734 | + InstKind::Load(a_ptr), | |
| 735 | + IrType::Int(IntWidth::I32), | |
| 736 | + ); | |
| 737 | + let b_ptr = push_inst( | |
| 738 | + &mut func, | |
| 739 | + body, | |
| 740 | + InstKind::GetElementPtr(b, vec![offset]), | |
| 741 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 742 | + ); | |
| 743 | + let b_val = push_inst( | |
| 744 | + &mut func, | |
| 745 | + body, | |
| 746 | + InstKind::Load(b_ptr), | |
| 747 | + IrType::Int(IntWidth::I32), | |
| 748 | + ); | |
| 749 | + let sum = push_inst( | |
| 750 | + &mut func, | |
| 751 | + body, | |
| 752 | + InstKind::IAdd(a_val, b_val), | |
| 753 | + IrType::Int(IntWidth::I32), | |
| 754 | + ); | |
| 755 | + let c_ptr = push_inst( | |
| 756 | + &mut func, | |
| 757 | + body, | |
| 758 | + InstKind::GetElementPtr(c, vec![offset]), | |
| 759 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), | |
| 760 | + ); | |
| 650 | 761 | push_inst(&mut func, body, InstKind::Store(sum, c_ptr), IrType::Void); |
| 651 | - let next = push_inst(&mut func, body, InstKind::IAdd(iv, one_i32), IrType::Int(IntWidth::I32)); | |
| 762 | + let next = push_inst( | |
| 763 | + &mut func, | |
| 764 | + body, | |
| 765 | + InstKind::IAdd(iv, one_i32), | |
| 766 | + IrType::Int(IntWidth::I32), | |
| 767 | + ); | |
| 652 | 768 | func.block_mut(body).terminator = Some(Terminator::Branch(header, vec![next])); |
| 653 | 769 | func.block_mut(exit).terminator = Some(Terminator::Return(None)); |
| 654 | 770 | |
| 655 | 771 | module.add_function(func); |
| 656 | 772 | |
| 657 | 773 | let changed = Vectorize.run(&mut module); |
| 658 | - assert!(changed, "vectorize should rewrite the counted array-add loop"); | |
| 774 | + assert!( | |
| 775 | + changed, | |
| 776 | + "vectorize should rewrite the counted array-add loop" | |
| 777 | + ); | |
| 659 | 778 | |
| 660 | 779 | let func = &module.functions[0]; |
| 661 | 780 | let entry_block = func.block(entry); |
src/parser/decl.rsmodified@@ -3,10 +3,10 @@ | ||
| 3 | 3 | //! Parses type declarations, USE statements, IMPLICIT, derived type |
| 4 | 4 | //! definitions, and legacy declaration forms (COMMON, DATA, etc.). |
| 5 | 5 | |
| 6 | -use crate::ast::Spanned; | |
| 6 | +use super::{ParseError, Parser}; | |
| 7 | 7 | use crate::ast::decl::*; |
| 8 | +use crate::ast::Spanned; | |
| 8 | 9 | use crate::lexer::TokenKind; |
| 9 | -use super::{Parser, ParseError}; | |
| 10 | 10 | |
| 11 | 11 | impl<'a> Parser<'a> { |
| 12 | 12 | // ---- Type specifier parsing ---- |
@@ -15,8 +15,14 @@ impl<'a> Parser<'a> { | ||
| 15 | 15 | pub fn try_parse_type_spec(&mut self) -> Option<Result<TypeSpec, ParseError>> { |
| 16 | 16 | let text = self.peek_text().to_lowercase(); |
| 17 | 17 | match text.as_str() { |
| 18 | - "integer" => { self.advance(); Some(self.parse_kind_selector().map(TypeSpec::Integer)) } | |
| 19 | - "real" => { self.advance(); Some(self.parse_kind_selector().map(TypeSpec::Real)) } | |
| 18 | + "integer" => { | |
| 19 | + self.advance(); | |
| 20 | + Some(self.parse_kind_selector().map(TypeSpec::Integer)) | |
| 21 | + } | |
| 22 | + "real" => { | |
| 23 | + self.advance(); | |
| 24 | + Some(self.parse_kind_selector().map(TypeSpec::Real)) | |
| 25 | + } | |
| 20 | 26 | "doubleprecision" | "double" => { |
| 21 | 27 | self.advance(); |
| 22 | 28 | // Handle "double precision" / "double complex" as two tokens. |
@@ -30,10 +36,22 @@ impl<'a> Parser<'a> { | ||
| 30 | 36 | Some(Ok(TypeSpec::DoublePrecision)) |
| 31 | 37 | } |
| 32 | 38 | } |
| 33 | - "complex" => { self.advance(); Some(self.parse_kind_selector().map(TypeSpec::Complex)) } | |
| 34 | - "doublecomplex" => { self.advance(); Some(Ok(TypeSpec::DoubleComplex)) } | |
| 35 | - "logical" => { self.advance(); Some(self.parse_kind_selector().map(TypeSpec::Logical)) } | |
| 36 | - "character" => { self.advance(); Some(self.parse_char_selector().map(TypeSpec::Character)) } | |
| 39 | + "complex" => { | |
| 40 | + self.advance(); | |
| 41 | + Some(self.parse_kind_selector().map(TypeSpec::Complex)) | |
| 42 | + } | |
| 43 | + "doublecomplex" => { | |
| 44 | + self.advance(); | |
| 45 | + Some(Ok(TypeSpec::DoubleComplex)) | |
| 46 | + } | |
| 47 | + "logical" => { | |
| 48 | + self.advance(); | |
| 49 | + Some(self.parse_kind_selector().map(TypeSpec::Logical)) | |
| 50 | + } | |
| 51 | + "character" => { | |
| 52 | + self.advance(); | |
| 53 | + Some(self.parse_char_selector().map(TypeSpec::Character)) | |
| 54 | + } | |
| 37 | 55 | "type" => { |
| 38 | 56 | // type(name) is a type specifier, but type :: name is a derived type definition. |
| 39 | 57 | // Only consume if followed by (. |
@@ -58,8 +76,14 @@ impl<'a> Parser<'a> { | ||
| 58 | 76 | fn parse_implicit_type_spec(&mut self) -> Option<Result<TypeSpec, ParseError>> { |
| 59 | 77 | let text = self.peek_text().to_lowercase(); |
| 60 | 78 | match text.as_str() { |
| 61 | - "integer" => { self.advance(); Some(Ok(TypeSpec::Integer(None))) } | |
| 62 | - "real" => { self.advance(); Some(Ok(TypeSpec::Real(None))) } | |
| 79 | + "integer" => { | |
| 80 | + self.advance(); | |
| 81 | + Some(Ok(TypeSpec::Integer(None))) | |
| 82 | + } | |
| 83 | + "real" => { | |
| 84 | + self.advance(); | |
| 85 | + Some(Ok(TypeSpec::Real(None))) | |
| 86 | + } | |
| 63 | 87 | "doubleprecision" | "double" => { |
| 64 | 88 | self.advance(); |
| 65 | 89 | if self.peek_text().eq_ignore_ascii_case("precision") { |
@@ -72,9 +96,18 @@ impl<'a> Parser<'a> { | ||
| 72 | 96 | Some(Ok(TypeSpec::DoublePrecision)) |
| 73 | 97 | } |
| 74 | 98 | } |
| 75 | - "complex" => { self.advance(); Some(Ok(TypeSpec::Complex(None))) } | |
| 76 | - "logical" => { self.advance(); Some(Ok(TypeSpec::Logical(None))) } | |
| 77 | - "character" => { self.advance(); Some(Ok(TypeSpec::Character(None))) } | |
| 99 | + "complex" => { | |
| 100 | + self.advance(); | |
| 101 | + Some(Ok(TypeSpec::Complex(None))) | |
| 102 | + } | |
| 103 | + "logical" => { | |
| 104 | + self.advance(); | |
| 105 | + Some(Ok(TypeSpec::Logical(None))) | |
| 106 | + } | |
| 107 | + "character" => { | |
| 108 | + self.advance(); | |
| 109 | + Some(Ok(TypeSpec::Character(None))) | |
| 110 | + } | |
| 78 | 111 | _ => None, |
| 79 | 112 | } |
| 80 | 113 | } |
@@ -90,7 +123,7 @@ impl<'a> Parser<'a> { | ||
| 90 | 123 | return Ok(None); |
| 91 | 124 | } |
| 92 | 125 | self.advance(); // ( |
| 93 | - // Check for kind= keyword | |
| 126 | + // Check for kind= keyword | |
| 94 | 127 | if self.peek_text().eq_ignore_ascii_case("kind") { |
| 95 | 128 | let next_pos = self.pos + 1; |
| 96 | 129 | if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign { |
@@ -107,7 +140,10 @@ impl<'a> Parser<'a> { | ||
| 107 | 140 | // Check for *N (old-style) |
| 108 | 141 | if self.eat(&TokenKind::Star) { |
| 109 | 142 | let len = self.parse_len_spec()?; |
| 110 | - return Ok(Some(CharSelector { len: Some(len), kind: None })); | |
| 143 | + return Ok(Some(CharSelector { | |
| 144 | + len: Some(len), | |
| 145 | + kind: None, | |
| 146 | + })); | |
| 111 | 147 | } |
| 112 | 148 | if self.peek() != &TokenKind::LParen { |
| 113 | 149 | return Ok(None); |
@@ -185,7 +221,11 @@ impl<'a> Parser<'a> { | ||
| 185 | 221 | self.expect(&TokenKind::LParen)?; |
| 186 | 222 | if self.eat(&TokenKind::Star) { |
| 187 | 223 | self.expect(&TokenKind::RParen)?; |
| 188 | - return Ok(if is_class { TypeSpec::ClassStar } else { TypeSpec::TypeStar }); | |
| 224 | + return Ok(if is_class { | |
| 225 | + TypeSpec::ClassStar | |
| 226 | + } else { | |
| 227 | + TypeSpec::TypeStar | |
| 228 | + }); | |
| 189 | 229 | } |
| 190 | 230 | if self.peek() != &TokenKind::Identifier { |
| 191 | 231 | return Err(self.error(format!("expected type name, got {}", self.peek()))); |
@@ -193,7 +233,11 @@ impl<'a> Parser<'a> { | ||
| 193 | 233 | let name_tok = self.advance().clone(); |
| 194 | 234 | let name = name_tok.text; |
| 195 | 235 | self.expect(&TokenKind::RParen)?; |
| 196 | - Ok(if is_class { TypeSpec::Class(name) } else { TypeSpec::Type(name) }) | |
| 236 | + Ok(if is_class { | |
| 237 | + TypeSpec::Class(name) | |
| 238 | + } else { | |
| 239 | + TypeSpec::Type(name) | |
| 240 | + }) | |
| 197 | 241 | } |
| 198 | 242 | |
| 199 | 243 | // ---- Attribute parsing ---- |
@@ -202,21 +246,66 @@ impl<'a> Parser<'a> { | ||
| 202 | 246 | pub fn try_parse_attribute(&mut self) -> Option<Result<Attribute, ParseError>> { |
| 203 | 247 | let text = self.peek_text().to_lowercase(); |
| 204 | 248 | match text.as_str() { |
| 205 | - "allocatable" => { self.advance(); Some(Ok(Attribute::Allocatable)) } | |
| 206 | - "pointer" => { self.advance(); Some(Ok(Attribute::Pointer)) } | |
| 207 | - "target" => { self.advance(); Some(Ok(Attribute::Target)) } | |
| 208 | - "optional" => { self.advance(); Some(Ok(Attribute::Optional)) } | |
| 209 | - "save" => { self.advance(); Some(Ok(Attribute::Save)) } | |
| 210 | - "parameter" => { self.advance(); Some(Ok(Attribute::Parameter)) } | |
| 211 | - "value" => { self.advance(); Some(Ok(Attribute::Value)) } | |
| 212 | - "volatile" => { self.advance(); Some(Ok(Attribute::Volatile)) } | |
| 213 | - "asynchronous" => { self.advance(); Some(Ok(Attribute::Asynchronous)) } | |
| 214 | - "protected" => { self.advance(); Some(Ok(Attribute::Protected)) } | |
| 215 | - "contiguous" => { self.advance(); Some(Ok(Attribute::Contiguous)) } | |
| 216 | - "external" => { self.advance(); Some(Ok(Attribute::External)) } | |
| 217 | - "intrinsic" => { self.advance(); Some(Ok(Attribute::Intrinsic)) } | |
| 218 | - "public" => { self.advance(); Some(Ok(Attribute::Public)) } | |
| 219 | - "private" => { self.advance(); Some(Ok(Attribute::Private)) } | |
| 249 | + "allocatable" => { | |
| 250 | + self.advance(); | |
| 251 | + Some(Ok(Attribute::Allocatable)) | |
| 252 | + } | |
| 253 | + "pointer" => { | |
| 254 | + self.advance(); | |
| 255 | + Some(Ok(Attribute::Pointer)) | |
| 256 | + } | |
| 257 | + "target" => { | |
| 258 | + self.advance(); | |
| 259 | + Some(Ok(Attribute::Target)) | |
| 260 | + } | |
| 261 | + "optional" => { | |
| 262 | + self.advance(); | |
| 263 | + Some(Ok(Attribute::Optional)) | |
| 264 | + } | |
| 265 | + "save" => { | |
| 266 | + self.advance(); | |
| 267 | + Some(Ok(Attribute::Save)) | |
| 268 | + } | |
| 269 | + "parameter" => { | |
| 270 | + self.advance(); | |
| 271 | + Some(Ok(Attribute::Parameter)) | |
| 272 | + } | |
| 273 | + "value" => { | |
| 274 | + self.advance(); | |
| 275 | + Some(Ok(Attribute::Value)) | |
| 276 | + } | |
| 277 | + "volatile" => { | |
| 278 | + self.advance(); | |
| 279 | + Some(Ok(Attribute::Volatile)) | |
| 280 | + } | |
| 281 | + "asynchronous" => { | |
| 282 | + self.advance(); | |
| 283 | + Some(Ok(Attribute::Asynchronous)) | |
| 284 | + } | |
| 285 | + "protected" => { | |
| 286 | + self.advance(); | |
| 287 | + Some(Ok(Attribute::Protected)) | |
| 288 | + } | |
| 289 | + "contiguous" => { | |
| 290 | + self.advance(); | |
| 291 | + Some(Ok(Attribute::Contiguous)) | |
| 292 | + } | |
| 293 | + "external" => { | |
| 294 | + self.advance(); | |
| 295 | + Some(Ok(Attribute::External)) | |
| 296 | + } | |
| 297 | + "intrinsic" => { | |
| 298 | + self.advance(); | |
| 299 | + Some(Ok(Attribute::Intrinsic)) | |
| 300 | + } | |
| 301 | + "public" => { | |
| 302 | + self.advance(); | |
| 303 | + Some(Ok(Attribute::Public)) | |
| 304 | + } | |
| 305 | + "private" => { | |
| 306 | + self.advance(); | |
| 307 | + Some(Ok(Attribute::Private)) | |
| 308 | + } | |
| 220 | 309 | "dimension" => { |
| 221 | 310 | self.advance(); |
| 222 | 311 | Some(self.parse_dimension_spec().map(Attribute::Dimension)) |
@@ -244,7 +333,9 @@ impl<'a> Parser<'a> { | ||
| 244 | 333 | let mut specs = Vec::new(); |
| 245 | 334 | loop { |
| 246 | 335 | specs.push(self.parse_one_array_spec()?); |
| 247 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 336 | + if !self.eat(&TokenKind::Comma) { | |
| 337 | + break; | |
| 338 | + } | |
| 248 | 339 | } |
| 249 | 340 | Ok(specs) |
| 250 | 341 | } |
@@ -289,11 +380,17 @@ impl<'a> Parser<'a> { | ||
| 289 | 380 | return Ok(ArraySpec::AssumedShape { lower: Some(first) }); |
| 290 | 381 | } |
| 291 | 382 | let upper = self.parse_expr()?; |
| 292 | - return Ok(ArraySpec::Explicit { lower: Some(first), upper }); | |
| 383 | + return Ok(ArraySpec::Explicit { | |
| 384 | + lower: Some(first), | |
| 385 | + upper, | |
| 386 | + }); | |
| 293 | 387 | } |
| 294 | 388 | |
| 295 | 389 | // Just an upper bound (lower is 1 implicitly). |
| 296 | - Ok(ArraySpec::Explicit { lower: None, upper: first }) | |
| 390 | + Ok(ArraySpec::Explicit { | |
| 391 | + lower: None, | |
| 392 | + upper: first, | |
| 393 | + }) | |
| 297 | 394 | } |
| 298 | 395 | |
| 299 | 396 | fn parse_intent_spec(&mut self) -> Result<Intent, ParseError> { |
@@ -309,9 +406,20 @@ impl<'a> Parser<'a> { | ||
| 309 | 406 | Intent::In |
| 310 | 407 | } |
| 311 | 408 | } |
| 312 | - "out" => { self.advance(); Intent::Out } | |
| 313 | - "inout" => { self.advance(); Intent::InOut } | |
| 314 | - _ => return Err(self.error(format!("expected intent specifier, got {}", self.peek_text()))), | |
| 409 | + "out" => { | |
| 410 | + self.advance(); | |
| 411 | + Intent::Out | |
| 412 | + } | |
| 413 | + "inout" => { | |
| 414 | + self.advance(); | |
| 415 | + Intent::InOut | |
| 416 | + } | |
| 417 | + _ => { | |
| 418 | + return Err(self.error(format!( | |
| 419 | + "expected intent specifier, got {}", | |
| 420 | + self.peek_text() | |
| 421 | + ))) | |
| 422 | + } | |
| 315 | 423 | }; |
| 316 | 424 | self.expect(&TokenKind::RParen)?; |
| 317 | 425 | Ok(intent) |
@@ -370,14 +478,23 @@ impl<'a> Parser<'a> { | ||
| 370 | 478 | let entities = self.parse_entity_list()?; |
| 371 | 479 | |
| 372 | 480 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 373 | - Ok(Spanned::new(Decl::TypeDecl { type_spec, attrs, entities }, span)) | |
| 481 | + Ok(Spanned::new( | |
| 482 | + Decl::TypeDecl { | |
| 483 | + type_spec, | |
| 484 | + attrs, | |
| 485 | + entities, | |
| 486 | + }, | |
| 487 | + span, | |
| 488 | + )) | |
| 374 | 489 | } |
| 375 | 490 | |
| 376 | 491 | fn parse_entity_list(&mut self) -> Result<Vec<EntityDecl>, ParseError> { |
| 377 | 492 | let mut entities = Vec::new(); |
| 378 | 493 | loop { |
| 379 | 494 | entities.push(self.parse_entity_decl()?); |
| 380 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 495 | + if !self.eat(&TokenKind::Comma) { | |
| 496 | + break; | |
| 497 | + } | |
| 381 | 498 | } |
| 382 | 499 | Ok(entities) |
| 383 | 500 | } |
@@ -401,7 +518,7 @@ impl<'a> Parser<'a> { | ||
| 401 | 518 | |
| 402 | 519 | if self.peek() == &TokenKind::LBracket { |
| 403 | 520 | return Err( |
| 404 | - self.error("coarray declarations are recognized but not yet implemented".into()), | |
| 521 | + self.error("coarray declarations are recognized but not yet implemented".into()) | |
| 405 | 522 | ); |
| 406 | 523 | } |
| 407 | 524 | |
@@ -426,7 +543,13 @@ impl<'a> Parser<'a> { | ||
| 426 | 543 | None |
| 427 | 544 | }; |
| 428 | 545 | |
| 429 | - Ok(EntityDecl { name, array_spec, char_len, init, ptr_init }) | |
| 546 | + Ok(EntityDecl { | |
| 547 | + name, | |
| 548 | + array_spec, | |
| 549 | + char_len, | |
| 550 | + init, | |
| 551 | + ptr_init, | |
| 552 | + }) | |
| 430 | 553 | } |
| 431 | 554 | |
| 432 | 555 | // ---- USE statement ---- |
@@ -471,21 +594,36 @@ impl<'a> Parser<'a> { | ||
| 471 | 594 | } |
| 472 | 595 | |
| 473 | 596 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 474 | - Ok(Spanned::new(Decl::UseStmt { module, nature, renames, only }, span)) | |
| 597 | + Ok(Spanned::new( | |
| 598 | + Decl::UseStmt { | |
| 599 | + module, | |
| 600 | + nature, | |
| 601 | + renames, | |
| 602 | + only, | |
| 603 | + }, | |
| 604 | + span, | |
| 605 | + )) | |
| 475 | 606 | } |
| 476 | 607 | |
| 477 | 608 | fn parse_only_list(&mut self) -> Result<Vec<OnlyItem>, ParseError> { |
| 478 | 609 | let mut items = Vec::new(); |
| 479 | - if self.at_stmt_end() { return Ok(items); } | |
| 610 | + if self.at_stmt_end() { | |
| 611 | + return Ok(items); | |
| 612 | + } | |
| 480 | 613 | loop { |
| 481 | 614 | let name = self.advance().clone().text; |
| 482 | 615 | if self.eat(&TokenKind::Arrow) { |
| 483 | 616 | let remote = self.advance().clone().text; |
| 484 | - items.push(OnlyItem::Rename(Rename { local: name, remote })); | |
| 617 | + items.push(OnlyItem::Rename(Rename { | |
| 618 | + local: name, | |
| 619 | + remote, | |
| 620 | + })); | |
| 485 | 621 | } else { |
| 486 | 622 | items.push(OnlyItem::Name(name)); |
| 487 | 623 | } |
| 488 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 624 | + if !self.eat(&TokenKind::Comma) { | |
| 625 | + break; | |
| 626 | + } | |
| 489 | 627 | } |
| 490 | 628 | Ok(items) |
| 491 | 629 | } |
@@ -497,7 +635,9 @@ impl<'a> Parser<'a> { | ||
| 497 | 635 | self.expect(&TokenKind::Arrow)?; |
| 498 | 636 | let remote = self.advance().clone().text; |
| 499 | 637 | renames.push(Rename { local, remote }); |
| 500 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 638 | + if !self.eat(&TokenKind::Comma) { | |
| 639 | + break; | |
| 640 | + } | |
| 501 | 641 | } |
| 502 | 642 | Ok(renames) |
| 503 | 643 | } |
@@ -519,11 +659,19 @@ impl<'a> Parser<'a> { | ||
| 519 | 659 | loop { |
| 520 | 660 | let spec = self.peek_text().to_lowercase(); |
| 521 | 661 | match spec.as_str() { |
| 522 | - "type" => { self.advance(); type_ = true; } | |
| 523 | - "external" => { self.advance(); external = true; } | |
| 662 | + "type" => { | |
| 663 | + self.advance(); | |
| 664 | + type_ = true; | |
| 665 | + } | |
| 666 | + "external" => { | |
| 667 | + self.advance(); | |
| 668 | + external = true; | |
| 669 | + } | |
| 524 | 670 | _ => break, |
| 525 | 671 | } |
| 526 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 672 | + if !self.eat(&TokenKind::Comma) { | |
| 673 | + break; | |
| 674 | + } | |
| 527 | 675 | } |
| 528 | 676 | self.expect(&TokenKind::RParen)?; |
| 529 | 677 | } |
@@ -538,7 +686,8 @@ impl<'a> Parser<'a> { | ||
| 538 | 686 | // is a letter range, not kind=i-n. |
| 539 | 687 | let mut specs = Vec::new(); |
| 540 | 688 | loop { |
| 541 | - let type_spec = self.parse_implicit_type_spec() | |
| 689 | + let type_spec = self | |
| 690 | + .parse_implicit_type_spec() | |
| 542 | 691 | .ok_or_else(|| self.error("expected type specifier in IMPLICIT".into()))??; |
| 543 | 692 | self.expect(&TokenKind::LParen)?; |
| 544 | 693 | let mut ranges = Vec::new(); |
@@ -547,11 +696,15 @@ impl<'a> Parser<'a> { | ||
| 547 | 696 | self.expect(&TokenKind::Minus)?; |
| 548 | 697 | let end_letter = self.advance().clone().text.chars().next().unwrap_or('z'); |
| 549 | 698 | ranges.push((start_letter, end_letter)); |
| 550 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 699 | + if !self.eat(&TokenKind::Comma) { | |
| 700 | + break; | |
| 701 | + } | |
| 551 | 702 | } |
| 552 | 703 | self.expect(&TokenKind::RParen)?; |
| 553 | 704 | specs.push(ImplicitSpec { type_spec, ranges }); |
| 554 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 705 | + if !self.eat(&TokenKind::Comma) { | |
| 706 | + break; | |
| 707 | + } | |
| 555 | 708 | } |
| 556 | 709 | |
| 557 | 710 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
@@ -570,9 +723,18 @@ impl<'a> Parser<'a> { | ||
| 570 | 723 | while self.eat(&TokenKind::Comma) { |
| 571 | 724 | let text = self.peek_text().to_lowercase(); |
| 572 | 725 | match text.as_str() { |
| 573 | - "abstract" => { self.advance(); attrs.push(TypeAttr::Abstract); } | |
| 574 | - "public" => { self.advance(); attrs.push(TypeAttr::Public); } | |
| 575 | - "private" => { self.advance(); attrs.push(TypeAttr::Private); } | |
| 726 | + "abstract" => { | |
| 727 | + self.advance(); | |
| 728 | + attrs.push(TypeAttr::Abstract); | |
| 729 | + } | |
| 730 | + "public" => { | |
| 731 | + self.advance(); | |
| 732 | + attrs.push(TypeAttr::Public); | |
| 733 | + } | |
| 734 | + "private" => { | |
| 735 | + self.advance(); | |
| 736 | + attrs.push(TypeAttr::Private); | |
| 737 | + } | |
| 576 | 738 | "bind" => { |
| 577 | 739 | self.advance(); |
| 578 | 740 | let name = self.parse_bind_spec()?; |
@@ -609,8 +771,12 @@ impl<'a> Parser<'a> { | ||
| 609 | 771 | loop { |
| 610 | 772 | self.skip_newlines(); |
| 611 | 773 | let proc_text = self.peek_text().to_lowercase(); |
| 612 | - if proc_text == "end" { break; } | |
| 613 | - if proc_text == "endtype" { break; } | |
| 774 | + if proc_text == "end" { | |
| 775 | + break; | |
| 776 | + } | |
| 777 | + if proc_text == "endtype" { | |
| 778 | + break; | |
| 779 | + } | |
| 614 | 780 | |
| 615 | 781 | if proc_text == "procedure" { |
| 616 | 782 | self.advance(); |
@@ -627,7 +793,9 @@ impl<'a> Parser<'a> { | ||
| 627 | 793 | final_procs.push(name); |
| 628 | 794 | } else { |
| 629 | 795 | // Skip unknown lines in contains section. |
| 630 | - while !self.at_stmt_end() { self.advance(); } | |
| 796 | + while !self.at_stmt_end() { | |
| 797 | + self.advance(); | |
| 798 | + } | |
| 631 | 799 | } |
| 632 | 800 | self.skip_newlines(); |
| 633 | 801 | } |
@@ -645,7 +813,9 @@ impl<'a> Parser<'a> { | ||
| 645 | 813 | components.push(comp); |
| 646 | 814 | } else { |
| 647 | 815 | // Skip unrecognized lines. |
| 648 | - while !self.at_stmt_end() { self.advance(); } | |
| 816 | + while !self.at_stmt_end() { | |
| 817 | + self.advance(); | |
| 818 | + } | |
| 649 | 819 | self.skip_newlines(); |
| 650 | 820 | } |
| 651 | 821 | } |
@@ -663,13 +833,25 @@ impl<'a> Parser<'a> { | ||
| 663 | 833 | } |
| 664 | 834 | |
| 665 | 835 | let extends = attrs.iter().find_map(|a| { |
| 666 | - if let TypeAttr::Extends(ref p) = a { Some(p.clone()) } else { None } | |
| 836 | + if let TypeAttr::Extends(ref p) = a { | |
| 837 | + Some(p.clone()) | |
| 838 | + } else { | |
| 839 | + None | |
| 840 | + } | |
| 667 | 841 | }); |
| 668 | 842 | |
| 669 | 843 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 670 | - Ok(Spanned::new(Decl::DerivedTypeDef { | |
| 671 | - name, extends, attrs, components, type_bound_procs, final_procs, | |
| 672 | - }, span)) | |
| 844 | + Ok(Spanned::new( | |
| 845 | + Decl::DerivedTypeDef { | |
| 846 | + name, | |
| 847 | + extends, | |
| 848 | + attrs, | |
| 849 | + components, | |
| 850 | + type_bound_procs, | |
| 851 | + final_procs, | |
| 852 | + }, | |
| 853 | + span, | |
| 854 | + )) | |
| 673 | 855 | } |
| 674 | 856 | |
| 675 | 857 | fn parse_type_bound_proc(&mut self) -> Result<TypeBoundProc, ParseError> { |
@@ -691,7 +873,12 @@ impl<'a> Parser<'a> { | ||
| 691 | 873 | } else { |
| 692 | 874 | None |
| 693 | 875 | }; |
| 694 | - Ok(TypeBoundProc { name, binding, attrs: proc_attrs, is_generic: false }) | |
| 876 | + Ok(TypeBoundProc { | |
| 877 | + name, | |
| 878 | + binding, | |
| 879 | + attrs: proc_attrs, | |
| 880 | + is_generic: false, | |
| 881 | + }) | |
| 695 | 882 | } |
| 696 | 883 | |
| 697 | 884 | fn parse_type_bound_proc_generic(&mut self) -> Result<TypeBoundProc, ParseError> { |
@@ -711,7 +898,12 @@ impl<'a> Parser<'a> { | ||
| 711 | 898 | } else { |
| 712 | 899 | None |
| 713 | 900 | }; |
| 714 | - Ok(TypeBoundProc { name, binding, attrs: Vec::new(), is_generic: true }) | |
| 901 | + Ok(TypeBoundProc { | |
| 902 | + name, | |
| 903 | + binding, | |
| 904 | + attrs: Vec::new(), | |
| 905 | + is_generic: true, | |
| 906 | + }) | |
| 715 | 907 | } |
| 716 | 908 | |
| 717 | 909 | // ---- PARAMETER, COMMON, EQUIVALENCE, DATA ---- |
@@ -726,7 +918,9 @@ impl<'a> Parser<'a> { | ||
| 726 | 918 | self.expect(&TokenKind::Assign)?; |
| 727 | 919 | let value = self.parse_expr()?; |
| 728 | 920 | pairs.push((name, value)); |
| 729 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 921 | + if !self.eat(&TokenKind::Comma) { | |
| 922 | + break; | |
| 923 | + } | |
| 730 | 924 | } |
| 731 | 925 | self.expect(&TokenKind::RParen)?; |
| 732 | 926 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
@@ -746,7 +940,9 @@ impl<'a> Parser<'a> { | ||
| 746 | 940 | let mut vars = Vec::new(); |
| 747 | 941 | loop { |
| 748 | 942 | vars.push(self.advance().clone().text); |
| 749 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 943 | + if !self.eat(&TokenKind::Comma) { | |
| 944 | + break; | |
| 945 | + } | |
| 750 | 946 | } |
| 751 | 947 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 752 | 948 | Ok(Spanned::new(Decl::CommonBlock { name, vars }, span)) |
@@ -763,19 +959,27 @@ impl<'a> Parser<'a> { | ||
| 763 | 959 | let mut objects = Vec::new(); |
| 764 | 960 | while self.peek() != &TokenKind::Slash { |
| 765 | 961 | objects.push(self.parse_expr_bp(BP_MUL.right)?); |
| 766 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 962 | + if !self.eat(&TokenKind::Comma) { | |
| 963 | + break; | |
| 964 | + } | |
| 767 | 965 | } |
| 768 | 966 | self.expect(&TokenKind::Slash)?; |
| 769 | 967 | let mut values = Vec::new(); |
| 770 | 968 | while self.peek() != &TokenKind::Slash { |
| 771 | 969 | values.push(self.parse_expr_bp(BP_MUL.right)?); |
| 772 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 970 | + if !self.eat(&TokenKind::Comma) { | |
| 971 | + break; | |
| 972 | + } | |
| 773 | 973 | } |
| 774 | 974 | self.expect(&TokenKind::Slash)?; |
| 775 | 975 | sets.push(DataSet { objects, values }); |
| 776 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 976 | + if !self.eat(&TokenKind::Comma) { | |
| 977 | + break; | |
| 978 | + } | |
| 777 | 979 | // Check if next batch starts or if we're at end of statement. |
| 778 | - if self.at_stmt_end() { break; } | |
| 980 | + if self.at_stmt_end() { | |
| 981 | + break; | |
| 982 | + } | |
| 779 | 983 | } |
| 780 | 984 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 781 | 985 | Ok(Spanned::new(Decl::DataStmt { sets }, span)) |
@@ -790,11 +994,15 @@ impl<'a> Parser<'a> { | ||
| 790 | 994 | let mut group = Vec::new(); |
| 791 | 995 | loop { |
| 792 | 996 | group.push(self.parse_expr()?); |
| 793 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 997 | + if !self.eat(&TokenKind::Comma) { | |
| 998 | + break; | |
| 999 | + } | |
| 794 | 1000 | } |
| 795 | 1001 | self.expect(&TokenKind::RParen)?; |
| 796 | 1002 | groups.push(group); |
| 797 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 1003 | + if !self.eat(&TokenKind::Comma) { | |
| 1004 | + break; | |
| 1005 | + } | |
| 798 | 1006 | } |
| 799 | 1007 | let span = crate::parser::expr::span_from_to(start, self.prev_span()); |
| 800 | 1008 | Ok(Spanned::new(Decl::EquivalenceStmt { groups }, span)) |
@@ -860,11 +1068,18 @@ mod tests { | ||
| 860 | 1068 | #[test] |
| 861 | 1069 | fn integer_simple() { |
| 862 | 1070 | let d = parse_decl("integer :: x, y, z"); |
| 863 | - if let Decl::TypeDecl { type_spec, entities, .. } = &d.node { | |
| 1071 | + if let Decl::TypeDecl { | |
| 1072 | + type_spec, | |
| 1073 | + entities, | |
| 1074 | + .. | |
| 1075 | + } = &d.node | |
| 1076 | + { | |
| 864 | 1077 | assert!(matches!(type_spec, TypeSpec::Integer(None))); |
| 865 | 1078 | assert_eq!(entities.len(), 3); |
| 866 | 1079 | assert_eq!(entities[0].name, "x"); |
| 867 | - } else { panic!("not TypeDecl"); } | |
| 1080 | + } else { | |
| 1081 | + panic!("not TypeDecl"); | |
| 1082 | + } | |
| 868 | 1083 | } |
| 869 | 1084 | |
| 870 | 1085 | #[test] |
@@ -873,7 +1088,9 @@ mod tests { | ||
| 873 | 1088 | if let Decl::TypeDecl { entities, .. } = &d.node { |
| 874 | 1089 | assert!(entities[0].init.is_some()); |
| 875 | 1090 | assert!(entities[1].init.is_some()); |
| 876 | - } else { panic!("not TypeDecl"); } | |
| 1091 | + } else { | |
| 1092 | + panic!("not TypeDecl"); | |
| 1093 | + } | |
| 877 | 1094 | } |
| 878 | 1095 | |
| 879 | 1096 | #[test] |
@@ -881,39 +1098,64 @@ mod tests { | ||
| 881 | 1098 | let d = parse_decl("integer(8) :: x"); |
| 882 | 1099 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 883 | 1100 | assert!(matches!(type_spec, TypeSpec::Integer(Some(_)))); |
| 884 | - } else { panic!("not TypeDecl"); } | |
| 1101 | + } else { | |
| 1102 | + panic!("not TypeDecl"); | |
| 1103 | + } | |
| 885 | 1104 | } |
| 886 | 1105 | |
| 887 | 1106 | #[test] |
| 888 | 1107 | fn real_allocatable() { |
| 889 | 1108 | let d = parse_decl("real(8), allocatable :: matrix(:,:)"); |
| 890 | - if let Decl::TypeDecl { type_spec, attrs, entities } = &d.node { | |
| 1109 | + if let Decl::TypeDecl { | |
| 1110 | + type_spec, | |
| 1111 | + attrs, | |
| 1112 | + entities, | |
| 1113 | + } = &d.node | |
| 1114 | + { | |
| 891 | 1115 | assert!(matches!(type_spec, TypeSpec::Real(Some(_)))); |
| 892 | 1116 | assert!(attrs.contains(&Attribute::Allocatable)); |
| 893 | 1117 | assert!(entities[0].array_spec.is_some()); |
| 894 | - } else { panic!("not TypeDecl"); } | |
| 1118 | + } else { | |
| 1119 | + panic!("not TypeDecl"); | |
| 1120 | + } | |
| 895 | 1121 | } |
| 896 | 1122 | |
| 897 | 1123 | #[test] |
| 898 | 1124 | fn character_deferred_length() { |
| 899 | 1125 | let d = parse_decl("character(len=:), allocatable :: name"); |
| 900 | - if let Decl::TypeDecl { type_spec, attrs, .. } = &d.node { | |
| 1126 | + if let Decl::TypeDecl { | |
| 1127 | + type_spec, attrs, .. | |
| 1128 | + } = &d.node | |
| 1129 | + { | |
| 901 | 1130 | if let TypeSpec::Character(Some(cs)) = type_spec { |
| 902 | 1131 | assert!(matches!(cs.len, Some(LenSpec::Colon))); |
| 903 | - } else { panic!("not character type"); } | |
| 1132 | + } else { | |
| 1133 | + panic!("not character type"); | |
| 1134 | + } | |
| 904 | 1135 | assert!(attrs.contains(&Attribute::Allocatable)); |
| 905 | - } else { panic!("not TypeDecl"); } | |
| 1136 | + } else { | |
| 1137 | + panic!("not TypeDecl"); | |
| 1138 | + } | |
| 906 | 1139 | } |
| 907 | 1140 | |
| 908 | 1141 | #[test] |
| 909 | 1142 | fn character_assumed_length() { |
| 910 | 1143 | let d = parse_decl("character(len=*), intent(in) :: input"); |
| 911 | - if let Decl::TypeDecl { type_spec, attrs, .. } = &d.node { | |
| 1144 | + if let Decl::TypeDecl { | |
| 1145 | + type_spec, attrs, .. | |
| 1146 | + } = &d.node | |
| 1147 | + { | |
| 912 | 1148 | if let TypeSpec::Character(Some(cs)) = type_spec { |
| 913 | 1149 | assert!(matches!(cs.len, Some(LenSpec::Star))); |
| 914 | - } else { panic!("not character type"); } | |
| 915 | - assert!(attrs.iter().any(|a| matches!(a, Attribute::Intent(Intent::In)))); | |
| 916 | - } else { panic!("not TypeDecl"); } | |
| 1150 | + } else { | |
| 1151 | + panic!("not character type"); | |
| 1152 | + } | |
| 1153 | + assert!(attrs | |
| 1154 | + .iter() | |
| 1155 | + .any(|a| matches!(a, Attribute::Intent(Intent::In)))); | |
| 1156 | + } else { | |
| 1157 | + panic!("not TypeDecl"); | |
| 1158 | + } | |
| 917 | 1159 | } |
| 918 | 1160 | |
| 919 | 1161 | #[test] |
@@ -921,7 +1163,9 @@ mod tests { | ||
| 921 | 1163 | let d = parse_decl("type(my_type) :: obj"); |
| 922 | 1164 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 923 | 1165 | assert!(matches!(type_spec, TypeSpec::Type(ref n) if n == "my_type")); |
| 924 | - } else { panic!("not TypeDecl"); } | |
| 1166 | + } else { | |
| 1167 | + panic!("not TypeDecl"); | |
| 1168 | + } | |
| 925 | 1169 | } |
| 926 | 1170 | |
| 927 | 1171 | #[test] |
@@ -929,7 +1173,9 @@ mod tests { | ||
| 929 | 1173 | let d = parse_decl("class(*) :: poly"); |
| 930 | 1174 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 931 | 1175 | assert!(matches!(type_spec, TypeSpec::ClassStar)); |
| 932 | - } else { panic!("not TypeDecl"); } | |
| 1176 | + } else { | |
| 1177 | + panic!("not TypeDecl"); | |
| 1178 | + } | |
| 933 | 1179 | } |
| 934 | 1180 | |
| 935 | 1181 | #[test] |
@@ -937,23 +1183,33 @@ mod tests { | ||
| 937 | 1183 | let d = parse_decl("type(node), pointer :: ptr => null()"); |
| 938 | 1184 | if let Decl::TypeDecl { entities, .. } = &d.node { |
| 939 | 1185 | assert!(entities[0].ptr_init.is_some()); |
| 940 | - } else { panic!("not TypeDecl"); } | |
| 1186 | + } else { | |
| 1187 | + panic!("not TypeDecl"); | |
| 1188 | + } | |
| 941 | 1189 | } |
| 942 | 1190 | |
| 943 | 1191 | #[test] |
| 944 | 1192 | fn intent_inout() { |
| 945 | 1193 | let d = parse_decl("real, intent(inout) :: x"); |
| 946 | 1194 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 947 | - assert!(attrs.iter().any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 948 | - } else { panic!("not TypeDecl"); } | |
| 1195 | + assert!(attrs | |
| 1196 | + .iter() | |
| 1197 | + .any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 1198 | + } else { | |
| 1199 | + panic!("not TypeDecl"); | |
| 1200 | + } | |
| 949 | 1201 | } |
| 950 | 1202 | |
| 951 | 1203 | #[test] |
| 952 | 1204 | fn intent_in_out_two_words() { |
| 953 | 1205 | let d = parse_decl("real, intent(in out) :: x"); |
| 954 | 1206 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 955 | - assert!(attrs.iter().any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 956 | - } else { panic!("not TypeDecl"); } | |
| 1207 | + assert!(attrs | |
| 1208 | + .iter() | |
| 1209 | + .any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 1210 | + } else { | |
| 1211 | + panic!("not TypeDecl"); | |
| 1212 | + } | |
| 957 | 1213 | } |
| 958 | 1214 | |
| 959 | 1215 | #[test] |
@@ -962,8 +1218,12 @@ mod tests { | ||
| 962 | 1218 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 963 | 1219 | assert!(attrs.iter().any(|a| matches!(a, Attribute::Dimension(_)))); |
| 964 | 1220 | assert!(attrs.contains(&Attribute::Allocatable)); |
| 965 | - assert!(attrs.iter().any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 966 | - } else { panic!("not TypeDecl"); } | |
| 1221 | + assert!(attrs | |
| 1222 | + .iter() | |
| 1223 | + .any(|a| matches!(a, Attribute::Intent(Intent::InOut)))); | |
| 1224 | + } else { | |
| 1225 | + panic!("not TypeDecl"); | |
| 1226 | + } | |
| 967 | 1227 | } |
| 968 | 1228 | |
| 969 | 1229 | #[test] |
@@ -972,7 +1232,9 @@ mod tests { | ||
| 972 | 1232 | if let Decl::TypeDecl { entities, .. } = &d.node { |
| 973 | 1233 | assert_eq!(entities.len(), 2); |
| 974 | 1234 | assert_eq!(entities[0].name, "x"); |
| 975 | - } else { panic!("not TypeDecl"); } | |
| 1235 | + } else { | |
| 1236 | + panic!("not TypeDecl"); | |
| 1237 | + } | |
| 976 | 1238 | } |
| 977 | 1239 | |
| 978 | 1240 | #[test] |
@@ -980,7 +1242,9 @@ mod tests { | ||
| 980 | 1242 | let d = parse_decl("double precision :: x"); |
| 981 | 1243 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 982 | 1244 | assert!(matches!(type_spec, TypeSpec::DoublePrecision)); |
| 983 | - } else { panic!("not TypeDecl"); } | |
| 1245 | + } else { | |
| 1246 | + panic!("not TypeDecl"); | |
| 1247 | + } | |
| 984 | 1248 | } |
| 985 | 1249 | |
| 986 | 1250 | #[test] |
@@ -988,7 +1252,9 @@ mod tests { | ||
| 988 | 1252 | let d = parse_decl("integer, bind(c) :: x"); |
| 989 | 1253 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 990 | 1254 | assert!(attrs.iter().any(|a| matches!(a, Attribute::Bind(None)))); |
| 991 | - } else { panic!("not TypeDecl"); } | |
| 1255 | + } else { | |
| 1256 | + panic!("not TypeDecl"); | |
| 1257 | + } | |
| 992 | 1258 | } |
| 993 | 1259 | |
| 994 | 1260 | // ---- USE statements ---- |
@@ -999,7 +1265,9 @@ mod tests { | ||
| 999 | 1265 | if let Decl::UseStmt { module, nature, .. } = &d.node { |
| 1000 | 1266 | assert_eq!(module, "my_module"); |
| 1001 | 1267 | assert_eq!(*nature, UseNature::Normal); |
| 1002 | - } else { panic!("not UseStmt"); } | |
| 1268 | + } else { | |
| 1269 | + panic!("not UseStmt"); | |
| 1270 | + } | |
| 1003 | 1271 | } |
| 1004 | 1272 | |
| 1005 | 1273 | #[test] |
@@ -1008,7 +1276,9 @@ mod tests { | ||
| 1008 | 1276 | if let Decl::UseStmt { only, .. } = &d.node { |
| 1009 | 1277 | let items = only.as_ref().unwrap(); |
| 1010 | 1278 | assert_eq!(items.len(), 2); |
| 1011 | - } else { panic!("not UseStmt"); } | |
| 1279 | + } else { | |
| 1280 | + panic!("not UseStmt"); | |
| 1281 | + } | |
| 1012 | 1282 | } |
| 1013 | 1283 | |
| 1014 | 1284 | #[test] |
@@ -1017,7 +1287,9 @@ mod tests { | ||
| 1017 | 1287 | if let Decl::UseStmt { module, nature, .. } = &d.node { |
| 1018 | 1288 | assert_eq!(module, "iso_c_binding"); |
| 1019 | 1289 | assert_eq!(*nature, UseNature::Intrinsic); |
| 1020 | - } else { panic!("not UseStmt"); } | |
| 1290 | + } else { | |
| 1291 | + panic!("not UseStmt"); | |
| 1292 | + } | |
| 1021 | 1293 | } |
| 1022 | 1294 | |
| 1023 | 1295 | #[test] |
@@ -1026,7 +1298,9 @@ mod tests { | ||
| 1026 | 1298 | if let Decl::UseStmt { only, .. } = &d.node { |
| 1027 | 1299 | let items = only.as_ref().unwrap(); |
| 1028 | 1300 | assert!(matches!(&items[0], OnlyItem::Rename(_))); |
| 1029 | - } else { panic!("not UseStmt"); } | |
| 1301 | + } else { | |
| 1302 | + panic!("not UseStmt"); | |
| 1303 | + } | |
| 1030 | 1304 | } |
| 1031 | 1305 | |
| 1032 | 1306 | // ---- IMPLICIT ---- |
@@ -1034,13 +1308,25 @@ mod tests { | ||
| 1034 | 1308 | #[test] |
| 1035 | 1309 | fn implicit_none() { |
| 1036 | 1310 | let d = parse_decl("implicit none"); |
| 1037 | - assert!(matches!(d.node, Decl::ImplicitNone { type_: true, external: false })); | |
| 1311 | + assert!(matches!( | |
| 1312 | + d.node, | |
| 1313 | + Decl::ImplicitNone { | |
| 1314 | + type_: true, | |
| 1315 | + external: false | |
| 1316 | + } | |
| 1317 | + )); | |
| 1038 | 1318 | } |
| 1039 | 1319 | |
| 1040 | 1320 | #[test] |
| 1041 | 1321 | fn implicit_none_type_external() { |
| 1042 | 1322 | let d = parse_decl("implicit none(type, external)"); |
| 1043 | - assert!(matches!(d.node, Decl::ImplicitNone { type_: true, external: true })); | |
| 1323 | + assert!(matches!( | |
| 1324 | + d.node, | |
| 1325 | + Decl::ImplicitNone { | |
| 1326 | + type_: true, | |
| 1327 | + external: true | |
| 1328 | + } | |
| 1329 | + )); | |
| 1044 | 1330 | } |
| 1045 | 1331 | |
| 1046 | 1332 | #[test] |
@@ -1050,7 +1336,9 @@ mod tests { | ||
| 1050 | 1336 | assert_eq!(specs.len(), 1); |
| 1051 | 1337 | assert!(matches!(specs[0].type_spec, TypeSpec::DoublePrecision)); |
| 1052 | 1338 | assert_eq!(specs[0].ranges.len(), 2); |
| 1053 | - } else { panic!("not ImplicitStmt"); } | |
| 1339 | + } else { | |
| 1340 | + panic!("not ImplicitStmt"); | |
| 1341 | + } | |
| 1054 | 1342 | } |
| 1055 | 1343 | |
| 1056 | 1344 | // ---- PARAMETER, COMMON, DATA, EQUIVALENCE ---- |
@@ -1062,7 +1350,9 @@ mod tests { | ||
| 1062 | 1350 | assert_eq!(pairs.len(), 2); |
| 1063 | 1351 | assert_eq!(pairs[0].0, "pi"); |
| 1064 | 1352 | assert_eq!(pairs[1].0, "e"); |
| 1065 | - } else { panic!("not ParameterStmt"); } | |
| 1353 | + } else { | |
| 1354 | + panic!("not ParameterStmt"); | |
| 1355 | + } | |
| 1066 | 1356 | } |
| 1067 | 1357 | |
| 1068 | 1358 | #[test] |
@@ -1071,7 +1361,9 @@ mod tests { | ||
| 1071 | 1361 | if let Decl::CommonBlock { name, vars } = &d.node { |
| 1072 | 1362 | assert_eq!(name.as_deref(), Some("block1")); |
| 1073 | 1363 | assert_eq!(vars.len(), 3); |
| 1074 | - } else { panic!("not CommonBlock"); } | |
| 1364 | + } else { | |
| 1365 | + panic!("not CommonBlock"); | |
| 1366 | + } | |
| 1075 | 1367 | } |
| 1076 | 1368 | |
| 1077 | 1369 | #[test] |
@@ -1079,7 +1371,9 @@ mod tests { | ||
| 1079 | 1371 | let d = parse_decl("data x /1.0/, y /2.0/"); |
| 1080 | 1372 | if let Decl::DataStmt { sets } = &d.node { |
| 1081 | 1373 | assert_eq!(sets.len(), 2); |
| 1082 | - } else { panic!("not DataStmt"); } | |
| 1374 | + } else { | |
| 1375 | + panic!("not DataStmt"); | |
| 1376 | + } | |
| 1083 | 1377 | } |
| 1084 | 1378 | |
| 1085 | 1379 | #[test] |
@@ -1088,7 +1382,9 @@ mod tests { | ||
| 1088 | 1382 | if let Decl::EquivalenceStmt { groups } = &d.node { |
| 1089 | 1383 | assert_eq!(groups.len(), 2); |
| 1090 | 1384 | assert_eq!(groups[0].len(), 2); |
| 1091 | - } else { panic!("not EquivalenceStmt"); } | |
| 1385 | + } else { | |
| 1386 | + panic!("not EquivalenceStmt"); | |
| 1387 | + } | |
| 1092 | 1388 | } |
| 1093 | 1389 | |
| 1094 | 1390 | // ---- Audit test gap coverage ---- |
@@ -1097,8 +1393,13 @@ mod tests { | ||
| 1097 | 1393 | fn real_star8_old_style() { |
| 1098 | 1394 | let d = parse_decl("real*8 :: x"); |
| 1099 | 1395 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1100 | - assert!(matches!(type_spec, TypeSpec::Real(Some(KindSelector::Star(_))))); | |
| 1101 | - } else { panic!("not TypeDecl"); } | |
| 1396 | + assert!(matches!( | |
| 1397 | + type_spec, | |
| 1398 | + TypeSpec::Real(Some(KindSelector::Star(_))) | |
| 1399 | + )); | |
| 1400 | + } else { | |
| 1401 | + panic!("not TypeDecl"); | |
| 1402 | + } | |
| 1102 | 1403 | } |
| 1103 | 1404 | |
| 1104 | 1405 | #[test] |
@@ -1107,8 +1408,12 @@ mod tests { | ||
| 1107 | 1408 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1108 | 1409 | if let TypeSpec::Character(Some(cs)) = type_spec { |
| 1109 | 1410 | assert!(matches!(cs.len, Some(LenSpec::Expr(_)))); |
| 1110 | - } else { panic!("not character type"); } | |
| 1111 | - } else { panic!("not TypeDecl"); } | |
| 1411 | + } else { | |
| 1412 | + panic!("not character type"); | |
| 1413 | + } | |
| 1414 | + } else { | |
| 1415 | + panic!("not TypeDecl"); | |
| 1416 | + } | |
| 1112 | 1417 | } |
| 1113 | 1418 | |
| 1114 | 1419 | #[test] |
@@ -1116,7 +1421,9 @@ mod tests { | ||
| 1116 | 1421 | let d = parse_decl("integer(kind=4) :: x"); |
| 1117 | 1422 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1118 | 1423 | assert!(matches!(type_spec, TypeSpec::Integer(Some(_)))); |
| 1119 | - } else { panic!("not TypeDecl"); } | |
| 1424 | + } else { | |
| 1425 | + panic!("not TypeDecl"); | |
| 1426 | + } | |
| 1120 | 1427 | } |
| 1121 | 1428 | |
| 1122 | 1429 | #[test] |
@@ -1124,7 +1431,9 @@ mod tests { | ||
| 1124 | 1431 | let d = parse_decl("class(my_type) :: x"); |
| 1125 | 1432 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1126 | 1433 | assert!(matches!(type_spec, TypeSpec::Class(ref n) if n == "my_type")); |
| 1127 | - } else { panic!("not TypeDecl"); } | |
| 1434 | + } else { | |
| 1435 | + panic!("not TypeDecl"); | |
| 1436 | + } | |
| 1128 | 1437 | } |
| 1129 | 1438 | |
| 1130 | 1439 | #[test] |
@@ -1132,7 +1441,9 @@ mod tests { | ||
| 1132 | 1441 | let d = parse_decl("type(*) :: x"); |
| 1133 | 1442 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1134 | 1443 | assert!(matches!(type_spec, TypeSpec::TypeStar)); |
| 1135 | - } else { panic!("not TypeDecl"); } | |
| 1444 | + } else { | |
| 1445 | + panic!("not TypeDecl"); | |
| 1446 | + } | |
| 1136 | 1447 | } |
| 1137 | 1448 | |
| 1138 | 1449 | #[test] |
@@ -1169,7 +1480,9 @@ mod tests { | ||
| 1169 | 1480 | let d = parse_decl("logical :: flag"); |
| 1170 | 1481 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1171 | 1482 | assert!(matches!(type_spec, TypeSpec::Logical(None))); |
| 1172 | - } else { panic!("not TypeDecl"); } | |
| 1483 | + } else { | |
| 1484 | + panic!("not TypeDecl"); | |
| 1485 | + } | |
| 1173 | 1486 | } |
| 1174 | 1487 | |
| 1175 | 1488 | #[test] |
@@ -1177,7 +1490,9 @@ mod tests { | ||
| 1177 | 1490 | let d = parse_decl("complex :: z"); |
| 1178 | 1491 | if let Decl::TypeDecl { type_spec, .. } = &d.node { |
| 1179 | 1492 | assert!(matches!(type_spec, TypeSpec::Complex(None))); |
| 1180 | - } else { panic!("not TypeDecl"); } | |
| 1493 | + } else { | |
| 1494 | + panic!("not TypeDecl"); | |
| 1495 | + } | |
| 1181 | 1496 | } |
| 1182 | 1497 | |
| 1183 | 1498 | #[test] |
@@ -1185,7 +1500,9 @@ mod tests { | ||
| 1185 | 1500 | let d = parse_decl("implicit integer (i-n)"); |
| 1186 | 1501 | if let Decl::ImplicitStmt { specs } = &d.node { |
| 1187 | 1502 | assert!(matches!(specs[0].type_spec, TypeSpec::Integer(_))); |
| 1188 | - } else { panic!("not ImplicitStmt"); } | |
| 1503 | + } else { | |
| 1504 | + panic!("not ImplicitStmt"); | |
| 1505 | + } | |
| 1189 | 1506 | } |
| 1190 | 1507 | |
| 1191 | 1508 | #[test] |
@@ -1193,7 +1510,9 @@ mod tests { | ||
| 1193 | 1510 | let d = parse_decl("use :: my_module"); |
| 1194 | 1511 | if let Decl::UseStmt { module, .. } = &d.node { |
| 1195 | 1512 | assert_eq!(module, "my_module"); |
| 1196 | - } else { panic!("not UseStmt"); } | |
| 1513 | + } else { | |
| 1514 | + panic!("not UseStmt"); | |
| 1515 | + } | |
| 1197 | 1516 | } |
| 1198 | 1517 | |
| 1199 | 1518 | #[test] |
@@ -1201,7 +1520,9 @@ mod tests { | ||
| 1201 | 1520 | let d = parse_decl("integer, bind(c, name='cfunc') :: x"); |
| 1202 | 1521 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 1203 | 1522 | assert!(attrs.iter().any(|a| matches!(a, Attribute::Bind(Some(_))))); |
| 1204 | - } else { panic!("not TypeDecl"); } | |
| 1523 | + } else { | |
| 1524 | + panic!("not TypeDecl"); | |
| 1525 | + } | |
| 1205 | 1526 | } |
| 1206 | 1527 | |
| 1207 | 1528 | #[test] |
@@ -1209,7 +1530,9 @@ mod tests { | ||
| 1209 | 1530 | let d = parse_decl("integer, save :: x"); |
| 1210 | 1531 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 1211 | 1532 | assert!(attrs.contains(&Attribute::Save)); |
| 1212 | - } else { panic!("not TypeDecl"); } | |
| 1533 | + } else { | |
| 1534 | + panic!("not TypeDecl"); | |
| 1535 | + } | |
| 1213 | 1536 | } |
| 1214 | 1537 | |
| 1215 | 1538 | #[test] |
@@ -1217,6 +1540,8 @@ mod tests { | ||
| 1217 | 1540 | let d = parse_decl("integer, value :: x"); |
| 1218 | 1541 | if let Decl::TypeDecl { attrs, .. } = &d.node { |
| 1219 | 1542 | assert!(attrs.contains(&Attribute::Value)); |
| 1220 | - } else { panic!("not TypeDecl"); } | |
| 1543 | + } else { | |
| 1544 | + panic!("not TypeDecl"); | |
| 1545 | + } | |
| 1221 | 1546 | } |
| 1222 | 1547 | } |
src/parser/expr.rsmodified@@ -5,10 +5,10 @@ | ||
| 5 | 5 | //! unary operators, function calls, array constructors, and |
| 6 | 6 | //! component access chains. |
| 7 | 7 | |
| 8 | -use crate::ast::Spanned; | |
| 8 | +use super::{ParseError, Parser}; | |
| 9 | 9 | use crate::ast::expr::*; |
| 10 | -use crate::lexer::{TokenKind, Span}; | |
| 11 | -use super::{Parser, ParseError}; | |
| 10 | +use crate::ast::Spanned; | |
| 11 | +use crate::lexer::{Span, TokenKind}; | |
| 12 | 12 | |
| 13 | 13 | /// Binding power for Pratt parsing. |
| 14 | 14 | /// Higher = tighter binding. Each level has left and right binding power. |
@@ -24,18 +24,33 @@ pub(crate) struct Bp { | ||
| 24 | 24 | // Precedence levels (from Fortran standard, lowest to highest). |
| 25 | 25 | // We use even numbers for left bp, odd for right, to create the half-levels |
| 26 | 26 | // needed for associativity. |
| 27 | -const BP_DEFINED_BINARY: Bp = Bp { left: 2, right: 3 }; // .myop. (binary) | |
| 28 | -const BP_EQV: Bp = Bp { left: 4, right: 5 }; // .eqv., .neqv. | |
| 29 | -const BP_OR: Bp = Bp { left: 6, right: 7 }; // .or. | |
| 30 | -const BP_AND: Bp = Bp { left: 8, right: 9 }; // .and. | |
| 31 | -const BP_NOT: u8 = 10; // .not. (unary, right) | |
| 32 | -const BP_COMPARISON: Bp = Bp { left: 12, right: 12 }; // ==, /=, <, >, <=, >= (non-assoc) | |
| 33 | -const BP_CONCAT: Bp = Bp { left: 14, right: 15 }; // // | |
| 34 | -const BP_ADD: Bp = Bp { left: 16, right: 17 }; // +, - (binary) | |
| 35 | -const BP_UNARY_ADD: u8 = 18; // +, - (unary) | |
| 36 | -pub(crate) const BP_MUL: Bp = Bp { left: 20, right: 21 }; // *, / | |
| 37 | -const BP_POW: Bp = Bp { left: 23, right: 22 }; // ** (RIGHT-assoc: left > right) | |
| 38 | -const BP_DEFINED_UNARY: u8 = 24; // .myop. (unary) | |
| 27 | +const BP_DEFINED_BINARY: Bp = Bp { left: 2, right: 3 }; // .myop. (binary) | |
| 28 | +const BP_EQV: Bp = Bp { left: 4, right: 5 }; // .eqv., .neqv. | |
| 29 | +const BP_OR: Bp = Bp { left: 6, right: 7 }; // .or. | |
| 30 | +const BP_AND: Bp = Bp { left: 8, right: 9 }; // .and. | |
| 31 | +const BP_NOT: u8 = 10; // .not. (unary, right) | |
| 32 | +const BP_COMPARISON: Bp = Bp { | |
| 33 | + left: 12, | |
| 34 | + right: 12, | |
| 35 | +}; // ==, /=, <, >, <=, >= (non-assoc) | |
| 36 | +const BP_CONCAT: Bp = Bp { | |
| 37 | + left: 14, | |
| 38 | + right: 15, | |
| 39 | +}; // // | |
| 40 | +const BP_ADD: Bp = Bp { | |
| 41 | + left: 16, | |
| 42 | + right: 17, | |
| 43 | +}; // +, - (binary) | |
| 44 | +const BP_UNARY_ADD: u8 = 18; // +, - (unary) | |
| 45 | +pub(crate) const BP_MUL: Bp = Bp { | |
| 46 | + left: 20, | |
| 47 | + right: 21, | |
| 48 | +}; // *, / | |
| 49 | +const BP_POW: Bp = Bp { | |
| 50 | + left: 23, | |
| 51 | + right: 22, | |
| 52 | +}; // ** (RIGHT-assoc: left > right) | |
| 53 | +const BP_DEFINED_UNARY: u8 = 24; // .myop. (unary) | |
| 39 | 54 | |
| 40 | 55 | impl<'a> Parser<'a> { |
| 41 | 56 | /// Parse an expression. |
@@ -55,13 +70,16 @@ impl<'a> Parser<'a> { | ||
| 55 | 70 | |
| 56 | 71 | // Check for infix operator. |
| 57 | 72 | let Some(bp) = self.infix_bp() else { break }; |
| 58 | - if bp.left < min_bp { break; } | |
| 73 | + if bp.left < min_bp { | |
| 74 | + break; | |
| 75 | + } | |
| 59 | 76 | |
| 60 | 77 | // Non-associative operators: if left_bp == right_bp and we're at the |
| 61 | 78 | // same precedence level, reject chaining (e.g., a < b < c is illegal). |
| 62 | 79 | if bp.left == bp.right && bp.left == min_bp { |
| 63 | 80 | return Err(self.error( |
| 64 | - "chained comparison operators are not allowed in Fortran (non-associative)".into() | |
| 81 | + "chained comparison operators are not allowed in Fortran (non-associative)" | |
| 82 | + .into(), | |
| 65 | 83 | )); |
| 66 | 84 | } |
| 67 | 85 | |
@@ -75,11 +93,14 @@ impl<'a> Parser<'a> { | ||
| 75 | 93 | end: right.span.end, |
| 76 | 94 | }; |
| 77 | 95 | |
| 78 | - left = Spanned::new(Expr::BinaryOp { | |
| 79 | - op, | |
| 80 | - left: Box::new(left), | |
| 81 | - right: Box::new(right), | |
| 82 | - }, span); | |
| 96 | + left = Spanned::new( | |
| 97 | + Expr::BinaryOp { | |
| 98 | + op, | |
| 99 | + left: Box::new(left), | |
| 100 | + right: Box::new(right), | |
| 101 | + }, | |
| 102 | + span, | |
| 103 | + ); | |
| 83 | 104 | } |
| 84 | 105 | |
| 85 | 106 | Ok(left) |
@@ -95,38 +116,50 @@ impl<'a> Parser<'a> { | ||
| 95 | 116 | self.advance(); |
| 96 | 117 | let operand = self.parse_expr_bp(BP_UNARY_ADD)?; |
| 97 | 118 | let span = span_from_to(start, operand.span); |
| 98 | - Ok(Spanned::new(Expr::UnaryOp { | |
| 99 | - op: UnaryOp::Plus, | |
| 100 | - operand: Box::new(operand), | |
| 101 | - }, span)) | |
| 119 | + Ok(Spanned::new( | |
| 120 | + Expr::UnaryOp { | |
| 121 | + op: UnaryOp::Plus, | |
| 122 | + operand: Box::new(operand), | |
| 123 | + }, | |
| 124 | + span, | |
| 125 | + )) | |
| 102 | 126 | } |
| 103 | 127 | TokenKind::Minus => { |
| 104 | 128 | self.advance(); |
| 105 | 129 | let operand = self.parse_expr_bp(BP_UNARY_ADD)?; |
| 106 | 130 | let span = span_from_to(start, operand.span); |
| 107 | - Ok(Spanned::new(Expr::UnaryOp { | |
| 108 | - op: UnaryOp::Minus, | |
| 109 | - operand: Box::new(operand), | |
| 110 | - }, span)) | |
| 131 | + Ok(Spanned::new( | |
| 132 | + Expr::UnaryOp { | |
| 133 | + op: UnaryOp::Minus, | |
| 134 | + operand: Box::new(operand), | |
| 135 | + }, | |
| 136 | + span, | |
| 137 | + )) | |
| 111 | 138 | } |
| 112 | 139 | TokenKind::DotOp(ref name) if name == "not" => { |
| 113 | 140 | self.advance(); |
| 114 | 141 | let operand = self.parse_expr_bp(BP_NOT)?; |
| 115 | 142 | let span = span_from_to(start, operand.span); |
| 116 | - Ok(Spanned::new(Expr::UnaryOp { | |
| 117 | - op: UnaryOp::Not, | |
| 118 | - operand: Box::new(operand), | |
| 119 | - }, span)) | |
| 143 | + Ok(Spanned::new( | |
| 144 | + Expr::UnaryOp { | |
| 145 | + op: UnaryOp::Not, | |
| 146 | + operand: Box::new(operand), | |
| 147 | + }, | |
| 148 | + span, | |
| 149 | + )) | |
| 120 | 150 | } |
| 121 | 151 | TokenKind::DefinedOp(ref name) => { |
| 122 | 152 | let op_name = name.clone(); |
| 123 | 153 | self.advance(); |
| 124 | 154 | let operand = self.parse_expr_bp(BP_DEFINED_UNARY)?; |
| 125 | 155 | let span = span_from_to(start, operand.span); |
| 126 | - Ok(Spanned::new(Expr::UnaryOp { | |
| 127 | - op: UnaryOp::Defined(op_name), | |
| 128 | - operand: Box::new(operand), | |
| 129 | - }, span)) | |
| 156 | + Ok(Spanned::new( | |
| 157 | + Expr::UnaryOp { | |
| 158 | + op: UnaryOp::Defined(op_name), | |
| 159 | + operand: Box::new(operand), | |
| 160 | + }, | |
| 161 | + span, | |
| 162 | + )) | |
| 130 | 163 | } |
| 131 | 164 | |
| 132 | 165 | // Parenthesized expression or array constructor (/ ... /). |
@@ -142,16 +175,22 @@ impl<'a> Parser<'a> { | ||
| 142 | 175 | let imag = self.parse_expr()?; |
| 143 | 176 | self.expect(&TokenKind::RParen)?; |
| 144 | 177 | let span = span_from_to(start, self.prev_span()); |
| 145 | - return Ok(Spanned::new(Expr::ComplexLiteral { | |
| 146 | - real: Box::new(inner), | |
| 147 | - imag: Box::new(imag), | |
| 148 | - }, span)); | |
| 178 | + return Ok(Spanned::new( | |
| 179 | + Expr::ComplexLiteral { | |
| 180 | + real: Box::new(inner), | |
| 181 | + imag: Box::new(imag), | |
| 182 | + }, | |
| 183 | + span, | |
| 184 | + )); | |
| 149 | 185 | } |
| 150 | 186 | self.expect(&TokenKind::RParen)?; |
| 151 | 187 | let span = span_from_to(start, self.prev_span()); |
| 152 | - Ok(Spanned::new(Expr::ParenExpr { | |
| 153 | - inner: Box::new(inner), | |
| 154 | - }, span)) | |
| 188 | + Ok(Spanned::new( | |
| 189 | + Expr::ParenExpr { | |
| 190 | + inner: Box::new(inner), | |
| 191 | + }, | |
| 192 | + span, | |
| 193 | + )) | |
| 155 | 194 | } |
| 156 | 195 | |
| 157 | 196 | // Array constructor [...] |
@@ -184,10 +223,13 @@ impl<'a> Parser<'a> { | ||
| 184 | 223 | let args = self.parse_argument_list()?; |
| 185 | 224 | self.expect(&TokenKind::RParen)?; |
| 186 | 225 | let span = span_from_to(expr.span, self.prev_span()); |
| 187 | - expr = Spanned::new(Expr::FunctionCall { | |
| 188 | - callee: Box::new(expr), | |
| 189 | - args, | |
| 190 | - }, span); | |
| 226 | + expr = Spanned::new( | |
| 227 | + Expr::FunctionCall { | |
| 228 | + callee: Box::new(expr), | |
| 229 | + args, | |
| 230 | + }, | |
| 231 | + span, | |
| 232 | + ); | |
| 191 | 233 | } |
| 192 | 234 | // Component access: expr%name |
| 193 | 235 | TokenKind::Percent => { |
@@ -200,10 +242,13 @@ impl<'a> Parser<'a> { | ||
| 200 | 242 | }); |
| 201 | 243 | } |
| 202 | 244 | let span = span_from_to(expr.span, name_tok.span); |
| 203 | - expr = Spanned::new(Expr::ComponentAccess { | |
| 204 | - base: Box::new(expr), | |
| 205 | - component: name_tok.text, | |
| 206 | - }, span); | |
| 245 | + expr = Spanned::new( | |
| 246 | + Expr::ComponentAccess { | |
| 247 | + base: Box::new(expr), | |
| 248 | + component: name_tok.text, | |
| 249 | + }, | |
| 250 | + span, | |
| 251 | + ); | |
| 207 | 252 | } |
| 208 | 253 | _ => break, |
| 209 | 254 | } |
@@ -220,8 +265,12 @@ impl<'a> Parser<'a> { | ||
| 220 | 265 | TokenKind::Plus => Some(BP_ADD), |
| 221 | 266 | TokenKind::Minus => Some(BP_ADD), |
| 222 | 267 | TokenKind::Concat => Some(BP_CONCAT), |
| 223 | - TokenKind::Eq | TokenKind::Ne | TokenKind::Lt | | |
| 224 | - TokenKind::Le | TokenKind::Gt | TokenKind::Ge => Some(BP_COMPARISON), | |
| 268 | + TokenKind::Eq | |
| 269 | + | TokenKind::Ne | |
| 270 | + | TokenKind::Lt | |
| 271 | + | TokenKind::Le | |
| 272 | + | TokenKind::Gt | |
| 273 | + | TokenKind::Ge => Some(BP_COMPARISON), | |
| 225 | 274 | TokenKind::DotOp(ref name) => match name.as_str() { |
| 226 | 275 | "eq" | "ne" | "lt" | "le" | "gt" | "ge" => Some(BP_COMPARISON), |
| 227 | 276 | "and" => Some(BP_AND), |
@@ -253,20 +302,25 @@ impl<'a> Parser<'a> { | ||
| 253 | 302 | let tok = self.advance().clone(); |
| 254 | 303 | // Strip outer quotes for the value. |
| 255 | 304 | let value = if tok.text.len() >= 2 { |
| 256 | - tok.text[1..tok.text.len()-1].replace("''", "'").replace("\"\"", "\"") | |
| 305 | + tok.text[1..tok.text.len() - 1] | |
| 306 | + .replace("''", "'") | |
| 307 | + .replace("\"\"", "\"") | |
| 257 | 308 | } else { |
| 258 | 309 | tok.text.clone() |
| 259 | 310 | }; |
| 260 | - Ok(Spanned::new(Expr::StringLiteral { value, kind: None }, tok.span)) | |
| 311 | + Ok(Spanned::new( | |
| 312 | + Expr::StringLiteral { value, kind: None }, | |
| 313 | + tok.span, | |
| 314 | + )) | |
| 261 | 315 | } |
| 262 | 316 | |
| 263 | 317 | fn parse_logical_literal(&mut self) -> Result<SpannedExpr, ParseError> { |
| 264 | 318 | let tok = self.advance().clone(); |
| 265 | 319 | let lower = tok.text.to_lowercase(); |
| 266 | 320 | let value = lower.contains("true"); |
| 267 | - let kind = lower.find("._").map(|pos| { | |
| 268 | - lower[pos+2..].trim_end_matches('.').to_string() | |
| 269 | - }); | |
| 321 | + let kind = lower | |
| 322 | + .find("._") | |
| 323 | + .map(|pos| lower[pos + 2..].trim_end_matches('.').to_string()); | |
| 270 | 324 | Ok(Spanned::new(Expr::LogicalLiteral { value, kind }, tok.span)) |
| 271 | 325 | } |
| 272 | 326 | |
@@ -278,7 +332,13 @@ impl<'a> Parser<'a> { | ||
| 278 | 332 | b'Z' | b'z' => BozBase::Hex, |
| 279 | 333 | _ => BozBase::Hex, |
| 280 | 334 | }; |
| 281 | - Ok(Spanned::new(Expr::BozLiteral { text: tok.text, base }, tok.span)) | |
| 335 | + Ok(Spanned::new( | |
| 336 | + Expr::BozLiteral { | |
| 337 | + text: tok.text, | |
| 338 | + base, | |
| 339 | + }, | |
| 340 | + tok.span, | |
| 341 | + )) | |
| 282 | 342 | } |
| 283 | 343 | |
| 284 | 344 | fn parse_name(&mut self) -> Result<SpannedExpr, ParseError> { |
@@ -297,7 +357,9 @@ impl<'a> Parser<'a> { | ||
| 297 | 357 | loop { |
| 298 | 358 | let arg = self.parse_argument()?; |
| 299 | 359 | args.push(arg); |
| 300 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 360 | + if !self.eat(&TokenKind::Comma) { | |
| 361 | + break; | |
| 362 | + } | |
| 301 | 363 | } |
| 302 | 364 | Ok(args) |
| 303 | 365 | } |
@@ -320,7 +382,10 @@ impl<'a> Parser<'a> { | ||
| 320 | 382 | // Leading colon → range with no start: :end or : or ::stride |
| 321 | 383 | if matches!(self.peek(), TokenKind::Colon | TokenKind::ColonColon) { |
| 322 | 384 | let sub = self.parse_range(None)?; |
| 323 | - return Ok(Argument { keyword: None, value: sub }); | |
| 385 | + return Ok(Argument { | |
| 386 | + keyword: None, | |
| 387 | + value: sub, | |
| 388 | + }); | |
| 324 | 389 | } |
| 325 | 390 | |
| 326 | 391 | // Parse an expression. |
@@ -329,11 +394,17 @@ impl<'a> Parser<'a> { | ||
| 329 | 394 | // If followed by colon, it's a range: start:end[:stride] |
| 330 | 395 | if self.peek() == &TokenKind::Colon { |
| 331 | 396 | let sub = self.parse_range(Some(expr))?; |
| 332 | - return Ok(Argument { keyword: None, value: sub }); | |
| 397 | + return Ok(Argument { | |
| 398 | + keyword: None, | |
| 399 | + value: sub, | |
| 400 | + }); | |
| 333 | 401 | } |
| 334 | 402 | |
| 335 | 403 | // Plain element. |
| 336 | - Ok(Argument { keyword: None, value: SectionSubscript::Element(expr) }) | |
| 404 | + Ok(Argument { | |
| 405 | + keyword: None, | |
| 406 | + value: SectionSubscript::Element(expr), | |
| 407 | + }) | |
| 337 | 408 | } |
| 338 | 409 | |
| 339 | 410 | /// Parse a range subscript: [start]:end[:stride] or [start]: or : |
@@ -341,23 +412,32 @@ impl<'a> Parser<'a> { | ||
| 341 | 412 | fn parse_range(&mut self, start: Option<SpannedExpr>) -> Result<SectionSubscript, ParseError> { |
| 342 | 413 | // Handle :: (ColonColon token) as two colons — means start::stride with no end. |
| 343 | 414 | if self.eat(&TokenKind::ColonColon) { |
| 344 | - let stride = if !matches!(self.peek(), | |
| 345 | - TokenKind::Comma | TokenKind::RParen | TokenKind::RBracket) | |
| 346 | - { | |
| 415 | + let stride = if !matches!( | |
| 416 | + self.peek(), | |
| 417 | + TokenKind::Comma | TokenKind::RParen | TokenKind::RBracket | |
| 418 | + ) { | |
| 347 | 419 | Some(self.parse_expr()?) |
| 348 | 420 | } else { |
| 349 | 421 | None |
| 350 | 422 | }; |
| 351 | - return Ok(SectionSubscript::Range { start, end: None, stride }); | |
| 423 | + return Ok(SectionSubscript::Range { | |
| 424 | + start, | |
| 425 | + end: None, | |
| 426 | + stride, | |
| 427 | + }); | |
| 352 | 428 | } |
| 353 | 429 | |
| 354 | 430 | self.expect(&TokenKind::Colon)?; // consume first colon |
| 355 | 431 | |
| 356 | 432 | // Parse end (optional — absent if next is colon, comma, ), ], or ::). |
| 357 | - let end = if !matches!(self.peek(), | |
| 358 | - TokenKind::Colon | TokenKind::ColonColon | TokenKind::Comma | | |
| 359 | - TokenKind::RParen | TokenKind::RBracket) | |
| 360 | - { | |
| 433 | + let end = if !matches!( | |
| 434 | + self.peek(), | |
| 435 | + TokenKind::Colon | |
| 436 | + | TokenKind::ColonColon | |
| 437 | + | TokenKind::Comma | |
| 438 | + | TokenKind::RParen | |
| 439 | + | TokenKind::RBracket | |
| 440 | + ) { | |
| 361 | 441 | Some(self.parse_expr()?) |
| 362 | 442 | } else { |
| 363 | 443 | None |
@@ -365,9 +445,10 @@ impl<'a> Parser<'a> { | ||
| 365 | 445 | |
| 366 | 446 | // Parse stride (optional, after second colon). |
| 367 | 447 | let stride = if self.eat(&TokenKind::Colon) { |
| 368 | - if !matches!(self.peek(), | |
| 369 | - TokenKind::Comma | TokenKind::RParen | TokenKind::RBracket) | |
| 370 | - { | |
| 448 | + if !matches!( | |
| 449 | + self.peek(), | |
| 450 | + TokenKind::Comma | TokenKind::RParen | TokenKind::RBracket | |
| 451 | + ) { | |
| 371 | 452 | Some(self.parse_expr()?) |
| 372 | 453 | } else { |
| 373 | 454 | None |
@@ -389,12 +470,17 @@ impl<'a> Parser<'a> { | ||
| 389 | 470 | if self.peek() != &TokenKind::RBracket { |
| 390 | 471 | loop { |
| 391 | 472 | values.push(self.parse_ac_value()?); |
| 392 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 473 | + if !self.eat(&TokenKind::Comma) { | |
| 474 | + break; | |
| 475 | + } | |
| 393 | 476 | } |
| 394 | 477 | } |
| 395 | 478 | self.expect(&TokenKind::RBracket)?; |
| 396 | 479 | let span = span_from_to(start, self.prev_span()); |
| 397 | - Ok(Spanned::new(Expr::ArrayConstructor { type_spec, values }, span)) | |
| 480 | + Ok(Spanned::new( | |
| 481 | + Expr::ArrayConstructor { type_spec, values }, | |
| 482 | + span, | |
| 483 | + )) | |
| 398 | 484 | } |
| 399 | 485 | |
| 400 | 486 | fn parse_array_constructor_slash(&mut self, start: Span) -> Result<SpannedExpr, ParseError> { |
@@ -409,14 +495,24 @@ impl<'a> Parser<'a> { | ||
| 409 | 495 | // parenthesised implied-do form and errored on `=`. |
| 410 | 496 | let mut values = Vec::new(); |
| 411 | 497 | loop { |
| 412 | - if matches!(self.peek(), TokenKind::Slash) { break; } | |
| 498 | + if matches!(self.peek(), TokenKind::Slash) { | |
| 499 | + break; | |
| 500 | + } | |
| 413 | 501 | values.push(self.parse_ac_value_bracketed(BP_MUL.right)?); |
| 414 | - if !self.eat(&TokenKind::Comma) { break; } | |
| 502 | + if !self.eat(&TokenKind::Comma) { | |
| 503 | + break; | |
| 504 | + } | |
| 415 | 505 | } |
| 416 | 506 | self.expect(&TokenKind::Slash)?; |
| 417 | 507 | self.expect(&TokenKind::RParen)?; |
| 418 | 508 | let span = span_from_to(start, self.prev_span()); |
| 419 | - Ok(Spanned::new(Expr::ArrayConstructor { type_spec: None, values }, span)) | |
| 509 | + Ok(Spanned::new( | |
| 510 | + Expr::ArrayConstructor { | |
| 511 | + type_spec: None, | |
| 512 | + values, | |
| 513 | + }, | |
| 514 | + span, | |
| 515 | + )) | |
| 420 | 516 | } |
| 421 | 517 | |
| 422 | 518 | /// Variant of parse_ac_value that honours a minimum binding |
@@ -534,17 +630,23 @@ fn token_to_binary_op(tok: &crate::lexer::Token) -> Result<BinaryOp, ParseError> | ||
| 534 | 630 | "or" => Ok(BinaryOp::Or), |
| 535 | 631 | "eqv" => Ok(BinaryOp::Eqv), |
| 536 | 632 | "neqv" => Ok(BinaryOp::Neqv), |
| 537 | - _ => Err(ParseError { span: tok.span, msg: format!("unknown dot-operator .{}.", name) }), | |
| 633 | + _ => Err(ParseError { | |
| 634 | + span: tok.span, | |
| 635 | + msg: format!("unknown dot-operator .{}.", name), | |
| 636 | + }), | |
| 538 | 637 | }, |
| 539 | 638 | TokenKind::DefinedOp(name) => Ok(BinaryOp::Defined(name.clone())), |
| 540 | - _ => Err(ParseError { span: tok.span, msg: format!("expected operator, got {}", tok.kind) }), | |
| 639 | + _ => Err(ParseError { | |
| 640 | + span: tok.span, | |
| 641 | + msg: format!("expected operator, got {}", tok.kind), | |
| 642 | + }), | |
| 541 | 643 | } |
| 542 | 644 | } |
| 543 | 645 | |
| 544 | 646 | fn split_kind_suffix(text: &str) -> (String, Option<String>) { |
| 545 | 647 | if let Some(pos) = text.find('_') { |
| 546 | 648 | let num = text[..pos].to_string(); |
| 547 | - let kind = text[pos+1..].to_string(); | |
| 649 | + let kind = text[pos + 1..].to_string(); | |
| 548 | 650 | if kind.is_empty() { |
| 549 | 651 | (text.to_string(), None) |
| 550 | 652 | } else { |
@@ -580,19 +682,58 @@ mod tests { | ||
| 580 | 682 | |
| 581 | 683 | // ---- Literals ---- |
| 582 | 684 | |
| 583 | - #[test] fn integer() { assert_eq!(sexpr("42"), "42"); } | |
| 584 | - #[test] fn integer_kind() { assert_eq!(sexpr("42_8"), "42"); } | |
| 585 | - #[test] fn real() { assert_eq!(sexpr("3.14"), "3.14"); } | |
| 586 | - #[test] fn real_exp() { assert_eq!(sexpr("1.0e5"), "1.0e5"); } | |
| 587 | - #[test] fn real_double() { assert_eq!(sexpr("1.0d0"), "1.0d0"); } | |
| 588 | - #[test] fn string_single() { assert_eq!(sexpr("'hello'"), "'hello'"); } | |
| 589 | - #[test] fn string_double() { assert_eq!(sexpr("\"hello\""), "'hello'"); } | |
| 590 | - #[test] fn logical_true() { assert_eq!(sexpr(".true."), ".true."); } | |
| 591 | - #[test] fn logical_false() { assert_eq!(sexpr(".false."), ".false."); } | |
| 592 | - #[test] fn boz() { assert_eq!(sexpr("B'1010'"), "B'1010'"); } | |
| 593 | - #[test] fn complex_literal() { assert_eq!(sexpr("(1.0, 2.0)"), "(1.0, 2.0)"); } | |
| 594 | - #[test] fn complex_literal_exprs() { assert_eq!(sexpr("(a + b, c * d)"), "((a + b), (c * d))"); } | |
| 595 | - #[test] fn name() { assert_eq!(sexpr("x"), "x"); } | |
| 685 | + #[test] | |
| 686 | + fn integer() { | |
| 687 | + assert_eq!(sexpr("42"), "42"); | |
| 688 | + } | |
| 689 | + #[test] | |
| 690 | + fn integer_kind() { | |
| 691 | + assert_eq!(sexpr("42_8"), "42"); | |
| 692 | + } | |
| 693 | + #[test] | |
| 694 | + fn real() { | |
| 695 | + assert_eq!(sexpr("3.14"), "3.14"); | |
| 696 | + } | |
| 697 | + #[test] | |
| 698 | + fn real_exp() { | |
| 699 | + assert_eq!(sexpr("1.0e5"), "1.0e5"); | |
| 700 | + } | |
| 701 | + #[test] | |
| 702 | + fn real_double() { | |
| 703 | + assert_eq!(sexpr("1.0d0"), "1.0d0"); | |
| 704 | + } | |
| 705 | + #[test] | |
| 706 | + fn string_single() { | |
| 707 | + assert_eq!(sexpr("'hello'"), "'hello'"); | |
| 708 | + } | |
| 709 | + #[test] | |
| 710 | + fn string_double() { | |
| 711 | + assert_eq!(sexpr("\"hello\""), "'hello'"); | |
| 712 | + } | |
| 713 | + #[test] | |
| 714 | + fn logical_true() { | |
| 715 | + assert_eq!(sexpr(".true."), ".true."); | |
| 716 | + } | |
| 717 | + #[test] | |
| 718 | + fn logical_false() { | |
| 719 | + assert_eq!(sexpr(".false."), ".false."); | |
| 720 | + } | |
| 721 | + #[test] | |
| 722 | + fn boz() { | |
| 723 | + assert_eq!(sexpr("B'1010'"), "B'1010'"); | |
| 724 | + } | |
| 725 | + #[test] | |
| 726 | + fn complex_literal() { | |
| 727 | + assert_eq!(sexpr("(1.0, 2.0)"), "(1.0, 2.0)"); | |
| 728 | + } | |
| 729 | + #[test] | |
| 730 | + fn complex_literal_exprs() { | |
| 731 | + assert_eq!(sexpr("(a + b, c * d)"), "((a + b), (c * d))"); | |
| 732 | + } | |
| 733 | + #[test] | |
| 734 | + fn name() { | |
| 735 | + assert_eq!(sexpr("x"), "x"); | |
| 736 | + } | |
| 596 | 737 | |
| 597 | 738 | // ---- Arithmetic precedence ---- |
| 598 | 739 | |
@@ -813,10 +954,7 @@ mod tests { | ||
| 813 | 954 | |
| 814 | 955 | #[test] |
| 815 | 956 | fn mixed_comparison_and_logical() { |
| 816 | - assert_eq!( | |
| 817 | - sexpr("x > 0 .and. y < 10"), | |
| 818 | - "((x > 0) .and. (y < 10))" | |
| 819 | - ); | |
| 957 | + assert_eq!(sexpr("x > 0 .and. y < 10"), "((x > 0) .and. (y < 10))"); | |
| 820 | 958 | } |
| 821 | 959 | |
| 822 | 960 | #[test] |
@@ -878,12 +1016,24 @@ mod tests { | ||
| 878 | 1016 | } |
| 879 | 1017 | |
| 880 | 1018 | // ---- BOZ variants ---- |
| 881 | - #[test] fn boz_octal() { assert_eq!(sexpr("O'777'"), "O'777'"); } | |
| 882 | - #[test] fn boz_hex() { assert_eq!(sexpr("Z'FF'"), "Z'FF'"); } | |
| 1019 | + #[test] | |
| 1020 | + fn boz_octal() { | |
| 1021 | + assert_eq!(sexpr("O'777'"), "O'777'"); | |
| 1022 | + } | |
| 1023 | + #[test] | |
| 1024 | + fn boz_hex() { | |
| 1025 | + assert_eq!(sexpr("Z'FF'"), "Z'FF'"); | |
| 1026 | + } | |
| 883 | 1027 | |
| 884 | 1028 | // ---- Real literal edge cases ---- |
| 885 | - #[test] fn real_leading_dot() { assert_eq!(sexpr(".5"), ".5"); } | |
| 886 | - #[test] fn real_trailing_dot() { assert_eq!(sexpr("5."), "5."); } | |
| 1029 | + #[test] | |
| 1030 | + fn real_leading_dot() { | |
| 1031 | + assert_eq!(sexpr(".5"), ".5"); | |
| 1032 | + } | |
| 1033 | + #[test] | |
| 1034 | + fn real_trailing_dot() { | |
| 1035 | + assert_eq!(sexpr("5."), "5."); | |
| 1036 | + } | |
| 887 | 1037 | |
| 888 | 1038 | // ---- Mixed postfix chains ---- |
| 889 | 1039 | #[test] |
src/parser/mod.rsmodified@@ -4,12 +4,12 @@ | ||
| 4 | 4 | //! Expression parsing uses a Pratt parser with 12 precedence levels |
| 5 | 5 | //! matching the Fortran standard exactly. |
| 6 | 6 | |
| 7 | -pub mod expr; | |
| 8 | 7 | pub mod decl; |
| 8 | +pub mod expr; | |
| 9 | 9 | pub mod stmt; |
| 10 | 10 | pub mod unit; |
| 11 | 11 | |
| 12 | -use crate::lexer::{Token, TokenKind, Span}; | |
| 12 | +use crate::lexer::{Span, Token, TokenKind}; | |
| 13 | 13 | |
| 14 | 14 | use std::fmt; |
| 15 | 15 | |
@@ -22,7 +22,11 @@ pub struct ParseError { | ||
| 22 | 22 | |
| 23 | 23 | impl fmt::Display for ParseError { |
| 24 | 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 25 | - write!(f, "{}:{}: error: {}", self.span.start.line, self.span.start.col, self.msg) | |
| 25 | + write!( | |
| 26 | + f, | |
| 27 | + "{}:{}: error: {}", | |
| 28 | + self.span.start.line, self.span.start.col, self.msg | |
| 29 | + ) | |
| 26 | 30 | } |
| 27 | 31 | } |
| 28 | 32 | |
@@ -111,7 +115,10 @@ impl<'a> Parser<'a> { | ||
| 111 | 115 | /// F2018 §6.3.2: a semicolon separates statements on the same line |
| 112 | 116 | /// and is semantically equivalent to a newline. |
| 113 | 117 | pub fn skip_newlines(&mut self) { |
| 114 | - while matches!(self.peek(), TokenKind::Newline | TokenKind::Comment | TokenKind::Semicolon) { | |
| 118 | + while matches!( | |
| 119 | + self.peek(), | |
| 120 | + TokenKind::Newline | TokenKind::Comment | TokenKind::Semicolon | |
| 121 | + ) { | |
| 115 | 122 | self.advance(); |
| 116 | 123 | } |
| 117 | 124 | } |
@@ -140,13 +147,21 @@ impl<'a> Parser<'a> { | ||
| 140 | 147 | |
| 141 | 148 | /// Check if we're at a statement-ending token. |
| 142 | 149 | pub fn at_stmt_end(&self) -> bool { |
| 143 | - matches!(self.peek(), TokenKind::Newline | TokenKind::Semicolon | TokenKind::Eof | TokenKind::Comment) | |
| 150 | + matches!( | |
| 151 | + self.peek(), | |
| 152 | + TokenKind::Newline | TokenKind::Semicolon | TokenKind::Eof | TokenKind::Comment | |
| 153 | + ) | |
| 144 | 154 | } |
| 145 | 155 | |
| 146 | 156 | /// Check if the token at offset `n` from current position is a statement terminator. |
| 147 | 157 | pub fn at_stmt_end_after(&self, n: usize) -> bool { |
| 148 | 158 | let idx = self.pos + n; |
| 149 | - if idx >= self.tokens.len() { return true; } | |
| 150 | - matches!(self.tokens[idx].kind, TokenKind::Newline | TokenKind::Semicolon | TokenKind::Eof | TokenKind::Comment) | |
| 159 | + if idx >= self.tokens.len() { | |
| 160 | + return true; | |
| 161 | + } | |
| 162 | + matches!( | |
| 163 | + self.tokens[idx].kind, | |
| 164 | + TokenKind::Newline | TokenKind::Semicolon | TokenKind::Eof | TokenKind::Comment | |
| 165 | + ) | |
| 151 | 166 | } |
| 152 | 167 | } |
src/parser/stmt.rsmodified@@ -99,7 +99,8 @@ impl<'a> Parser<'a> { | ||
| 99 | 99 | Some(TokenKind::Identifier) |
| 100 | 100 | ); |
| 101 | 101 | if looks_like_entry_stmt { |
| 102 | - Err(self.error("ENTRY statements are recognized but not yet implemented".into())) | |
| 102 | + Err(self | |
| 103 | + .error("ENTRY statements are recognized but not yet implemented".into())) | |
| 103 | 104 | } else { |
| 104 | 105 | self.parse_assignment_or_call(start) |
| 105 | 106 | } |
@@ -178,7 +179,10 @@ impl<'a> Parser<'a> { | ||
| 178 | 179 | let span = span_from_to(start, self.prev_span()); |
| 179 | 180 | Ok(Spanned::new(Stmt::Continue { label: None }, span)) |
| 180 | 181 | } |
| 181 | - "sync" => Err(self.error("coarray SYNC statements are recognized but not yet implemented".into())), | |
| 182 | + "sync" => { | |
| 183 | + Err(self | |
| 184 | + .error("coarray SYNC statements are recognized but not yet implemented".into())) | |
| 185 | + } | |
| 182 | 186 | _ => self.parse_assignment_or_call(start), |
| 183 | 187 | } |
| 184 | 188 | } |
@@ -1179,9 +1183,10 @@ impl<'a> Parser<'a> { | ||
| 1179 | 1183 | loop { |
| 1180 | 1184 | self.skip_newlines(); |
| 1181 | 1185 | if self.peek() == &TokenKind::Eof { |
| 1182 | - return Err( | |
| 1183 | - self.error(format!("expected statement with terminating label {}", label)) | |
| 1184 | - ); | |
| 1186 | + return Err(self.error(format!( | |
| 1187 | + "expected statement with terminating label {}", | |
| 1188 | + label | |
| 1189 | + ))); | |
| 1185 | 1190 | } |
| 1186 | 1191 | |
| 1187 | 1192 | let is_terminator = self.peek() == &TokenKind::IntegerLiteral |
@@ -2310,7 +2315,9 @@ end if | ||
| 2310 | 2315 | let tokens = Lexer::tokenize("entry g(y)\n", 0).unwrap(); |
| 2311 | 2316 | let mut parser = Parser::new(&tokens); |
| 2312 | 2317 | let err = parser.parse_stmt().expect_err("ENTRY should not parse yet"); |
| 2313 | - assert!(err.msg.contains("ENTRY statements are recognized but not yet implemented")); | |
| 2318 | + assert!(err | |
| 2319 | + .msg | |
| 2320 | + .contains("ENTRY statements are recognized but not yet implemented")); | |
| 2314 | 2321 | } |
| 2315 | 2322 | |
| 2316 | 2323 | #[test] |
src/preprocess/mod.rsmodified@@ -60,11 +60,21 @@ pub struct MacroDef { | ||
| 60 | 60 | |
| 61 | 61 | impl MacroDef { |
| 62 | 62 | pub fn object(body: &str) -> Self { |
| 63 | - Self { body: body.into(), params: Vec::new(), is_function: false, is_variadic: false } | |
| 63 | + Self { | |
| 64 | + body: body.into(), | |
| 65 | + params: Vec::new(), | |
| 66 | + is_function: false, | |
| 67 | + is_variadic: false, | |
| 68 | + } | |
| 64 | 69 | } |
| 65 | 70 | |
| 66 | 71 | pub fn function(params: Vec<String>, body: &str) -> Self { |
| 67 | - Self { body: body.into(), params, is_function: true, is_variadic: false } | |
| 72 | + Self { | |
| 73 | + body: body.into(), | |
| 74 | + params, | |
| 75 | + is_function: true, | |
| 76 | + is_variadic: false, | |
| 77 | + } | |
| 68 | 78 | } |
| 69 | 79 | } |
| 70 | 80 | |
@@ -155,10 +165,20 @@ impl Preprocessor { | ||
| 155 | 165 | |
| 156 | 166 | fn make_source_loc(&self, filename: &str, line: u32) -> SourceLoc { |
| 157 | 167 | if let Some((override_line, ref override_file)) = self.line_override { |
| 158 | - let fname = if override_file.is_empty() { filename } else { override_file.as_str() }; | |
| 159 | - SourceLoc { filename: fname.into(), line: override_line } | |
| 168 | + let fname = if override_file.is_empty() { | |
| 169 | + filename | |
| 170 | + } else { | |
| 171 | + override_file.as_str() | |
| 172 | + }; | |
| 173 | + SourceLoc { | |
| 174 | + filename: fname.into(), | |
| 175 | + line: override_line, | |
| 176 | + } | |
| 160 | 177 | } else { |
| 161 | - SourceLoc { filename: filename.into(), line } | |
| 178 | + SourceLoc { | |
| 179 | + filename: filename.into(), | |
| 180 | + line, | |
| 181 | + } | |
| 162 | 182 | } |
| 163 | 183 | } |
| 164 | 184 | |
@@ -176,11 +196,17 @@ impl Preprocessor { | ||
| 176 | 196 | return Err(PreprocError { |
| 177 | 197 | filename: filename.into(), |
| 178 | 198 | line: source.lines().count() as u32, |
| 179 | - msg: format!("unterminated #if/#ifdef ({} level(s) still open)", self.cond_stack.len()), | |
| 199 | + msg: format!( | |
| 200 | + "unterminated #if/#ifdef ({} level(s) still open)", | |
| 201 | + self.cond_stack.len() | |
| 202 | + ), | |
| 180 | 203 | }); |
| 181 | 204 | } |
| 182 | 205 | |
| 183 | - Ok(PreprocOutput { text: output, source_map }) | |
| 206 | + Ok(PreprocOutput { | |
| 207 | + text: output, | |
| 208 | + source_map, | |
| 209 | + }) | |
| 184 | 210 | } |
| 185 | 211 | |
| 186 | 212 | fn process_into( |
@@ -191,11 +217,20 @@ impl Preprocessor { | ||
| 191 | 217 | source_map: &mut Vec<SourceLoc>, |
| 192 | 218 | ) -> Result<(), PreprocError> { |
| 193 | 219 | // Set dynamic macros. |
| 194 | - self.defines.insert("__FILE__".into(), MacroDef::object(&format!("\"{}\"", filename))); | |
| 220 | + self.defines.insert( | |
| 221 | + "__FILE__".into(), | |
| 222 | + MacroDef::object(&format!("\"{}\"", filename)), | |
| 223 | + ); | |
| 195 | 224 | |
| 196 | 225 | let now = current_datetime(); |
| 197 | - self.defines.insert("__DATE__".into(), MacroDef::object(&format!("\"{}\"", now.0))); | |
| 198 | - self.defines.insert("__TIME__".into(), MacroDef::object(&format!("\"{}\"", now.1))); | |
| 226 | + self.defines.insert( | |
| 227 | + "__DATE__".into(), | |
| 228 | + MacroDef::object(&format!("\"{}\"", now.0)), | |
| 229 | + ); | |
| 230 | + self.defines.insert( | |
| 231 | + "__TIME__".into(), | |
| 232 | + MacroDef::object(&format!("\"{}\"", now.1)), | |
| 233 | + ); | |
| 199 | 234 | |
| 200 | 235 | // Process lines with inline backslash continuation joining, |
| 201 | 236 | // tracking original line numbers so __LINE__ and source_map are correct. |
@@ -238,7 +273,10 @@ impl Preprocessor { | ||
| 238 | 273 | } |
| 239 | 274 | |
| 240 | 275 | // Update __LINE__ to the original starting line of this logical line. |
| 241 | - self.defines.insert("__LINE__".into(), MacroDef::object(&orig_line_num.to_string())); | |
| 276 | + self.defines.insert( | |
| 277 | + "__LINE__".into(), | |
| 278 | + MacroDef::object(&orig_line_num.to_string()), | |
| 279 | + ); | |
| 242 | 280 | |
| 243 | 281 | // Fixed-form: C, c, or * in column 1 is a comment line. |
| 244 | 282 | if self.fixed_form { |
@@ -307,7 +345,8 @@ impl Preprocessor { | ||
| 307 | 345 | "undef" => self.do_undef(args), |
| 308 | 346 | "include" => self.do_include(args, filename, line_num, output, source_map), |
| 309 | 347 | "error" => Err(PreprocError { |
| 310 | - filename: filename.into(), line: line_num, | |
| 348 | + filename: filename.into(), | |
| 349 | + line: line_num, | |
| 311 | 350 | msg: format!("#error {}", args), |
| 312 | 351 | }), |
| 313 | 352 | "warning" => { |
@@ -316,14 +355,16 @@ impl Preprocessor { | ||
| 316 | 355 | } |
| 317 | 356 | "line" => self.do_line(args, filename, line_num), |
| 318 | 357 | "" => Ok(()), // bare # is allowed (null directive) |
| 319 | - _ => Ok(()), // unknown directives are ignored (like #pragma) | |
| 358 | + _ => Ok(()), // unknown directives are ignored (like #pragma) | |
| 320 | 359 | } |
| 321 | 360 | } |
| 322 | 361 | |
| 323 | 362 | // ---- Conditional directive helpers (maintain skip_depth counter) ---- |
| 324 | 363 | |
| 325 | 364 | fn push_cond(&mut self, state: CondState) { |
| 326 | - if !matches!(state, CondState::Active) { self.skip_depth += 1; } | |
| 365 | + if !matches!(state, CondState::Active) { | |
| 366 | + self.skip_depth += 1; | |
| 367 | + } | |
| 327 | 368 | self.cond_stack.push(state); |
| 328 | 369 | } |
| 329 | 370 | |
@@ -341,7 +382,9 @@ impl Preprocessor { | ||
| 341 | 382 | |
| 342 | 383 | fn pop_cond(&mut self) -> Option<CondState> { |
| 343 | 384 | let popped = self.cond_stack.pop()?; |
| 344 | - if !matches!(popped, CondState::Active) { self.skip_depth -= 1; } | |
| 385 | + if !matches!(popped, CondState::Active) { | |
| 386 | + self.skip_depth -= 1; | |
| 387 | + } | |
| 345 | 388 | Some(popped) |
| 346 | 389 | } |
| 347 | 390 | |
@@ -355,7 +398,11 @@ impl Preprocessor { | ||
| 355 | 398 | } |
| 356 | 399 | let defined = self.defines.contains_key(name); |
| 357 | 400 | let condition = if negate { !defined } else { defined }; |
| 358 | - self.push_cond(if condition { CondState::Active } else { CondState::Skipping }); | |
| 401 | + self.push_cond(if condition { | |
| 402 | + CondState::Active | |
| 403 | + } else { | |
| 404 | + CondState::Skipping | |
| 405 | + }); | |
| 359 | 406 | Ok(()) |
| 360 | 407 | } |
| 361 | 408 | |
@@ -365,14 +412,19 @@ impl Preprocessor { | ||
| 365 | 412 | return Ok(()); |
| 366 | 413 | } |
| 367 | 414 | let val = self.eval_condition(args, filename, line_num)?; |
| 368 | - self.push_cond(if val { CondState::Active } else { CondState::Skipping }); | |
| 415 | + self.push_cond(if val { | |
| 416 | + CondState::Active | |
| 417 | + } else { | |
| 418 | + CondState::Skipping | |
| 419 | + }); | |
| 369 | 420 | Ok(()) |
| 370 | 421 | } |
| 371 | 422 | |
| 372 | 423 | fn do_elif(&mut self, args: &str, filename: &str, line_num: u32) -> Result<(), PreprocError> { |
| 373 | 424 | match self.cond_stack.last().copied() { |
| 374 | 425 | None => Err(PreprocError { |
| 375 | - filename: filename.into(), line: line_num, | |
| 426 | + filename: filename.into(), | |
| 427 | + line: line_num, | |
| 376 | 428 | msg: "#elif without matching #if".into(), |
| 377 | 429 | }), |
| 378 | 430 | Some(CondState::ParentSkipping) => Ok(()), |
@@ -383,7 +435,11 @@ impl Preprocessor { | ||
| 383 | 435 | Some(CondState::Done) => Ok(()), |
| 384 | 436 | Some(CondState::Skipping) => { |
| 385 | 437 | let val = self.eval_condition(args, filename, line_num)?; |
| 386 | - self.set_top_cond(if val { CondState::Active } else { CondState::Skipping }); | |
| 438 | + self.set_top_cond(if val { | |
| 439 | + CondState::Active | |
| 440 | + } else { | |
| 441 | + CondState::Skipping | |
| 442 | + }); | |
| 387 | 443 | Ok(()) |
| 388 | 444 | } |
| 389 | 445 | } |
@@ -392,7 +448,8 @@ impl Preprocessor { | ||
| 392 | 448 | fn do_else(&mut self, filename: &str, line_num: u32) -> Result<(), PreprocError> { |
| 393 | 449 | match self.cond_stack.last().copied() { |
| 394 | 450 | None => Err(PreprocError { |
| 395 | - filename: filename.into(), line: line_num, | |
| 451 | + filename: filename.into(), | |
| 452 | + line: line_num, | |
| 396 | 453 | msg: "#else without matching #if".into(), |
| 397 | 454 | }), |
| 398 | 455 | Some(CondState::ParentSkipping) => Ok(()), |
@@ -411,7 +468,8 @@ impl Preprocessor { | ||
| 411 | 468 | fn do_endif(&mut self, filename: &str, line_num: u32) -> Result<(), PreprocError> { |
| 412 | 469 | if self.pop_cond().is_none() { |
| 413 | 470 | return Err(PreprocError { |
| 414 | - filename: filename.into(), line: line_num, | |
| 471 | + filename: filename.into(), | |
| 472 | + line: line_num, | |
| 415 | 473 | msg: "#endif without matching #if".into(), |
| 416 | 474 | }); |
| 417 | 475 | } |
@@ -434,7 +492,8 @@ impl Preprocessor { | ||
| 434 | 492 | let rest = &args[paren_pos + 1..]; |
| 435 | 493 | if let Some(close) = rest.find(')') { |
| 436 | 494 | let params_str = &rest[..close]; |
| 437 | - let mut params: Vec<String> = params_str.split(',') | |
| 495 | + let mut params: Vec<String> = params_str | |
| 496 | + .split(',') | |
| 438 | 497 | .map(|p| p.trim().to_string()) |
| 439 | 498 | .filter(|p| !p.is_empty()) |
| 440 | 499 | .collect(); |
@@ -495,42 +554,48 @@ impl Preprocessor { | ||
| 495 | 554 | let args = args.trim(); |
| 496 | 555 | let (path_str, search_system) = if let Some(rest) = args.strip_prefix('"') { |
| 497 | 556 | let end = rest.find('"').ok_or_else(|| PreprocError { |
| 498 | - filename: filename.into(), line: line_num, | |
| 557 | + filename: filename.into(), | |
| 558 | + line: line_num, | |
| 499 | 559 | msg: "unterminated #include string".into(), |
| 500 | 560 | })?; |
| 501 | 561 | (&rest[..end], false) |
| 502 | 562 | } else if let Some(rest) = args.strip_prefix('<') { |
| 503 | 563 | let end = rest.find('>').ok_or_else(|| PreprocError { |
| 504 | - filename: filename.into(), line: line_num, | |
| 564 | + filename: filename.into(), | |
| 565 | + line: line_num, | |
| 505 | 566 | msg: "unterminated #include <path>".into(), |
| 506 | 567 | })?; |
| 507 | 568 | (&rest[..end], true) |
| 508 | 569 | } else { |
| 509 | 570 | return Err(PreprocError { |
| 510 | - filename: filename.into(), line: line_num, | |
| 571 | + filename: filename.into(), | |
| 572 | + line: line_num, | |
| 511 | 573 | msg: format!("expected \"file\" or <file> after #include, got: {}", args), |
| 512 | 574 | }); |
| 513 | 575 | }; |
| 514 | 576 | |
| 515 | 577 | if self.include_depth >= 64 { |
| 516 | 578 | return Err(PreprocError { |
| 517 | - filename: filename.into(), line: line_num, | |
| 579 | + filename: filename.into(), | |
| 580 | + line: line_num, | |
| 518 | 581 | msg: "include depth limit exceeded (possible recursion)".into(), |
| 519 | 582 | }); |
| 520 | 583 | } |
| 521 | 584 | |
| 522 | 585 | // Search for the file. |
| 523 | - let resolved = self.resolve_include(path_str, filename, search_system) | |
| 586 | + let resolved = self | |
| 587 | + .resolve_include(path_str, filename, search_system) | |
| 524 | 588 | .ok_or_else(|| PreprocError { |
| 525 | - filename: filename.into(), line: line_num, | |
| 589 | + filename: filename.into(), | |
| 590 | + line: line_num, | |
| 526 | 591 | msg: format!("cannot find include file: {}", path_str), |
| 527 | 592 | })?; |
| 528 | 593 | |
| 529 | - let content = std::fs::read_to_string(&resolved) | |
| 530 | - .map_err(|e| PreprocError { | |
| 531 | - filename: filename.into(), line: line_num, | |
| 532 | - msg: format!("reading {}: {}", resolved.display(), e), | |
| 533 | - })?; | |
| 594 | + let content = std::fs::read_to_string(&resolved).map_err(|e| PreprocError { | |
| 595 | + filename: filename.into(), | |
| 596 | + line: line_num, | |
| 597 | + msg: format!("reading {}: {}", resolved.display(), e), | |
| 598 | + })?; | |
| 534 | 599 | |
| 535 | 600 | // Save __FILE__ so it's restored after the include returns. |
| 536 | 601 | let saved_file = self.defines.get("__FILE__").cloned(); |
@@ -571,12 +636,18 @@ impl Preprocessor { | ||
| 571 | 636 | |
| 572 | 637 | // ---- Condition expression evaluator ---- |
| 573 | 638 | |
| 574 | - fn eval_condition(&self, expr: &str, filename: &str, line_num: u32) -> Result<bool, PreprocError> { | |
| 639 | + fn eval_condition( | |
| 640 | + &self, | |
| 641 | + expr: &str, | |
| 642 | + filename: &str, | |
| 643 | + line_num: u32, | |
| 644 | + ) -> Result<bool, PreprocError> { | |
| 575 | 645 | // Expand macros in the expression first. |
| 576 | 646 | let expanded = self.expand_condition_macros(expr); |
| 577 | 647 | // Parse and evaluate the expression. |
| 578 | 648 | eval_expr(&expanded).map_err(|msg| PreprocError { |
| 579 | - filename: filename.into(), line: line_num, | |
| 649 | + filename: filename.into(), | |
| 650 | + line: line_num, | |
| 580 | 651 | msg: format!("in #if expression: {}", msg), |
| 581 | 652 | }) |
| 582 | 653 | } |
@@ -657,7 +728,11 @@ impl Preprocessor { | ||
| 657 | 728 | |
| 658 | 729 | if mode == MacroExpandMode::ConditionExpr && ident == "defined" { |
| 659 | 730 | let (name, new_i) = parse_defined_operand(line, i); |
| 660 | - result.push_str(if self.defines.contains_key(name) { "1" } else { "0" }); | |
| 731 | + result.push_str(if self.defines.contains_key(name) { | |
| 732 | + "1" | |
| 733 | + } else { | |
| 734 | + "0" | |
| 735 | + }); | |
| 661 | 736 | i = new_i; |
| 662 | 737 | continue; |
| 663 | 738 | } |
@@ -671,11 +746,17 @@ impl Preprocessor { | ||
| 671 | 746 | if let Some(def) = self.defines.get(ident) { |
| 672 | 747 | if def.is_function { |
| 673 | 748 | if i < bytes.len() && bytes[i] == b'(' { |
| 674 | - if let Some((expanded, new_i)) = self.expand_function_macro(def, line, i) { | |
| 749 | + if let Some((expanded, new_i)) = | |
| 750 | + self.expand_function_macro(def, line, i) | |
| 751 | + { | |
| 675 | 752 | // Re-expand the result with this macro marked as expanding. |
| 676 | 753 | let mut next_expanding = expanding.clone(); |
| 677 | 754 | next_expanding.insert(ident.to_string()); |
| 678 | - result.push_str(&self.expand_macros_inner(&expanded, &next_expanding, mode)); | |
| 755 | + result.push_str(&self.expand_macros_inner( | |
| 756 | + &expanded, | |
| 757 | + &next_expanding, | |
| 758 | + mode, | |
| 759 | + )); | |
| 679 | 760 | i = new_i; |
| 680 | 761 | continue; |
| 681 | 762 | } |
@@ -685,7 +766,11 @@ impl Preprocessor { | ||
| 685 | 766 | // Re-expand object macro body with this macro marked as expanding. |
| 686 | 767 | let mut next_expanding = expanding.clone(); |
| 687 | 768 | next_expanding.insert(ident.to_string()); |
| 688 | - result.push_str(&self.expand_macros_inner(&def.body, &next_expanding, mode)); | |
| 769 | + result.push_str(&self.expand_macros_inner( | |
| 770 | + &def.body, | |
| 771 | + &next_expanding, | |
| 772 | + mode, | |
| 773 | + )); | |
| 689 | 774 | } |
| 690 | 775 | } else { |
| 691 | 776 | result.push_str(ident); |
@@ -700,7 +785,12 @@ impl Preprocessor { | ||
| 700 | 785 | result |
| 701 | 786 | } |
| 702 | 787 | |
| 703 | - fn expand_function_macro(&self, def: &MacroDef, line: &str, paren_start: usize) -> Option<(String, usize)> { | |
| 788 | + fn expand_function_macro( | |
| 789 | + &self, | |
| 790 | + def: &MacroDef, | |
| 791 | + line: &str, | |
| 792 | + paren_start: usize, | |
| 793 | + ) -> Option<(String, usize)> { | |
| 704 | 794 | let bytes = line.as_bytes(); |
| 705 | 795 | let mut i = paren_start + 1; // skip '(' |
| 706 | 796 | let mut args: Vec<String> = Vec::new(); |
@@ -709,10 +799,15 @@ impl Preprocessor { | ||
| 709 | 799 | |
| 710 | 800 | while i < bytes.len() && depth > 0 { |
| 711 | 801 | match bytes[i] { |
| 712 | - b'(' => { depth += 1; current_arg.push('('); } | |
| 802 | + b'(' => { | |
| 803 | + depth += 1; | |
| 804 | + current_arg.push('('); | |
| 805 | + } | |
| 713 | 806 | b')' => { |
| 714 | 807 | depth -= 1; |
| 715 | - if depth > 0 { current_arg.push(')'); } | |
| 808 | + if depth > 0 { | |
| 809 | + current_arg.push(')'); | |
| 810 | + } | |
| 716 | 811 | } |
| 717 | 812 | b',' if depth == 1 => { |
| 718 | 813 | args.push(current_arg.trim().to_string()); |
@@ -723,7 +818,9 @@ impl Preprocessor { | ||
| 723 | 818 | i += 1; |
| 724 | 819 | } |
| 725 | 820 | |
| 726 | - if depth != 0 { return None; } | |
| 821 | + if depth != 0 { | |
| 822 | + return None; | |
| 823 | + } | |
| 727 | 824 | args.push(current_arg.trim().to_string()); |
| 728 | 825 | |
| 729 | 826 | // Build parameter lookup table. |
@@ -753,13 +850,18 @@ impl Preprocessor { | ||
| 753 | 850 | id_start += 1; |
| 754 | 851 | } |
| 755 | 852 | let mut id_end = id_start; |
| 756 | - while id_end < body_bytes.len() && (body_bytes[id_end].is_ascii_alphanumeric() || body_bytes[id_end] == b'_') { | |
| 853 | + while id_end < body_bytes.len() | |
| 854 | + && (body_bytes[id_end].is_ascii_alphanumeric() || body_bytes[id_end] == b'_') | |
| 855 | + { | |
| 757 | 856 | id_end += 1; |
| 758 | 857 | } |
| 759 | 858 | if id_end > id_start { |
| 760 | 859 | let id = std::str::from_utf8(&body_bytes[id_start..id_end]).unwrap_or(""); |
| 761 | 860 | if let Some(&pi) = param_map.get(id) { |
| 762 | - body.push_str(&format!("\"{}\"", args.get(pi).map(|s| s.as_str()).unwrap_or(""))); | |
| 861 | + body.push_str(&format!( | |
| 862 | + "\"{}\"", | |
| 863 | + args.get(pi).map(|s| s.as_str()).unwrap_or("") | |
| 864 | + )); | |
| 763 | 865 | bi = id_end; |
| 764 | 866 | continue; |
| 765 | 867 | } |
@@ -781,7 +883,9 @@ impl Preprocessor { | ||
| 781 | 883 | // Identifier: check if it's a parameter name. |
| 782 | 884 | if body_bytes[bi].is_ascii_alphabetic() || body_bytes[bi] == b'_' { |
| 783 | 885 | let id_start = bi; |
| 784 | - while bi < body_bytes.len() && (body_bytes[bi].is_ascii_alphanumeric() || body_bytes[bi] == b'_') { | |
| 886 | + while bi < body_bytes.len() | |
| 887 | + && (body_bytes[bi].is_ascii_alphanumeric() || body_bytes[bi] == b'_') | |
| 888 | + { | |
| 785 | 889 | bi += 1; |
| 786 | 890 | } |
| 787 | 891 | let id = std::str::from_utf8(&body_bytes[id_start..bi]).unwrap_or(""); |
@@ -870,7 +974,14 @@ fn eval_and(expr: &str) -> Result<i64, String> { | ||
| 870 | 974 | |
| 871 | 975 | fn eval_comparison(expr: &str) -> Result<i64, String> { |
| 872 | 976 | // Scan right-to-left for left-associative evaluation. |
| 873 | - for (op, op_len) in [("==", 2), ("!=", 2), (">=", 2), ("<=", 2), (">", 1), ("<", 1)] { | |
| 977 | + for (op, op_len) in [ | |
| 978 | + ("==", 2), | |
| 979 | + ("!=", 2), | |
| 980 | + (">=", 2), | |
| 981 | + ("<=", 2), | |
| 982 | + (">", 1), | |
| 983 | + ("<", 1), | |
| 984 | + ] { | |
| 874 | 985 | if let Some(pos) = find_op_right(expr, op) { |
| 875 | 986 | let left = eval_comparison(&expr[..pos])?; |
| 876 | 987 | let right = eval_additive(&expr[pos + op_len..])?; |
@@ -917,13 +1028,17 @@ fn eval_multiplicative(expr: &str) -> Result<i64, String> { | ||
| 917 | 1028 | if let Some(pos) = find_op_right(expr, "/") { |
| 918 | 1029 | let left = eval_multiplicative(&expr[..pos])?; |
| 919 | 1030 | let right = eval_unary(&expr[pos + 1..])?; |
| 920 | - if right == 0 { return Err("division by zero in #if expression".into()); } | |
| 1031 | + if right == 0 { | |
| 1032 | + return Err("division by zero in #if expression".into()); | |
| 1033 | + } | |
| 921 | 1034 | return Ok(left / right); |
| 922 | 1035 | } |
| 923 | 1036 | if let Some(pos) = find_op_right(expr, "%") { |
| 924 | 1037 | let left = eval_multiplicative(&expr[..pos])?; |
| 925 | 1038 | let right = eval_unary(&expr[pos + 1..])?; |
| 926 | - if right == 0 { return Err("modulo by zero in #if expression".into()); } | |
| 1039 | + if right == 0 { | |
| 1040 | + return Err("modulo by zero in #if expression".into()); | |
| 1041 | + } | |
| 927 | 1042 | return Ok(left % right); |
| 928 | 1043 | } |
| 929 | 1044 | eval_unary(expr) |
@@ -954,8 +1069,8 @@ fn eval_primary(expr: &str) -> Result<i64, String> { | ||
| 954 | 1069 | |
| 955 | 1070 | // Parenthesized expression. |
| 956 | 1071 | if trimmed.starts_with('(') { |
| 957 | - let close = find_matching_paren(trimmed) | |
| 958 | - .ok_or("unmatched parenthesis in #if expression")?; | |
| 1072 | + let close = | |
| 1073 | + find_matching_paren(trimmed).ok_or("unmatched parenthesis in #if expression")?; | |
| 959 | 1074 | return eval_or(&trimmed[1..close]); |
| 960 | 1075 | } |
| 961 | 1076 | |
@@ -1039,7 +1154,9 @@ fn find_matching_paren(s: &str) -> Option<usize> { | ||
| 1039 | 1154 | '(' => depth += 1, |
| 1040 | 1155 | ')' => { |
| 1041 | 1156 | depth -= 1; |
| 1042 | - if depth == 0 { return Some(i); } | |
| 1157 | + if depth == 0 { | |
| 1158 | + return Some(i); | |
| 1159 | + } | |
| 1043 | 1160 | } |
| 1044 | 1161 | _ => {} |
| 1045 | 1162 | } |
@@ -1176,8 +1293,9 @@ fn current_datetime() -> (String, String) { | ||
| 1176 | 1293 | // Days since epoch to year/month/day (simplified Gregorian). |
| 1177 | 1294 | let (year, month, day) = epoch_days_to_date(days); |
| 1178 | 1295 | |
| 1179 | - let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
| 1180 | - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |
| 1296 | + let months = [ | |
| 1297 | + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", | |
| 1298 | + ]; | |
| 1181 | 1299 | let month_name = months.get(month as usize).unwrap_or(&"???"); |
| 1182 | 1300 | |
| 1183 | 1301 | let date = format!("{} {:2} {}", month_name, day, year); |
@@ -1398,7 +1516,10 @@ mod tests { | ||
| 1398 | 1516 | |
| 1399 | 1517 | #[test] |
| 1400 | 1518 | fn if_arithmetic() { |
| 1401 | - let out = pp_with("#if MAX > 512\nbig\n#else\nsmall\n#endif\n", &[("MAX", "1024")]); | |
| 1519 | + let out = pp_with( | |
| 1520 | + "#if MAX > 512\nbig\n#else\nsmall\n#endif\n", | |
| 1521 | + &[("MAX", "1024")], | |
| 1522 | + ); | |
| 1402 | 1523 | assert!(lines(&out).contains(&"big")); |
| 1403 | 1524 | } |
| 1404 | 1525 | |
@@ -1482,7 +1603,11 @@ mod tests { | ||
| 1482 | 1603 | // Regression test: the '' must not cause early string termination. |
| 1483 | 1604 | let out = pp_with("x = 'he said ''hello'' there' + FOO\n", &[("FOO", "1")]); |
| 1484 | 1605 | assert!(out.contains("'he said ''hello'' there'"), "got: {:?}", out); |
| 1485 | - assert!(out.contains("+ 1"), "FOO after string should expand, got: {:?}", out); | |
| 1606 | + assert!( | |
| 1607 | + out.contains("+ 1"), | |
| 1608 | + "FOO after string should expand, got: {:?}", | |
| 1609 | + out | |
| 1610 | + ); | |
| 1486 | 1611 | } |
| 1487 | 1612 | |
| 1488 | 1613 | #[test] |
@@ -1669,7 +1794,10 @@ end module | ||
| 1669 | 1794 | |
| 1670 | 1795 | #[test] |
| 1671 | 1796 | fn if_with_arithmetic() { |
| 1672 | - let out = pp_with("#if MAX + 1 > 512\nbig\n#else\nsmall\n#endif\n", &[("MAX", "1024")]); | |
| 1797 | + let out = pp_with( | |
| 1798 | + "#if MAX + 1 > 512\nbig\n#else\nsmall\n#endif\n", | |
| 1799 | + &[("MAX", "1024")], | |
| 1800 | + ); | |
| 1673 | 1801 | assert!(lines(&out).contains(&"big")); |
| 1674 | 1802 | } |
| 1675 | 1803 | |
@@ -1677,26 +1805,44 @@ end module | ||
| 1677 | 1805 | fn if_chained_macros() { |
| 1678 | 1806 | // A -> 1, B -> A. #if B should expand B->A->1, evaluating to true. |
| 1679 | 1807 | let out = pp_with("#if B\nyes\n#endif\n", &[("A", "1"), ("B", "A")]); |
| 1680 | - assert!(lines(&out).contains(&"yes"), "chained macro in #if failed, got: {:?}", lines(&out)); | |
| 1808 | + assert!( | |
| 1809 | + lines(&out).contains(&"yes"), | |
| 1810 | + "chained macro in #if failed, got: {:?}", | |
| 1811 | + lines(&out) | |
| 1812 | + ); | |
| 1681 | 1813 | } |
| 1682 | 1814 | |
| 1683 | 1815 | #[test] |
| 1684 | 1816 | fn if_chained_three_levels() { |
| 1685 | - let out = pp_with("#if C > 10\nyes\n#endif\n", &[("A", "42"), ("B", "A"), ("C", "B")]); | |
| 1686 | - assert!(lines(&out).contains(&"yes"), "3-level chain in #if failed, got: {:?}", lines(&out)); | |
| 1817 | + let out = pp_with( | |
| 1818 | + "#if C > 10\nyes\n#endif\n", | |
| 1819 | + &[("A", "42"), ("B", "A"), ("C", "B")], | |
| 1820 | + ); | |
| 1821 | + assert!( | |
| 1822 | + lines(&out).contains(&"yes"), | |
| 1823 | + "3-level chain in #if failed, got: {:?}", | |
| 1824 | + lines(&out) | |
| 1825 | + ); | |
| 1687 | 1826 | } |
| 1688 | 1827 | |
| 1689 | 1828 | #[test] |
| 1690 | 1829 | fn if_defined_and_value() { |
| 1691 | 1830 | // Common real-world pattern: #if defined(FOO) && FOO > 5 |
| 1692 | - let out = pp_with("#if defined(FOO) && FOO > 5\nyes\n#endif\n", &[("FOO", "10")]); | |
| 1831 | + let out = pp_with( | |
| 1832 | + "#if defined(FOO) && FOO > 5\nyes\n#endif\n", | |
| 1833 | + &[("FOO", "10")], | |
| 1834 | + ); | |
| 1693 | 1835 | assert!(lines(&out).contains(&"yes")); |
| 1694 | 1836 | } |
| 1695 | 1837 | |
| 1696 | 1838 | #[test] |
| 1697 | 1839 | fn if_function_macro_expands() { |
| 1698 | 1840 | let out = pp("#define INC(x) ((x) + 1)\n#if INC(41) > 41\nyes\n#endif\n"); |
| 1699 | - assert!(lines(&out).contains(&"yes"), "function macro in #if failed, got: {:?}", lines(&out)); | |
| 1841 | + assert!( | |
| 1842 | + lines(&out).contains(&"yes"), | |
| 1843 | + "function macro in #if failed, got: {:?}", | |
| 1844 | + lines(&out) | |
| 1845 | + ); | |
| 1700 | 1846 | } |
| 1701 | 1847 | |
| 1702 | 1848 | #[test] |
@@ -1773,7 +1919,11 @@ end module | ||
| 1773 | 1919 | // After continuation joining, the define body is " ((a) + (b))" |
| 1774 | 1920 | // with leading/trailing whitespace from the continuation lines. |
| 1775 | 1921 | // The body gets trimmed during define processing, so it becomes "((a) + (b))". |
| 1776 | - assert!(out.contains("((1) +"), "got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 1922 | + assert!( | |
| 1923 | + out.contains("((1) +"), | |
| 1924 | + "got: {:?}", | |
| 1925 | + out.lines().collect::<Vec<_>>() | |
| 1926 | + ); | |
| 1777 | 1927 | assert!(out.contains("(2))")); |
| 1778 | 1928 | } |
| 1779 | 1929 | |
@@ -1781,7 +1931,11 @@ end module | ||
| 1781 | 1931 | fn line_number_correct_after_continuation() { |
| 1782 | 1932 | // Lines 1-3 are a continued #define. Line 4 should report __LINE__ = 4. |
| 1783 | 1933 | let out = pp("#define M \\\n 42\na\nb = __LINE__\n"); |
| 1784 | - assert!(out.contains("b = 4"), "got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 1934 | + assert!( | |
| 1935 | + out.contains("b = 4"), | |
| 1936 | + "got: {:?}", | |
| 1937 | + out.lines().collect::<Vec<_>>() | |
| 1938 | + ); | |
| 1785 | 1939 | } |
| 1786 | 1940 | |
| 1787 | 1941 | // ---- Self-referencing macro (no infinite loop) ---- |
@@ -1853,7 +2007,11 @@ deep | ||
| 1853 | 2007 | config.include_paths.push(dir); |
| 1854 | 2008 | let src = "#include \"test_pp_include.inc\"\nreal :: x\n"; |
| 1855 | 2009 | let result = preprocess(src, &config).unwrap(); |
| 1856 | - assert!(result.text.contains("integer :: included_var"), "got: {:?}", result.text); | |
| 2010 | + assert!( | |
| 2011 | + result.text.contains("integer :: included_var"), | |
| 2012 | + "got: {:?}", | |
| 2013 | + result.text | |
| 2014 | + ); | |
| 1857 | 2015 | assert!(result.text.contains("real :: x")); |
| 1858 | 2016 | } |
| 1859 | 2017 | |
@@ -1887,8 +2045,16 @@ deep | ||
| 1887 | 2045 | config.filename = "parent.f90".into(); |
| 1888 | 2046 | let src = "before = __FILE__\n#include \"test_pp_file_restore.inc\"\nafter = __FILE__\n"; |
| 1889 | 2047 | let result = preprocess(src, &config).unwrap(); |
| 1890 | - assert!(result.text.contains("before = \"parent.f90\""), "got: {:?}", result.text); | |
| 1891 | - assert!(result.text.contains("after = \"parent.f90\""), "__FILE__ not restored, got: {:?}", result.text); | |
| 2048 | + assert!( | |
| 2049 | + result.text.contains("before = \"parent.f90\""), | |
| 2050 | + "got: {:?}", | |
| 2051 | + result.text | |
| 2052 | + ); | |
| 2053 | + assert!( | |
| 2054 | + result.text.contains("after = \"parent.f90\""), | |
| 2055 | + "__FILE__ not restored, got: {:?}", | |
| 2056 | + result.text | |
| 2057 | + ); | |
| 1892 | 2058 | } |
| 1893 | 2059 | |
| 1894 | 2060 | // ---- Fixed-form awareness ---- |
@@ -1900,7 +2066,11 @@ deep | ||
| 1900 | 2066 | config.defines.insert("FOO".into(), MacroDef::object("BAR")); |
| 1901 | 2067 | let result = preprocess("C FOO is a comment\n x = FOO\n", &config).unwrap(); |
| 1902 | 2068 | // C-line should not have FOO expanded. |
| 1903 | - assert!(result.text.contains("C FOO is a comment"), "got: {:?}", result.text); | |
| 2069 | + assert!( | |
| 2070 | + result.text.contains("C FOO is a comment"), | |
| 2071 | + "got: {:?}", | |
| 2072 | + result.text | |
| 2073 | + ); | |
| 1904 | 2074 | // Continuation line should expand FOO. |
| 1905 | 2075 | assert!(result.text.contains("x = BAR"), "got: {:?}", result.text); |
| 1906 | 2076 | } |
@@ -1911,7 +2081,11 @@ deep | ||
| 1911 | 2081 | config.fixed_form = true; |
| 1912 | 2082 | config.defines.insert("FOO".into(), MacroDef::object("BAR")); |
| 1913 | 2083 | let result = preprocess("* FOO is a comment\n", &config).unwrap(); |
| 1914 | - assert!(result.text.contains("* FOO is a comment"), "got: {:?}", result.text); | |
| 2084 | + assert!( | |
| 2085 | + result.text.contains("* FOO is a comment"), | |
| 2086 | + "got: {:?}", | |
| 2087 | + result.text | |
| 2088 | + ); | |
| 1915 | 2089 | } |
| 1916 | 2090 | |
| 1917 | 2091 | // ---- Fortran & continuation ---- |
@@ -1919,23 +2093,43 @@ deep | ||
| 1919 | 2093 | #[test] |
| 1920 | 2094 | fn fortran_ampersand_continuation() { |
| 1921 | 2095 | let out = pp_with("x = FOO + &\n BAR\n", &[("FOO", "1"), ("BAR", "2")]); |
| 1922 | - assert!(out.contains("x = 1 + 2"), "got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 2096 | + assert!( | |
| 2097 | + out.contains("x = 1 + 2"), | |
| 2098 | + "got: {:?}", | |
| 2099 | + out.lines().collect::<Vec<_>>() | |
| 2100 | + ); | |
| 1923 | 2101 | } |
| 1924 | 2102 | |
| 1925 | 2103 | #[test] |
| 1926 | 2104 | fn ampersand_in_string_not_continued() { |
| 1927 | 2105 | // & inside a string literal must NOT trigger continuation. |
| 1928 | 2106 | let out = pp("x = 'hello &'\ny = 2\n"); |
| 1929 | - assert!(out.contains("'hello &'"), "string corrupted, got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 1930 | - assert!(out.contains("y = 2"), "next line missing, got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 2107 | + assert!( | |
| 2108 | + out.contains("'hello &'"), | |
| 2109 | + "string corrupted, got: {:?}", | |
| 2110 | + out.lines().collect::<Vec<_>>() | |
| 2111 | + ); | |
| 2112 | + assert!( | |
| 2113 | + out.contains("y = 2"), | |
| 2114 | + "next line missing, got: {:?}", | |
| 2115 | + out.lines().collect::<Vec<_>>() | |
| 2116 | + ); | |
| 1931 | 2117 | } |
| 1932 | 2118 | |
| 1933 | 2119 | #[test] |
| 1934 | 2120 | fn ampersand_in_comment_not_continued() { |
| 1935 | 2121 | // & after ! comment must NOT trigger continuation. |
| 1936 | 2122 | let out = pp("x = 1 ! comment &\ny = 2\n"); |
| 1937 | - assert!(out.contains("! comment &"), "comment corrupted, got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 1938 | - assert!(out.contains("y = 2"), "next line missing, got: {:?}", out.lines().collect::<Vec<_>>()); | |
| 2123 | + assert!( | |
| 2124 | + out.contains("! comment &"), | |
| 2125 | + "comment corrupted, got: {:?}", | |
| 2126 | + out.lines().collect::<Vec<_>>() | |
| 2127 | + ); | |
| 2128 | + assert!( | |
| 2129 | + out.contains("y = 2"), | |
| 2130 | + "next line missing, got: {:?}", | |
| 2131 | + out.lines().collect::<Vec<_>>() | |
| 2132 | + ); | |
| 1939 | 2133 | } |
| 1940 | 2134 | |
| 1941 | 2135 | // ---- __DATE__ and __TIME__ ---- |
@@ -1961,7 +2155,11 @@ deep | ||
| 1961 | 2155 | let mut config = PreprocConfig::default(); |
| 1962 | 2156 | config.filename = "test.f90".into(); |
| 1963 | 2157 | let result = preprocess("x = __FILE__\n", &config).unwrap(); |
| 1964 | - assert!(result.text.contains("\"test.f90\""), "got: {:?}", result.text); | |
| 2158 | + assert!( | |
| 2159 | + result.text.contains("\"test.f90\""), | |
| 2160 | + "got: {:?}", | |
| 2161 | + result.text | |
| 2162 | + ); | |
| 1965 | 2163 | } |
| 1966 | 2164 | |
| 1967 | 2165 | // ---- defined without parens ---- |
@@ -2070,6 +2268,10 @@ deep | ||
| 2070 | 2268 | let result = preprocess("#include \"test_pp_recurse.inc\"\n", &config); |
| 2071 | 2269 | assert!(result.is_err()); |
| 2072 | 2270 | let err = result.unwrap_err(); |
| 2073 | - assert!(err.msg.contains("depth") || err.msg.contains("recursion"), "got: {}", err.msg); | |
| 2271 | + assert!( | |
| 2272 | + err.msg.contains("depth") || err.msg.contains("recursion"), | |
| 2273 | + "got: {}", | |
| 2274 | + err.msg | |
| 2275 | + ); | |
| 2074 | 2276 | } |
| 2075 | 2277 | } |
src/sema/intrinsic_modules.rsmodified@@ -17,53 +17,78 @@ pub fn register_intrinsic_modules(st: &mut SymbolTable) { | ||
| 17 | 17 | |
| 18 | 18 | fn builtin_span() -> Span { |
| 19 | 19 | let pos = crate::lexer::Position { line: 0, col: 0 }; |
| 20 | - Span { start: pos, end: pos, file_id: 0 } | |
| 20 | + Span { | |
| 21 | + start: pos, | |
| 22 | + end: pos, | |
| 23 | + file_id: 0, | |
| 24 | + } | |
| 21 | 25 | } |
| 22 | 26 | |
| 23 | 27 | fn insert_param(st: &mut SymbolTable, mod_id: ScopeId, name: &str, ti: TypeInfo) { |
| 24 | 28 | insert_param_val(st, mod_id, name, ti, None); |
| 25 | 29 | } |
| 26 | 30 | |
| 27 | -fn insert_param_val(st: &mut SymbolTable, mod_id: ScopeId, name: &str, ti: TypeInfo, val: Option<i64>) { | |
| 31 | +fn insert_param_val( | |
| 32 | + st: &mut SymbolTable, | |
| 33 | + mod_id: ScopeId, | |
| 34 | + name: &str, | |
| 35 | + ti: TypeInfo, | |
| 36 | + val: Option<i64>, | |
| 37 | +) { | |
| 28 | 38 | let span = builtin_span(); |
| 29 | - st.scope_mut(mod_id).symbols.insert(name.to_lowercase(), Symbol { | |
| 30 | - name: name.to_string(), | |
| 31 | - kind: SymbolKind::Parameter, | |
| 32 | - type_info: Some(ti), | |
| 33 | - attrs: SymbolAttrs { parameter: true, ..Default::default() }, | |
| 34 | - defined_at: span, | |
| 35 | - scope: mod_id, | |
| 36 | - arg_names: vec![], | |
| 37 | - const_value: val, | |
| 38 | - }); | |
| 39 | + st.scope_mut(mod_id).symbols.insert( | |
| 40 | + name.to_lowercase(), | |
| 41 | + Symbol { | |
| 42 | + name: name.to_string(), | |
| 43 | + kind: SymbolKind::Parameter, | |
| 44 | + type_info: Some(ti), | |
| 45 | + attrs: SymbolAttrs { | |
| 46 | + parameter: true, | |
| 47 | + ..Default::default() | |
| 48 | + }, | |
| 49 | + defined_at: span, | |
| 50 | + scope: mod_id, | |
| 51 | + arg_names: vec![], | |
| 52 | + const_value: val, | |
| 53 | + }, | |
| 54 | + ); | |
| 39 | 55 | } |
| 40 | 56 | |
| 41 | 57 | fn insert_type(st: &mut SymbolTable, mod_id: ScopeId, name: &str) { |
| 42 | 58 | let span = builtin_span(); |
| 43 | - st.scope_mut(mod_id).symbols.insert(name.to_lowercase(), Symbol { | |
| 44 | - name: name.to_string(), | |
| 45 | - kind: SymbolKind::DerivedType, | |
| 46 | - type_info: Some(TypeInfo::Derived(name.to_string())), | |
| 47 | - attrs: Default::default(), | |
| 48 | - defined_at: span, | |
| 49 | - scope: mod_id, | |
| 50 | - arg_names: vec![], | |
| 51 | - const_value: None, | |
| 52 | - }); | |
| 59 | + st.scope_mut(mod_id).symbols.insert( | |
| 60 | + name.to_lowercase(), | |
| 61 | + Symbol { | |
| 62 | + name: name.to_string(), | |
| 63 | + kind: SymbolKind::DerivedType, | |
| 64 | + type_info: Some(TypeInfo::Derived(name.to_string())), | |
| 65 | + attrs: Default::default(), | |
| 66 | + defined_at: span, | |
| 67 | + scope: mod_id, | |
| 68 | + arg_names: vec![], | |
| 69 | + const_value: None, | |
| 70 | + }, | |
| 71 | + ); | |
| 53 | 72 | } |
| 54 | 73 | |
| 55 | 74 | fn insert_proc(st: &mut SymbolTable, mod_id: ScopeId, name: &str) { |
| 56 | 75 | let span = builtin_span(); |
| 57 | - st.scope_mut(mod_id).symbols.insert(name.to_lowercase(), Symbol { | |
| 58 | - name: name.to_string(), | |
| 59 | - kind: SymbolKind::IntrinsicProc, | |
| 60 | - type_info: None, | |
| 61 | - attrs: SymbolAttrs { intrinsic: true, ..Default::default() }, | |
| 62 | - defined_at: span, | |
| 63 | - scope: mod_id, | |
| 64 | - arg_names: vec![], | |
| 65 | - const_value: None, | |
| 66 | - }); | |
| 76 | + st.scope_mut(mod_id).symbols.insert( | |
| 77 | + name.to_lowercase(), | |
| 78 | + Symbol { | |
| 79 | + name: name.to_string(), | |
| 80 | + kind: SymbolKind::IntrinsicProc, | |
| 81 | + type_info: None, | |
| 82 | + attrs: SymbolAttrs { | |
| 83 | + intrinsic: true, | |
| 84 | + ..Default::default() | |
| 85 | + }, | |
| 86 | + defined_at: span, | |
| 87 | + scope: mod_id, | |
| 88 | + arg_names: vec![], | |
| 89 | + const_value: None, | |
| 90 | + }, | |
| 91 | + ); | |
| 67 | 92 | } |
| 68 | 93 | |
| 69 | 94 | /// Populate the iso_c_binding module scope. |
@@ -74,36 +99,65 @@ fn register_iso_c_binding(st: &mut SymbolTable) { | ||
| 74 | 99 | // Each constant's VALUE is the kind number (e.g., c_int = 4 means kind=4 = 4 bytes). |
| 75 | 100 | let ik = |k: u8| TypeInfo::Integer { kind: Some(k) }; |
| 76 | 101 | for (name, kind) in [ |
| 77 | - ("c_int", 4u8), ("c_short", 2), ("c_long", 8), ("c_long_long", 8), | |
| 102 | + ("c_int", 4u8), | |
| 103 | + ("c_short", 2), | |
| 104 | + ("c_long", 8), | |
| 105 | + ("c_long_long", 8), | |
| 78 | 106 | ("c_signed_char", 1), |
| 79 | - ("c_int8_t", 1), ("c_int16_t", 2), ("c_int32_t", 4), ("c_int64_t", 8), | |
| 80 | - ("c_size_t", 8), ("c_intptr_t", 8), ("c_ptrdiff_t", 8), | |
| 107 | + ("c_int8_t", 1), | |
| 108 | + ("c_int16_t", 2), | |
| 109 | + ("c_int32_t", 4), | |
| 110 | + ("c_int64_t", 8), | |
| 111 | + ("c_size_t", 8), | |
| 112 | + ("c_intptr_t", 8), | |
| 113 | + ("c_ptrdiff_t", 8), | |
| 81 | 114 | ] { |
| 82 | 115 | insert_param_val(st, m, name, ik(4), Some(kind as i64)); |
| 83 | 116 | } |
| 84 | 117 | |
| 85 | 118 | // ---- Real kind parameters ---- |
| 86 | - for (name, kind) in [ | |
| 87 | - ("c_float", 4u8), ("c_double", 8), ("c_long_double", 8), | |
| 88 | - ] { | |
| 89 | - insert_param_val(st, m, name, TypeInfo::Integer { kind: Some(4) }, Some(kind as i64)); | |
| 119 | + for (name, kind) in [("c_float", 4u8), ("c_double", 8), ("c_long_double", 8)] { | |
| 120 | + insert_param_val( | |
| 121 | + st, | |
| 122 | + m, | |
| 123 | + name, | |
| 124 | + TypeInfo::Integer { kind: Some(4) }, | |
| 125 | + Some(kind as i64), | |
| 126 | + ); | |
| 90 | 127 | } |
| 91 | 128 | |
| 92 | 129 | // ---- Complex kind parameters ---- |
| 93 | 130 | for (name, kind) in [ |
| 94 | - ("c_float_complex", 4u8), ("c_double_complex", 8), ("c_long_double_complex", 8), | |
| 131 | + ("c_float_complex", 4u8), | |
| 132 | + ("c_double_complex", 8), | |
| 133 | + ("c_long_double_complex", 8), | |
| 95 | 134 | ] { |
| 96 | - insert_param_val(st, m, name, TypeInfo::Integer { kind: Some(4) }, Some(kind as i64)); | |
| 135 | + insert_param_val( | |
| 136 | + st, | |
| 137 | + m, | |
| 138 | + name, | |
| 139 | + TypeInfo::Integer { kind: Some(4) }, | |
| 140 | + Some(kind as i64), | |
| 141 | + ); | |
| 97 | 142 | } |
| 98 | 143 | |
| 99 | 144 | // ---- Character and logical kinds ---- |
| 100 | 145 | // c_char is an integer kind parameter (value = 1), not a character type. |
| 101 | 146 | insert_param_val(st, m, "c_char", ik(4), Some(1)); |
| 102 | - insert_param_val(st, m, "c_bool", TypeInfo::Integer { kind: Some(4) }, Some(1)); | |
| 147 | + insert_param_val( | |
| 148 | + st, | |
| 149 | + m, | |
| 150 | + "c_bool", | |
| 151 | + TypeInfo::Integer { kind: Some(4) }, | |
| 152 | + Some(1), | |
| 153 | + ); | |
| 103 | 154 | |
| 104 | 155 | // ---- Character constants (c_null_char, etc.) ---- |
| 105 | 156 | // Each constant's value is its ASCII byte code. |
| 106 | - let ck = TypeInfo::Character { len: Some(1), kind: Some(1) }; | |
| 157 | + let ck = TypeInfo::Character { | |
| 158 | + len: Some(1), | |
| 159 | + kind: Some(1), | |
| 160 | + }; | |
| 107 | 161 | for (name, ascii) in [ |
| 108 | 162 | ("c_null_char", 0i64), |
| 109 | 163 | ("c_alert", 7), |
@@ -126,7 +180,14 @@ fn register_iso_c_binding(st: &mut SymbolTable) { | ||
| 126 | 180 | insert_param(st, m, "c_null_funptr", ik(8)); |
| 127 | 181 | |
| 128 | 182 | // ---- Procedures ---- |
| 129 | - for name in ["c_loc", "c_funloc", "c_f_pointer", "c_f_procpointer", "c_associated", "c_sizeof"] { | |
| 183 | + for name in [ | |
| 184 | + "c_loc", | |
| 185 | + "c_funloc", | |
| 186 | + "c_f_pointer", | |
| 187 | + "c_f_procpointer", | |
| 188 | + "c_associated", | |
| 189 | + "c_sizeof", | |
| 190 | + ] { | |
| 130 | 191 | insert_proc(st, m, name); |
| 131 | 192 | } |
| 132 | 193 | |
@@ -204,10 +265,19 @@ fn register_ieee_stubs(st: &mut SymbolTable) { | ||
| 204 | 265 | insert_proc(st, m, "ieee_set_halting_mode"); |
| 205 | 266 | } |
| 206 | 267 | "ieee_features" => { |
| 207 | - for feat in ["ieee_datatype", "ieee_denormal", "ieee_divide", | |
| 208 | - "ieee_halting", "ieee_inexact_flag", "ieee_inf", | |
| 209 | - "ieee_invalid_flag", "ieee_nan", "ieee_rounding", | |
| 210 | - "ieee_sqrt", "ieee_underflow_flag"] { | |
| 268 | + for feat in [ | |
| 269 | + "ieee_datatype", | |
| 270 | + "ieee_denormal", | |
| 271 | + "ieee_divide", | |
| 272 | + "ieee_halting", | |
| 273 | + "ieee_inexact_flag", | |
| 274 | + "ieee_inf", | |
| 275 | + "ieee_invalid_flag", | |
| 276 | + "ieee_nan", | |
| 277 | + "ieee_rounding", | |
| 278 | + "ieee_sqrt", | |
| 279 | + "ieee_underflow_flag", | |
| 280 | + ] { | |
| 211 | 281 | insert_param(st, m, feat, TypeInfo::Logical { kind: Some(4) }); |
| 212 | 282 | } |
| 213 | 283 | } |
src/sema/mod.rsmodified@@ -3,10 +3,10 @@ | ||
| 3 | 3 | //! Symbol tables, scoping, type checking, module resolution, |
| 4 | 4 | //! interface validation, and standard conformance checks. |
| 5 | 5 | |
| 6 | -pub mod symtab; | |
| 6 | +pub mod amod; | |
| 7 | +pub mod intrinsic_modules; | |
| 7 | 8 | pub mod resolve; |
| 9 | +pub mod symtab; | |
| 10 | +pub mod type_layout; | |
| 8 | 11 | pub mod types; |
| 9 | 12 | pub mod validate; |
| 10 | -pub mod intrinsic_modules; | |
| 11 | -pub mod type_layout; | |
| 12 | -pub mod amod; | |
src/sema/symtab.rsmodified@@ -4,8 +4,8 @@ | ||
| 4 | 4 | //! mechanisms: local declaration, USE association, host association, and |
| 5 | 5 | //! IMPORT. Handles implicit typing and case-insensitive lookup. |
| 6 | 6 | |
| 7 | -use std::collections::HashMap; | |
| 8 | 7 | use crate::lexer::Span; |
| 8 | +use std::collections::HashMap; | |
| 9 | 9 | |
| 10 | 10 | /// Scope identifier — an index into the SymbolTable's scope list. |
| 11 | 11 | pub type ScopeId = usize; |
@@ -30,16 +30,20 @@ impl SymbolTable { | ||
| 30 | 30 | pending_access: HashMap::new(), |
| 31 | 31 | arg_order: Vec::new(), |
| 32 | 32 | }; |
| 33 | - Self { scopes: vec![global], current: 0 } | |
| 33 | + Self { | |
| 34 | + scopes: vec![global], | |
| 35 | + current: 0, | |
| 36 | + } | |
| 34 | 37 | } |
| 35 | 38 | } |
| 36 | 39 | |
| 37 | 40 | impl Default for SymbolTable { |
| 38 | - fn default() -> Self { Self::new() } | |
| 41 | + fn default() -> Self { | |
| 42 | + Self::new() | |
| 43 | + } | |
| 39 | 44 | } |
| 40 | 45 | |
| 41 | 46 | impl SymbolTable { |
| 42 | - | |
| 43 | 47 | /// Create a new child scope of the current scope. |
| 44 | 48 | pub fn push_scope(&mut self, kind: ScopeKind) -> ScopeId { |
| 45 | 49 | let id = self.scopes.len(); |
@@ -161,7 +165,10 @@ impl SymbolTable { | ||
| 161 | 165 | // 2. Direct USE association. |
| 162 | 166 | for assoc in &scope.use_associations { |
| 163 | 167 | if assoc.local_name == key { |
| 164 | - if let Some(sym) = self.scopes[assoc.source_scope].symbols.get(&assoc.original_name) { | |
| 168 | + if let Some(sym) = self.scopes[assoc.source_scope] | |
| 169 | + .symbols | |
| 170 | + .get(&assoc.original_name) | |
| 171 | + { | |
| 165 | 172 | if sym.attrs.access != Access::Private || assoc.is_submodule_access { |
| 166 | 173 | return Some(sym); |
| 167 | 174 | } |
@@ -232,7 +239,10 @@ impl SymbolTable { | ||
| 232 | 239 | for scope in &self.scopes { |
| 233 | 240 | for assoc in &scope.use_associations { |
| 234 | 241 | if assoc.local_name == key { |
| 235 | - if let Some(sym) = self.scopes[assoc.source_scope].symbols.get(&assoc.original_name) { | |
| 242 | + if let Some(sym) = self.scopes[assoc.source_scope] | |
| 243 | + .symbols | |
| 244 | + .get(&assoc.original_name) | |
| 245 | + { | |
| 236 | 246 | return Some(sym); |
| 237 | 247 | } |
| 238 | 248 | } |
@@ -284,7 +294,10 @@ impl SymbolTable { | ||
| 284 | 294 | pub fn set_implicit_rule(&mut self, start: char, end: char, itype: ImplicitType) { |
| 285 | 295 | let scope = &mut self.scopes[self.current]; |
| 286 | 296 | for c in start..=end { |
| 287 | - scope.implicit_rules.rules.insert(c.to_ascii_lowercase(), itype); | |
| 297 | + scope | |
| 298 | + .implicit_rules | |
| 299 | + .rules | |
| 300 | + .insert(c.to_ascii_lowercase(), itype); | |
| 288 | 301 | } |
| 289 | 302 | } |
| 290 | 303 | |
@@ -336,8 +349,14 @@ impl SymbolTable { | ||
| 336 | 349 | let key = name.to_lowercase(); |
| 337 | 350 | self.scopes.iter().find_map(|s| { |
| 338 | 351 | if let ScopeKind::Module(ref n) = s.kind { |
| 339 | - if n.to_lowercase() == key { Some(s.id) } else { None } | |
| 340 | - } else { None } | |
| 352 | + if n.to_lowercase() == key { | |
| 353 | + Some(s.id) | |
| 354 | + } else { | |
| 355 | + None | |
| 356 | + } | |
| 357 | + } else { | |
| 358 | + None | |
| 359 | + } | |
| 341 | 360 | }) |
| 342 | 361 | } |
| 343 | 362 | } |
@@ -509,15 +528,27 @@ impl ImplicitRules { | ||
| 509 | 528 | /// Standard Fortran default: I-N integer, everything else real. |
| 510 | 529 | pub fn default_fortran() -> Self { |
| 511 | 530 | let mut rules = HashMap::new(); |
| 512 | - for c in 'a'..='h' { rules.insert(c, ImplicitType::Real); } | |
| 513 | - for c in 'i'..='n' { rules.insert(c, ImplicitType::Integer); } | |
| 514 | - for c in 'o'..='z' { rules.insert(c, ImplicitType::Real); } | |
| 515 | - Self { none_type: false, none_external: false, rules } | |
| 531 | + for c in 'a'..='h' { | |
| 532 | + rules.insert(c, ImplicitType::Real); | |
| 533 | + } | |
| 534 | + for c in 'i'..='n' { | |
| 535 | + rules.insert(c, ImplicitType::Integer); | |
| 536 | + } | |
| 537 | + for c in 'o'..='z' { | |
| 538 | + rules.insert(c, ImplicitType::Real); | |
| 539 | + } | |
| 540 | + Self { | |
| 541 | + none_type: false, | |
| 542 | + none_external: false, | |
| 543 | + rules, | |
| 544 | + } | |
| 516 | 545 | } |
| 517 | 546 | |
| 518 | 547 | /// Look up the implicit type for a name's first letter. |
| 519 | 548 | pub fn type_for(&self, name: &str) -> Option<ImplicitType> { |
| 520 | - if self.none_type { return None; } | |
| 549 | + if self.none_type { | |
| 550 | + return None; | |
| 551 | + } | |
| 521 | 552 | let first = name.chars().next()?.to_ascii_lowercase(); |
| 522 | 553 | self.rules.get(&first).copied() |
| 523 | 554 | } |
@@ -543,7 +574,11 @@ pub struct SemaError { | ||
| 543 | 574 | |
| 544 | 575 | impl std::fmt::Display for SemaError { |
| 545 | 576 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 546 | - write!(f, "{}:{}: error: {}", self.span.start.line, self.span.start.col, self.msg) | |
| 577 | + write!( | |
| 578 | + f, | |
| 579 | + "{}:{}: error: {}", | |
| 580 | + self.span.start.line, self.span.start.col, self.msg | |
| 581 | + ) | |
| 547 | 582 | } |
| 548 | 583 | } |
| 549 | 584 | |
@@ -555,7 +590,11 @@ mod tests { | ||
| 555 | 590 | use crate::lexer::{Position, Span}; |
| 556 | 591 | |
| 557 | 592 | fn dummy_span() -> Span { |
| 558 | - Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } | |
| 593 | + Span { | |
| 594 | + file_id: 0, | |
| 595 | + start: Position { line: 1, col: 1 }, | |
| 596 | + end: Position { line: 1, col: 1 }, | |
| 597 | + } | |
| 559 | 598 | } |
| 560 | 599 | |
| 561 | 600 | fn make_symbol(name: &str, kind: SymbolKind) -> Symbol { |
@@ -596,7 +635,8 @@ mod tests { | ||
| 596 | 635 | fn case_insensitive_lookup() { |
| 597 | 636 | let mut st = SymbolTable::new(); |
| 598 | 637 | st.push_scope(ScopeKind::Program("main".into())); |
| 599 | - st.define(make_symbol("MyVar", SymbolKind::Variable)).unwrap(); | |
| 638 | + st.define(make_symbol("MyVar", SymbolKind::Variable)) | |
| 639 | + .unwrap(); | |
| 600 | 640 | assert!(st.lookup("myvar").is_some()); |
| 601 | 641 | assert!(st.lookup("MYVAR").is_some()); |
| 602 | 642 | assert!(st.lookup("MyVar").is_some()); |
@@ -707,7 +747,8 @@ mod tests { | ||
| 707 | 747 | let mut st = SymbolTable::new(); |
| 708 | 748 | |
| 709 | 749 | let mod_scope = st.push_scope(ScopeKind::Module("mymod".into())); |
| 710 | - st.define(make_symbol("original_name", SymbolKind::Variable)).unwrap(); | |
| 750 | + st.define(make_symbol("original_name", SymbolKind::Variable)) | |
| 751 | + .unwrap(); | |
| 711 | 752 | st.pop_scope(); |
| 712 | 753 | |
| 713 | 754 | st.push_scope(ScopeKind::Program("main".into())); |
@@ -775,11 +816,23 @@ mod tests { | ||
| 775 | 816 | fn implicit_default_rules() { |
| 776 | 817 | let st = SymbolTable::new(); |
| 777 | 818 | // i-n → integer. |
| 778 | - assert_eq!(st.scopes[0].implicit_rules.type_for("index"), Some(ImplicitType::Integer)); | |
| 779 | - assert_eq!(st.scopes[0].implicit_rules.type_for("jmax"), Some(ImplicitType::Integer)); | |
| 819 | + assert_eq!( | |
| 820 | + st.scopes[0].implicit_rules.type_for("index"), | |
| 821 | + Some(ImplicitType::Integer) | |
| 822 | + ); | |
| 823 | + assert_eq!( | |
| 824 | + st.scopes[0].implicit_rules.type_for("jmax"), | |
| 825 | + Some(ImplicitType::Integer) | |
| 826 | + ); | |
| 780 | 827 | // a-h, o-z → real. |
| 781 | - assert_eq!(st.scopes[0].implicit_rules.type_for("x"), Some(ImplicitType::Real)); | |
| 782 | - assert_eq!(st.scopes[0].implicit_rules.type_for("alpha"), Some(ImplicitType::Real)); | |
| 828 | + assert_eq!( | |
| 829 | + st.scopes[0].implicit_rules.type_for("x"), | |
| 830 | + Some(ImplicitType::Real) | |
| 831 | + ); | |
| 832 | + assert_eq!( | |
| 833 | + st.scopes[0].implicit_rules.type_for("alpha"), | |
| 834 | + Some(ImplicitType::Real) | |
| 835 | + ); | |
| 783 | 836 | } |
| 784 | 837 | |
| 785 | 838 | #[test] |
@@ -797,7 +850,10 @@ mod tests { | ||
| 797 | 850 | st.push_scope(ScopeKind::Program("main".into())); |
| 798 | 851 | st.set_implicit_rule('a', 'z', ImplicitType::DoublePrecision); |
| 799 | 852 | assert_eq!(st.implicit_type("x"), Some(ImplicitType::DoublePrecision)); |
| 800 | - assert_eq!(st.implicit_type("index"), Some(ImplicitType::DoublePrecision)); | |
| 853 | + assert_eq!( | |
| 854 | + st.implicit_type("index"), | |
| 855 | + Some(ImplicitType::DoublePrecision) | |
| 856 | + ); | |
| 801 | 857 | } |
| 802 | 858 | |
| 803 | 859 | // ---- Module scope finding ---- |
src/sema/type_layout.rsmodified@@ -3,8 +3,8 @@ | ||
| 3 | 3 | //! Computes field offsets, alignment, and total size for derived types |
| 4 | 4 | //! using natural alignment rules (same as C struct layout on ARM64). |
| 5 | 5 | |
| 6 | -use std::collections::HashMap; | |
| 7 | 6 | use super::symtab::TypeInfo; |
| 7 | +use std::collections::HashMap; | |
| 8 | 8 | |
| 9 | 9 | /// Layout of a single field in a derived type. |
| 10 | 10 | #[derive(Debug, Clone)] |
@@ -56,7 +56,9 @@ impl TypeLayout { | ||
| 56 | 56 | /// Look up a type-bound procedure by method name. |
| 57 | 57 | pub fn bound_proc(&self, name: &str) -> Option<&BoundProc> { |
| 58 | 58 | let key = name.to_lowercase(); |
| 59 | - self.bound_procs.iter().find(|p| p.method_name.to_lowercase() == key) | |
| 59 | + self.bound_procs | |
| 60 | + .iter() | |
| 61 | + .find(|p| p.method_name.to_lowercase() == key) | |
| 60 | 62 | } |
| 61 | 63 | } |
| 62 | 64 | |
@@ -69,7 +71,10 @@ pub struct TypeLayoutRegistry { | ||
| 69 | 71 | |
| 70 | 72 | impl TypeLayoutRegistry { |
| 71 | 73 | pub fn new() -> Self { |
| 72 | - Self { layouts: HashMap::new(), next_tag: 1 } | |
| 74 | + Self { | |
| 75 | + layouts: HashMap::new(), | |
| 76 | + next_tag: 1, | |
| 77 | + } | |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | 80 | pub fn alloc_tag(&mut self) -> u64 { |
@@ -202,7 +207,11 @@ fn eval_const_int_expr( | ||
| 202 | 207 | }; |
| 203 | 208 | match &e.node { |
| 204 | 209 | Expr::RealLiteral { text, .. } => { |
| 205 | - Some(if text.contains('d') || text.contains('D') { 8 } else { 4 }) | |
| 210 | + Some(if text.contains('d') || text.contains('D') { | |
| 211 | + 8 | |
| 212 | + } else { | |
| 213 | + 4 | |
| 214 | + }) | |
| 206 | 215 | } |
| 207 | 216 | Expr::IntegerLiteral { .. } => Some(4), |
| 208 | 217 | _ => None, |
@@ -246,52 +255,47 @@ fn type_spec_to_type_info( | ||
| 246 | 255 | ts: &crate::ast::decl::TypeSpec, |
| 247 | 256 | const_params: &HashMap<String, i64>, |
| 248 | 257 | ) -> TypeInfo { |
| 249 | - use crate::ast::decl::{TypeSpec, KindSelector, LenSpec}; | |
| 258 | + use crate::ast::decl::{KindSelector, LenSpec, TypeSpec}; | |
| 250 | 259 | |
| 251 | 260 | match ts { |
| 252 | 261 | TypeSpec::Integer(kind) => TypeInfo::Integer { |
| 253 | - kind: kind | |
| 254 | - .as_ref() | |
| 255 | - .and_then(|k| match k { | |
| 256 | - KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 257 | - eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 258 | - } | |
| 259 | - }), | |
| 262 | + kind: kind.as_ref().and_then(|k| match k { | |
| 263 | + KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 264 | + eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 265 | + } | |
| 266 | + }), | |
| 260 | 267 | }, |
| 261 | 268 | TypeSpec::Real(kind) => TypeInfo::Real { |
| 262 | - kind: kind | |
| 263 | - .as_ref() | |
| 264 | - .and_then(|k| match k { | |
| 265 | - KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 266 | - eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 267 | - } | |
| 268 | - }), | |
| 269 | + kind: kind.as_ref().and_then(|k| match k { | |
| 270 | + KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 271 | + eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 272 | + } | |
| 273 | + }), | |
| 269 | 274 | }, |
| 270 | 275 | TypeSpec::DoublePrecision => TypeInfo::DoublePrecision, |
| 271 | 276 | TypeSpec::Complex(kind) => TypeInfo::Complex { |
| 272 | - kind: kind | |
| 273 | - .as_ref() | |
| 274 | - .and_then(|k| match k { | |
| 275 | - KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 276 | - eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 277 | - } | |
| 278 | - }), | |
| 277 | + kind: kind.as_ref().and_then(|k| match k { | |
| 278 | + KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 279 | + eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 280 | + } | |
| 281 | + }), | |
| 279 | 282 | }, |
| 280 | 283 | TypeSpec::DoubleComplex => TypeInfo::Complex { kind: Some(8) }, |
| 281 | 284 | TypeSpec::Logical(kind) => TypeInfo::Logical { |
| 282 | - kind: kind | |
| 283 | - .as_ref() | |
| 284 | - .and_then(|k| match k { | |
| 285 | - KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 286 | - eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 287 | - } | |
| 288 | - }), | |
| 285 | + kind: kind.as_ref().and_then(|k| match k { | |
| 286 | + KindSelector::Expr(e) | KindSelector::Star(e) => { | |
| 287 | + eval_const_int_expr(e, const_params).and_then(|v| u8::try_from(v).ok()) | |
| 288 | + } | |
| 289 | + }), | |
| 289 | 290 | }, |
| 290 | 291 | TypeSpec::Character(sel) => { |
| 291 | - let len = sel.as_ref().and_then(|s| s.len.as_ref()).and_then(|l| match l { | |
| 292 | - LenSpec::Expr(e) => eval_const_int_expr(e, const_params), | |
| 293 | - _ => None, | |
| 294 | - }); | |
| 292 | + let len = sel | |
| 293 | + .as_ref() | |
| 294 | + .and_then(|s| s.len.as_ref()) | |
| 295 | + .and_then(|l| match l { | |
| 296 | + LenSpec::Expr(e) => eval_const_int_expr(e, const_params), | |
| 297 | + _ => None, | |
| 298 | + }); | |
| 295 | 299 | let kind = sel |
| 296 | 300 | .as_ref() |
| 297 | 301 | .and_then(|s| s.kind.as_ref()) |
@@ -331,10 +335,21 @@ pub fn compute_layout( | ||
| 331 | 335 | |
| 332 | 336 | // Process component declarations. |
| 333 | 337 | for decl in components { |
| 334 | - if let crate::ast::decl::Decl::TypeDecl { type_spec, attrs, entities } = &decl.node { | |
| 335 | - let is_allocatable = attrs.iter().any(|a| matches!(a, crate::ast::decl::Attribute::Allocatable)); | |
| 336 | - let is_pointer = attrs.iter().any(|a| matches!(a, crate::ast::decl::Attribute::Pointer)); | |
| 337 | - let is_target = attrs.iter().any(|a| matches!(a, crate::ast::decl::Attribute::Target)); | |
| 338 | + if let crate::ast::decl::Decl::TypeDecl { | |
| 339 | + type_spec, | |
| 340 | + attrs, | |
| 341 | + entities, | |
| 342 | + } = &decl.node | |
| 343 | + { | |
| 344 | + let is_allocatable = attrs | |
| 345 | + .iter() | |
| 346 | + .any(|a| matches!(a, crate::ast::decl::Attribute::Allocatable)); | |
| 347 | + let is_pointer = attrs | |
| 348 | + .iter() | |
| 349 | + .any(|a| matches!(a, crate::ast::decl::Attribute::Pointer)); | |
| 350 | + let is_target = attrs | |
| 351 | + .iter() | |
| 352 | + .any(|a| matches!(a, crate::ast::decl::Attribute::Target)); | |
| 338 | 353 | |
| 339 | 354 | let ti = type_spec_to_type_info(type_spec, const_params); |
| 340 | 355 | for entity in entities { |
@@ -343,20 +358,22 @@ pub fn compute_layout( | ||
| 343 | 358 | } else { |
| 344 | 359 | eval_explicit_array_dims(entity.array_spec.as_ref(), const_params) |
| 345 | 360 | }; |
| 346 | - let (elem_size, elem_align) = if matches!( | |
| 347 | - &ti, | |
| 348 | - TypeInfo::Character { len: None, .. } | |
| 349 | - ) && (is_allocatable || is_pointer) && entity.array_spec.is_none() { | |
| 350 | - (32, 8) // StringDescriptor for deferred-length scalar char components | |
| 351 | - } else if is_allocatable || is_pointer { | |
| 352 | - (384, 8) // ArrayDescriptor size for allocatable/pointer components | |
| 353 | - } else if let TypeInfo::Derived(ref dname) = ti { | |
| 354 | - registry.get(dname) | |
| 355 | - .map(|l| (l.size, l.align)) | |
| 356 | - .unwrap_or((8, 8)) | |
| 357 | - } else { | |
| 358 | - size_of_type(&ti) | |
| 359 | - }; | |
| 361 | + let (elem_size, elem_align) = | |
| 362 | + if matches!(&ti, TypeInfo::Character { len: None, .. }) | |
| 363 | + && (is_allocatable || is_pointer) | |
| 364 | + && entity.array_spec.is_none() | |
| 365 | + { | |
| 366 | + (32, 8) // StringDescriptor for deferred-length scalar char components | |
| 367 | + } else if is_allocatable || is_pointer { | |
| 368 | + (384, 8) // ArrayDescriptor size for allocatable/pointer components | |
| 369 | + } else if let TypeInfo::Derived(ref dname) = ti { | |
| 370 | + registry | |
| 371 | + .get(dname) | |
| 372 | + .map(|l| (l.size, l.align)) | |
| 373 | + .unwrap_or((8, 8)) | |
| 374 | + } else { | |
| 375 | + size_of_type(&ti) | |
| 376 | + }; | |
| 360 | 377 | // Pad to alignment. |
| 361 | 378 | let padding = (elem_align - (offset % elem_align)) % elem_align; |
| 362 | 379 | offset += padding; |
@@ -392,15 +409,18 @@ pub fn compute_layout( | ||
| 392 | 409 | } |
| 393 | 410 | |
| 394 | 411 | // Collect type-bound procedure mappings. |
| 395 | - let bound_procs: Vec<BoundProc> = type_bound_procs.iter().map(|tbp| { | |
| 396 | - let target = tbp.binding.as_deref().unwrap_or(&tbp.name); | |
| 397 | - let nopass = tbp.attrs.iter().any(|a| a.eq_ignore_ascii_case("nopass")); | |
| 398 | - BoundProc { | |
| 399 | - method_name: tbp.name.clone(), | |
| 400 | - target_name: target.to_string(), | |
| 401 | - nopass, | |
| 402 | - } | |
| 403 | - }).collect(); | |
| 412 | + let bound_procs: Vec<BoundProc> = type_bound_procs | |
| 413 | + .iter() | |
| 414 | + .map(|tbp| { | |
| 415 | + let target = tbp.binding.as_deref().unwrap_or(&tbp.name); | |
| 416 | + let nopass = tbp.attrs.iter().any(|a| a.eq_ignore_ascii_case("nopass")); | |
| 417 | + BoundProc { | |
| 418 | + method_name: tbp.name.clone(), | |
| 419 | + target_name: target.to_string(), | |
| 420 | + nopass, | |
| 421 | + } | |
| 422 | + }) | |
| 423 | + .collect(); | |
| 404 | 424 | |
| 405 | 425 | TypeLayout { |
| 406 | 426 | name: type_name.to_string(), |
@@ -425,7 +445,13 @@ mod tests { | ||
| 425 | 445 | assert_eq!(size_of_type(&TypeInfo::Real { kind: Some(4) }), (4, 4)); |
| 426 | 446 | assert_eq!(size_of_type(&TypeInfo::Real { kind: Some(8) }), (8, 8)); |
| 427 | 447 | assert_eq!(size_of_type(&TypeInfo::Logical { kind: Some(4) }), (4, 4)); |
| 428 | - assert_eq!(size_of_type(&TypeInfo::Character { len: Some(10), kind: None }), (10, 1)); | |
| 448 | + assert_eq!( | |
| 449 | + size_of_type(&TypeInfo::Character { | |
| 450 | + len: Some(10), | |
| 451 | + kind: None | |
| 452 | + }), | |
| 453 | + (10, 1) | |
| 454 | + ); | |
| 429 | 455 | assert_eq!(size_of_type(&TypeInfo::Complex { kind: Some(4) }), (8, 4)); |
| 430 | 456 | assert_eq!(size_of_type(&TypeInfo::Complex { kind: Some(8) }), (16, 8)); |
| 431 | 457 | } |
@@ -437,8 +463,26 @@ mod tests { | ||
| 437 | 463 | size: 8, |
| 438 | 464 | align: 4, |
| 439 | 465 | fields: vec![ |
| 440 | - FieldLayout { name: "x".into(), offset: 0, size: 4, dims: vec![], type_info: TypeInfo::Real { kind: Some(4) }, allocatable: false, pointer: false, target: false }, | |
| 441 | - FieldLayout { name: "y".into(), offset: 4, size: 4, dims: vec![], type_info: TypeInfo::Real { kind: Some(4) }, allocatable: false, pointer: false, target: false }, | |
| 466 | + FieldLayout { | |
| 467 | + name: "x".into(), | |
| 468 | + offset: 0, | |
| 469 | + size: 4, | |
| 470 | + dims: vec![], | |
| 471 | + type_info: TypeInfo::Real { kind: Some(4) }, | |
| 472 | + allocatable: false, | |
| 473 | + pointer: false, | |
| 474 | + target: false, | |
| 475 | + }, | |
| 476 | + FieldLayout { | |
| 477 | + name: "y".into(), | |
| 478 | + offset: 4, | |
| 479 | + size: 4, | |
| 480 | + dims: vec![], | |
| 481 | + type_info: TypeInfo::Real { kind: Some(4) }, | |
| 482 | + allocatable: false, | |
| 483 | + pointer: false, | |
| 484 | + target: false, | |
| 485 | + }, | |
| 442 | 486 | ], |
| 443 | 487 | bound_procs: vec![], |
| 444 | 488 | final_procs: vec![], |
@@ -463,9 +507,36 @@ mod tests { | ||
| 463 | 507 | size: 24, |
| 464 | 508 | align: 8, |
| 465 | 509 | fields: vec![ |
| 466 | - FieldLayout { name: "a".into(), offset: 0, size: 1, dims: vec![], type_info: TypeInfo::Integer { kind: Some(1) }, allocatable: false, pointer: false, target: false }, | |
| 467 | - FieldLayout { name: "b".into(), offset: 8, size: 8, dims: vec![], type_info: TypeInfo::Real { kind: Some(8) }, allocatable: false, pointer: false, target: false }, | |
| 468 | - FieldLayout { name: "c".into(), offset: 16, size: 4, dims: vec![], type_info: TypeInfo::Integer { kind: Some(4) }, allocatable: false, pointer: false, target: false }, | |
| 510 | + FieldLayout { | |
| 511 | + name: "a".into(), | |
| 512 | + offset: 0, | |
| 513 | + size: 1, | |
| 514 | + dims: vec![], | |
| 515 | + type_info: TypeInfo::Integer { kind: Some(1) }, | |
| 516 | + allocatable: false, | |
| 517 | + pointer: false, | |
| 518 | + target: false, | |
| 519 | + }, | |
| 520 | + FieldLayout { | |
| 521 | + name: "b".into(), | |
| 522 | + offset: 8, | |
| 523 | + size: 8, | |
| 524 | + dims: vec![], | |
| 525 | + type_info: TypeInfo::Real { kind: Some(8) }, | |
| 526 | + allocatable: false, | |
| 527 | + pointer: false, | |
| 528 | + target: false, | |
| 529 | + }, | |
| 530 | + FieldLayout { | |
| 531 | + name: "c".into(), | |
| 532 | + offset: 16, | |
| 533 | + size: 4, | |
| 534 | + dims: vec![], | |
| 535 | + type_info: TypeInfo::Integer { kind: Some(4) }, | |
| 536 | + allocatable: false, | |
| 537 | + pointer: false, | |
| 538 | + target: false, | |
| 539 | + }, | |
| 469 | 540 | ], |
| 470 | 541 | bound_procs: vec![], |
| 471 | 542 | final_procs: vec![], |
@@ -503,18 +574,25 @@ mod tests { | ||
| 503 | 574 | use crate::ast::decl::*; |
| 504 | 575 | use crate::ast::Spanned; |
| 505 | 576 | let pos = crate::lexer::Position { line: 0, col: 0 }; |
| 506 | - let span = crate::lexer::Span { start: pos, end: pos, file_id: 0 }; | |
| 507 | - Spanned::new(Decl::TypeDecl { | |
| 508 | - type_spec: ts, | |
| 509 | - attrs: vec![], | |
| 510 | - entities: vec![EntityDecl { | |
| 511 | - name: name.to_string(), | |
| 512 | - array_spec: None, | |
| 513 | - char_len: None, | |
| 514 | - init: None, | |
| 515 | - ptr_init: None, | |
| 516 | - }], | |
| 517 | - }, span) | |
| 577 | + let span = crate::lexer::Span { | |
| 578 | + start: pos, | |
| 579 | + end: pos, | |
| 580 | + file_id: 0, | |
| 581 | + }; | |
| 582 | + Spanned::new( | |
| 583 | + Decl::TypeDecl { | |
| 584 | + type_spec: ts, | |
| 585 | + attrs: vec![], | |
| 586 | + entities: vec![EntityDecl { | |
| 587 | + name: name.to_string(), | |
| 588 | + array_spec: None, | |
| 589 | + char_len: None, | |
| 590 | + init: None, | |
| 591 | + ptr_init: None, | |
| 592 | + }], | |
| 593 | + }, | |
| 594 | + span, | |
| 595 | + ) | |
| 518 | 596 | } |
| 519 | 597 | |
| 520 | 598 | fn empty_params() -> std::collections::HashMap<String, i64> { |
@@ -544,14 +622,22 @@ mod tests { | ||
| 544 | 622 | // a(1) + 7pad + b(8) = 16 |
| 545 | 623 | let reg = TypeLayoutRegistry::new(); |
| 546 | 624 | let components = vec![ |
| 547 | - make_component("a", crate::ast::decl::TypeSpec::Integer( | |
| 548 | - Some(crate::ast::decl::KindSelector::Expr( | |
| 625 | + make_component( | |
| 626 | + "a", | |
| 627 | + crate::ast::decl::TypeSpec::Integer(Some(crate::ast::decl::KindSelector::Expr( | |
| 549 | 628 | crate::ast::Spanned::new( |
| 550 | - crate::ast::expr::Expr::IntegerLiteral { text: "1".into(), kind: None }, | |
| 551 | - crate::lexer::Span { start: crate::lexer::Position { line: 0, col: 0 }, end: crate::lexer::Position { line: 0, col: 0 }, file_id: 0 }, | |
| 552 | - ) | |
| 553 | - )) | |
| 554 | - )), | |
| 629 | + crate::ast::expr::Expr::IntegerLiteral { | |
| 630 | + text: "1".into(), | |
| 631 | + kind: None, | |
| 632 | + }, | |
| 633 | + crate::lexer::Span { | |
| 634 | + start: crate::lexer::Position { line: 0, col: 0 }, | |
| 635 | + end: crate::lexer::Position { line: 0, col: 0 }, | |
| 636 | + file_id: 0, | |
| 637 | + }, | |
| 638 | + ), | |
| 639 | + ))), | |
| 640 | + ), | |
| 555 | 641 | make_component("b", crate::ast::decl::TypeSpec::DoublePrecision), |
| 556 | 642 | ]; |
| 557 | 643 | let layout = compute_layout("padded", &[], &[], &components, None, ®, &empty_params()); |
@@ -568,13 +654,24 @@ mod tests { | ||
| 568 | 654 | // type :: base; integer :: x; end type |
| 569 | 655 | // type, extends(base) :: child; real :: y; end type |
| 570 | 656 | let reg = TypeLayoutRegistry::new(); |
| 571 | - let base_comps = vec![make_component("x", crate::ast::decl::TypeSpec::Integer(None))]; | |
| 572 | - let base_layout = compute_layout("base", &[], &[], &base_comps, None, ®, &empty_params()); | |
| 657 | + let base_comps = vec![make_component( | |
| 658 | + "x", | |
| 659 | + crate::ast::decl::TypeSpec::Integer(None), | |
| 660 | + )]; | |
| 661 | + let base_layout = | |
| 662 | + compute_layout("base", &[], &[], &base_comps, None, ®, &empty_params()); | |
| 573 | 663 | assert_eq!(base_layout.size, 4); |
| 574 | 664 | |
| 575 | 665 | let child_comps = vec![make_component("y", crate::ast::decl::TypeSpec::Real(None))]; |
| 576 | - let child_layout = | |
| 577 | - compute_layout("child", &[], &[], &child_comps, Some(&base_layout), ®, &empty_params()); | |
| 666 | + let child_layout = compute_layout( | |
| 667 | + "child", | |
| 668 | + &[], | |
| 669 | + &[], | |
| 670 | + &child_comps, | |
| 671 | + Some(&base_layout), | |
| 672 | + ®, | |
| 673 | + &empty_params(), | |
| 674 | + ); | |
| 578 | 675 | assert_eq!(child_layout.fields.len(), 2); // x + y |
| 579 | 676 | assert_eq!(child_layout.field("x").unwrap().offset, 0); // inherited |
| 580 | 677 | assert_eq!(child_layout.field("y").unwrap().offset, 4); // appended |
src/sema/types.rsmodified@@ -6,26 +6,26 @@ | ||
| 6 | 6 | /// A Fortran type. |
| 7 | 7 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 8 | 8 | pub enum FortranType { |
| 9 | - Integer { kind: u8 }, // kind in bytes: 1, 2, 4, 8, 16 | |
| 10 | - Real { kind: u8 }, // 4 (single), 8 (double), 16 (quad) | |
| 11 | - Complex { kind: u8 }, // 4, 8, 16 | |
| 12 | - Logical { kind: u8 }, // 1, 2, 4, 8 | |
| 9 | + Integer { kind: u8 }, // kind in bytes: 1, 2, 4, 8, 16 | |
| 10 | + Real { kind: u8 }, // 4 (single), 8 (double), 16 (quad) | |
| 11 | + Complex { kind: u8 }, // 4, 8, 16 | |
| 12 | + Logical { kind: u8 }, // 1, 2, 4, 8 | |
| 13 | 13 | Character { kind: u8, len: CharLen }, |
| 14 | 14 | Derived { name: String }, |
| 15 | - ClassOf { base: String }, // CLASS(t) | |
| 16 | - UnlimitedPoly, // CLASS(*) | |
| 17 | - AssumedType, // TYPE(*) | |
| 18 | - Void, // subroutine (no return value) | |
| 19 | - Unknown, // not yet determined | |
| 15 | + ClassOf { base: String }, // CLASS(t) | |
| 16 | + UnlimitedPoly, // CLASS(*) | |
| 17 | + AssumedType, // TYPE(*) | |
| 18 | + Void, // subroutine (no return value) | |
| 19 | + Unknown, // not yet determined | |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | 22 | /// Character length. |
| 23 | 23 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 24 | 24 | pub enum CharLen { |
| 25 | 25 | Known(i64), |
| 26 | - Assumed, // len=* | |
| 27 | - Deferred, // len=: | |
| 28 | - Unknown, // runtime expression | |
| 26 | + Assumed, // len=* | |
| 27 | + Deferred, // len=: | |
| 28 | + Unknown, // runtime expression | |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | 31 | /// Array type information — wraps an element type with rank and shape. |
@@ -62,35 +62,59 @@ pub enum Bound { | ||
| 62 | 62 | |
| 63 | 63 | impl FortranType { |
| 64 | 64 | /// Default integer: integer(4). |
| 65 | - pub fn default_integer() -> Self { Self::Integer { kind: 4 } } | |
| 65 | + pub fn default_integer() -> Self { | |
| 66 | + Self::Integer { kind: 4 } | |
| 67 | + } | |
| 66 | 68 | /// Default real: real(4). |
| 67 | - pub fn default_real() -> Self { Self::Real { kind: 4 } } | |
| 69 | + pub fn default_real() -> Self { | |
| 70 | + Self::Real { kind: 4 } | |
| 71 | + } | |
| 68 | 72 | /// Default double precision: real(8). |
| 69 | - pub fn double_precision() -> Self { Self::Real { kind: 8 } } | |
| 73 | + pub fn double_precision() -> Self { | |
| 74 | + Self::Real { kind: 8 } | |
| 75 | + } | |
| 70 | 76 | /// Default complex: complex(4). |
| 71 | - pub fn default_complex() -> Self { Self::Complex { kind: 4 } } | |
| 77 | + pub fn default_complex() -> Self { | |
| 78 | + Self::Complex { kind: 4 } | |
| 79 | + } | |
| 72 | 80 | /// Default logical: logical(4). |
| 73 | - pub fn default_logical() -> Self { Self::Logical { kind: 4 } } | |
| 81 | + pub fn default_logical() -> Self { | |
| 82 | + Self::Logical { kind: 4 } | |
| 83 | + } | |
| 74 | 84 | /// Default character: character(1, len=1). |
| 75 | - pub fn default_character() -> Self { Self::Character { kind: 1, len: CharLen::Known(1) } } | |
| 85 | + pub fn default_character() -> Self { | |
| 86 | + Self::Character { | |
| 87 | + kind: 1, | |
| 88 | + len: CharLen::Known(1), | |
| 89 | + } | |
| 90 | + } | |
| 76 | 91 | |
| 77 | 92 | /// Is this a numeric type (integer, real, or complex)? |
| 78 | 93 | pub fn is_numeric(&self) -> bool { |
| 79 | - matches!(self, Self::Integer { .. } | Self::Real { .. } | Self::Complex { .. }) | |
| 94 | + matches!( | |
| 95 | + self, | |
| 96 | + Self::Integer { .. } | Self::Real { .. } | Self::Complex { .. } | |
| 97 | + ) | |
| 80 | 98 | } |
| 81 | 99 | |
| 82 | 100 | /// Is this a logical type? |
| 83 | - pub fn is_logical(&self) -> bool { matches!(self, Self::Logical { .. }) } | |
| 101 | + pub fn is_logical(&self) -> bool { | |
| 102 | + matches!(self, Self::Logical { .. }) | |
| 103 | + } | |
| 84 | 104 | |
| 85 | 105 | /// Is this a character type? |
| 86 | - pub fn is_character(&self) -> bool { matches!(self, Self::Character { .. }) } | |
| 106 | + pub fn is_character(&self) -> bool { | |
| 107 | + matches!(self, Self::Character { .. }) | |
| 108 | + } | |
| 87 | 109 | |
| 88 | 110 | /// Get the kind (size in bytes) for numeric/logical types. |
| 89 | 111 | pub fn kind(&self) -> Option<u8> { |
| 90 | 112 | match self { |
| 91 | - Self::Integer { kind } | Self::Real { kind } | | |
| 92 | - Self::Complex { kind } | Self::Logical { kind } | | |
| 93 | - Self::Character { kind, .. } => Some(*kind), | |
| 113 | + Self::Integer { kind } | |
| 114 | + | Self::Real { kind } | |
| 115 | + | Self::Complex { kind } | |
| 116 | + | Self::Logical { kind } | |
| 117 | + | Self::Character { kind, .. } => Some(*kind), | |
| 94 | 118 | _ => None, |
| 95 | 119 | } |
| 96 | 120 | } |
@@ -125,13 +149,13 @@ pub fn arithmetic_result_type(left: &FortranType, right: &FortranType) -> Option | ||
| 125 | 149 | // real + complex → max(kind_a, kind_b) |
| 126 | 150 | let result_rank = left_rank.max(right_rank); |
| 127 | 151 | let result_kind = if left_rank == right_rank { |
| 128 | - left_kind.max(right_kind) // same type class: max kind | |
| 152 | + left_kind.max(right_kind) // same type class: max kind | |
| 129 | 153 | } else if left_rank == 1 { |
| 130 | - right_kind // integer + real/complex: use real/complex kind | |
| 154 | + right_kind // integer + real/complex: use real/complex kind | |
| 131 | 155 | } else if right_rank == 1 { |
| 132 | - left_kind // real/complex + integer: use real/complex kind | |
| 156 | + left_kind // real/complex + integer: use real/complex kind | |
| 133 | 157 | } else { |
| 134 | - left_kind.max(right_kind) // real + complex: max kind | |
| 158 | + left_kind.max(right_kind) // real + complex: max kind | |
| 135 | 159 | }; |
| 136 | 160 | |
| 137 | 161 | Some(match result_rank { |
@@ -151,7 +175,9 @@ pub fn power_result_type(base: &FortranType, exponent: &FortranType) -> Option<F | ||
| 151 | 175 | return None; |
| 152 | 176 | } |
| 153 | 177 | // If both integer, result is integer with max kind. |
| 154 | - if matches!(base, FortranType::Integer { .. }) && matches!(exponent, FortranType::Integer { .. }) { | |
| 178 | + if matches!(base, FortranType::Integer { .. }) | |
| 179 | + && matches!(exponent, FortranType::Integer { .. }) | |
| 180 | + { | |
| 155 | 181 | let kind = base.kind().unwrap_or(4).max(exponent.kind().unwrap_or(4)); |
| 156 | 182 | return Some(FortranType::Integer { kind }); |
| 157 | 183 | } |
@@ -186,17 +212,23 @@ pub fn concat_result_type(left: &FortranType, right: &FortranType) -> Option<For | ||
| 186 | 212 | (CharLen::Known(a), CharLen::Known(b)) => CharLen::Known(a + b), |
| 187 | 213 | _ => CharLen::Unknown, |
| 188 | 214 | }; |
| 189 | - Some(FortranType::Character { kind: left_kind, len: result_len }) | |
| 215 | + Some(FortranType::Character { | |
| 216 | + kind: left_kind, | |
| 217 | + len: result_len, | |
| 218 | + }) | |
| 190 | 219 | } |
| 191 | 220 | |
| 192 | 221 | /// Check if an implicit conversion is needed from `from` to `to`. |
| 193 | 222 | /// Returns None if no conversion needed, or the target type if needed. |
| 194 | 223 | /// Complex→non-complex requires explicit conversion per standard. |
| 195 | 224 | pub fn needs_conversion(from: &FortranType, to: &FortranType) -> Option<FortranType> { |
| 196 | - if from == to { return None; } | |
| 225 | + if from == to { | |
| 226 | + return None; | |
| 227 | + } | |
| 197 | 228 | if from.is_numeric() && to.is_numeric() { |
| 198 | 229 | // Complex → non-complex requires explicit conversion (REAL(), INT()). |
| 199 | - if matches!(from, FortranType::Complex { .. }) && !matches!(to, FortranType::Complex { .. }) { | |
| 230 | + if matches!(from, FortranType::Complex { .. }) && !matches!(to, FortranType::Complex { .. }) | |
| 231 | + { | |
| 200 | 232 | return None; |
| 201 | 233 | } |
| 202 | 234 | return Some(to.clone()); |
@@ -207,7 +239,9 @@ pub fn needs_conversion(from: &FortranType, to: &FortranType) -> Option<FortranT | ||
| 207 | 239 | /// Logical operators require logical operands and produce logical. |
| 208 | 240 | pub fn logical_result_type(operand: &FortranType) -> Option<FortranType> { |
| 209 | 241 | if operand.is_logical() { |
| 210 | - Some(FortranType::Logical { kind: operand.kind().unwrap_or(4) }) | |
| 242 | + Some(FortranType::Logical { | |
| 243 | + kind: operand.kind().unwrap_or(4), | |
| 244 | + }) | |
| 211 | 245 | } else { |
| 212 | 246 | None // Error: logical operator applied to non-logical type |
| 213 | 247 | } |
@@ -224,7 +258,11 @@ pub fn binary_logical_result_type(left: &FortranType, right: &FortranType) -> Op | ||
| 224 | 258 | } |
| 225 | 259 | |
| 226 | 260 | /// Compute the result type of any binary operation. |
| 227 | -pub fn binary_op_result_type(op: &crate::ast::expr::BinaryOp, left: &FortranType, right: &FortranType) -> Option<FortranType> { | |
| 261 | +pub fn binary_op_result_type( | |
| 262 | + op: &crate::ast::expr::BinaryOp, | |
| 263 | + left: &FortranType, | |
| 264 | + right: &FortranType, | |
| 265 | +) -> Option<FortranType> { | |
| 228 | 266 | use crate::ast::expr::BinaryOp; |
| 229 | 267 | match op { |
| 230 | 268 | BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div => { |
@@ -232,9 +270,10 @@ pub fn binary_op_result_type(op: &crate::ast::expr::BinaryOp, left: &FortranType | ||
| 232 | 270 | } |
| 233 | 271 | BinaryOp::Pow => power_result_type(left, right), |
| 234 | 272 | BinaryOp::Concat => concat_result_type(left, right), |
| 235 | - BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | | |
| 236 | - BinaryOp::Gt | BinaryOp::Ge => { | |
| 237 | - if (left.is_numeric() && right.is_numeric()) || (left.is_character() && right.is_character()) { | |
| 273 | + BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => { | |
| 274 | + if (left.is_numeric() && right.is_numeric()) | |
| 275 | + || (left.is_character() && right.is_character()) | |
| 276 | + { | |
| 238 | 277 | Some(comparison_result_type()) |
| 239 | 278 | } else { |
| 240 | 279 | None |
@@ -248,11 +287,18 @@ pub fn binary_op_result_type(op: &crate::ast::expr::BinaryOp, left: &FortranType | ||
| 248 | 287 | } |
| 249 | 288 | |
| 250 | 289 | /// Compute the result type of a unary operation. |
| 251 | -pub fn unary_op_result_type(op: &crate::ast::expr::UnaryOp, operand: &FortranType) -> Option<FortranType> { | |
| 290 | +pub fn unary_op_result_type( | |
| 291 | + op: &crate::ast::expr::UnaryOp, | |
| 292 | + operand: &FortranType, | |
| 293 | +) -> Option<FortranType> { | |
| 252 | 294 | use crate::ast::expr::UnaryOp; |
| 253 | 295 | match op { |
| 254 | 296 | UnaryOp::Plus | UnaryOp::Minus => { |
| 255 | - if operand.is_numeric() { Some(operand.clone()) } else { None } | |
| 297 | + if operand.is_numeric() { | |
| 298 | + Some(operand.clone()) | |
| 299 | + } else { | |
| 300 | + None | |
| 301 | + } | |
| 256 | 302 | } |
| 257 | 303 | UnaryOp::Not => logical_result_type(operand), |
| 258 | 304 | UnaryOp::Defined(_) => Some(FortranType::Unknown), |
@@ -269,7 +315,10 @@ pub enum CallKind { | ||
| 269 | 315 | } |
| 270 | 316 | |
| 271 | 317 | /// Disambiguate `A(I)` based on symbol table information. |
| 272 | -pub fn disambiguate_call(sym_kind: &super::symtab::SymbolKind, has_range_subscript: bool) -> CallKind { | |
| 318 | +pub fn disambiguate_call( | |
| 319 | + sym_kind: &super::symtab::SymbolKind, | |
| 320 | + has_range_subscript: bool, | |
| 321 | +) -> CallKind { | |
| 273 | 322 | use super::symtab::SymbolKind; |
| 274 | 323 | match sym_kind { |
| 275 | 324 | SymbolKind::Variable => { |
@@ -279,8 +328,10 @@ pub fn disambiguate_call(sym_kind: &super::symtab::SymbolKind, has_range_subscri | ||
| 279 | 328 | CallKind::ArrayElement // variable with subscripts → array element |
| 280 | 329 | } |
| 281 | 330 | } |
| 282 | - SymbolKind::Function | SymbolKind::ExternalProc | | |
| 283 | - SymbolKind::IntrinsicProc | SymbolKind::ProcedurePointer => CallKind::FunctionCall, | |
| 331 | + SymbolKind::Function | |
| 332 | + | SymbolKind::ExternalProc | |
| 333 | + | SymbolKind::IntrinsicProc | |
| 334 | + | SymbolKind::ProcedurePointer => CallKind::FunctionCall, | |
| 284 | 335 | SymbolKind::NamedInterface => CallKind::FunctionCall, // generic → function call |
| 285 | 336 | _ => CallKind::Unknown, |
| 286 | 337 | } |
@@ -290,11 +341,19 @@ pub fn disambiguate_call(sym_kind: &super::symtab::SymbolKind, has_range_subscri | ||
| 290 | 341 | pub fn type_info_to_fortran_type(info: &super::symtab::TypeInfo) -> FortranType { |
| 291 | 342 | use super::symtab::TypeInfo; |
| 292 | 343 | match info { |
| 293 | - TypeInfo::Integer { kind } => FortranType::Integer { kind: kind.unwrap_or(4) }, | |
| 294 | - TypeInfo::Real { kind } => FortranType::Real { kind: kind.unwrap_or(4) }, | |
| 344 | + TypeInfo::Integer { kind } => FortranType::Integer { | |
| 345 | + kind: kind.unwrap_or(4), | |
| 346 | + }, | |
| 347 | + TypeInfo::Real { kind } => FortranType::Real { | |
| 348 | + kind: kind.unwrap_or(4), | |
| 349 | + }, | |
| 295 | 350 | TypeInfo::DoublePrecision => FortranType::Real { kind: 8 }, |
| 296 | - TypeInfo::Complex { kind } => FortranType::Complex { kind: kind.unwrap_or(4) }, | |
| 297 | - TypeInfo::Logical { kind } => FortranType::Logical { kind: kind.unwrap_or(4) }, | |
| 351 | + TypeInfo::Complex { kind } => FortranType::Complex { | |
| 352 | + kind: kind.unwrap_or(4), | |
| 353 | + }, | |
| 354 | + TypeInfo::Logical { kind } => FortranType::Logical { | |
| 355 | + kind: kind.unwrap_or(4), | |
| 356 | + }, | |
| 298 | 357 | TypeInfo::Character { len, kind } => FortranType::Character { |
| 299 | 358 | kind: kind.unwrap_or(1), |
| 300 | 359 | len: match len { |
@@ -327,7 +386,10 @@ pub fn literal_type(expr: &crate::ast::expr::Expr) -> FortranType { | ||
| 327 | 386 | use crate::ast::expr::Expr; |
| 328 | 387 | match expr { |
| 329 | 388 | Expr::IntegerLiteral { kind, .. } => { |
| 330 | - let k = kind.as_ref().and_then(|s| s.parse::<u8>().ok()).unwrap_or(4); | |
| 389 | + let k = kind | |
| 390 | + .as_ref() | |
| 391 | + .and_then(|s| s.parse::<u8>().ok()) | |
| 392 | + .unwrap_or(4); | |
| 331 | 393 | FortranType::Integer { kind: k } |
| 332 | 394 | } |
| 333 | 395 | Expr::RealLiteral { text, kind, .. } => { |
@@ -344,11 +406,20 @@ pub fn literal_type(expr: &crate::ast::expr::Expr) -> FortranType { | ||
| 344 | 406 | } |
| 345 | 407 | } |
| 346 | 408 | Expr::StringLiteral { kind, value, .. } => { |
| 347 | - let k = kind.as_ref().and_then(|s| s.parse::<u8>().ok()).unwrap_or(1); | |
| 348 | - FortranType::Character { kind: k, len: CharLen::Known(value.len() as i64) } | |
| 409 | + let k = kind | |
| 410 | + .as_ref() | |
| 411 | + .and_then(|s| s.parse::<u8>().ok()) | |
| 412 | + .unwrap_or(1); | |
| 413 | + FortranType::Character { | |
| 414 | + kind: k, | |
| 415 | + len: CharLen::Known(value.len() as i64), | |
| 416 | + } | |
| 349 | 417 | } |
| 350 | 418 | Expr::LogicalLiteral { kind, .. } => { |
| 351 | - let k = kind.as_ref().and_then(|s| s.parse::<u8>().ok()).unwrap_or(4); | |
| 419 | + let k = kind | |
| 420 | + .as_ref() | |
| 421 | + .and_then(|s| s.parse::<u8>().ok()) | |
| 422 | + .unwrap_or(4); | |
| 352 | 423 | FortranType::Logical { kind: k } |
| 353 | 424 | } |
| 354 | 425 | Expr::ComplexLiteral { .. } => FortranType::default_complex(), |
@@ -359,13 +430,19 @@ pub fn literal_type(expr: &crate::ast::expr::Expr) -> FortranType { | ||
| 359 | 430 | |
| 360 | 431 | /// Compute the type of an expression given a symbol table context. |
| 361 | 432 | /// Returns Unknown for expressions that can't be resolved without more context. |
| 362 | -pub fn expr_type(expr: &crate::ast::expr::SpannedExpr, symtab: &super::symtab::SymbolTable) -> FortranType { | |
| 433 | +pub fn expr_type( | |
| 434 | + expr: &crate::ast::expr::SpannedExpr, | |
| 435 | + symtab: &super::symtab::SymbolTable, | |
| 436 | +) -> FortranType { | |
| 363 | 437 | use crate::ast::expr::Expr; |
| 364 | 438 | match &expr.node { |
| 365 | 439 | // Literals |
| 366 | - Expr::IntegerLiteral { .. } | Expr::RealLiteral { .. } | | |
| 367 | - Expr::StringLiteral { .. } | Expr::LogicalLiteral { .. } | | |
| 368 | - Expr::ComplexLiteral { .. } | Expr::BozLiteral { .. } => literal_type(&expr.node), | |
| 440 | + Expr::IntegerLiteral { .. } | |
| 441 | + | Expr::RealLiteral { .. } | |
| 442 | + | Expr::StringLiteral { .. } | |
| 443 | + | Expr::LogicalLiteral { .. } | |
| 444 | + | Expr::ComplexLiteral { .. } | |
| 445 | + | Expr::BozLiteral { .. } => literal_type(&expr.node), | |
| 369 | 446 | |
| 370 | 447 | // Name — look up in symbol table |
| 371 | 448 | Expr::Name { name } => { |
@@ -401,12 +478,13 @@ pub fn expr_type(expr: &crate::ast::expr::SpannedExpr, symtab: &super::symtab::S | ||
| 401 | 478 | Expr::FunctionCall { callee, args } => { |
| 402 | 479 | if let Expr::Name { name } = &callee.node { |
| 403 | 480 | // Check intrinsics first |
| 404 | - let arg_types: Vec<FortranType> = args.iter().map(|a| { | |
| 405 | - match &a.value { | |
| 481 | + let arg_types: Vec<FortranType> = args | |
| 482 | + .iter() | |
| 483 | + .map(|a| match &a.value { | |
| 406 | 484 | crate::ast::expr::SectionSubscript::Element(e) => expr_type(e, symtab), |
| 407 | 485 | crate::ast::expr::SectionSubscript::Range { .. } => FortranType::Unknown, |
| 408 | - } | |
| 409 | - }).collect(); | |
| 486 | + }) | |
| 487 | + .collect(); | |
| 410 | 488 | |
| 411 | 489 | if let Some(result) = intrinsic_result_type(name, &arg_types) { |
| 412 | 490 | return result; |
@@ -432,9 +510,10 @@ pub fn expr_type(expr: &crate::ast::expr::SpannedExpr, symtab: &super::symtab::S | ||
| 432 | 510 | None => FortranType::Unknown, |
| 433 | 511 | } |
| 434 | 512 | } |
| 435 | - CallKind::Substring => { | |
| 436 | - FortranType::Character { kind: 1, len: CharLen::Unknown } | |
| 437 | - } | |
| 513 | + CallKind::Substring => FortranType::Character { | |
| 514 | + kind: 1, | |
| 515 | + len: CharLen::Unknown, | |
| 516 | + }, | |
| 438 | 517 | CallKind::Unknown => FortranType::Unknown, |
| 439 | 518 | } |
| 440 | 519 | } else { |
@@ -495,7 +574,10 @@ pub fn check_arguments( | ||
| 495 | 574 | if let Some(kw) = keyword { |
| 496 | 575 | seen_keyword = true; |
| 497 | 576 | // Keyword argument — find matching dummy |
| 498 | - if let Some(idx) = dummy_args.iter().position(|d| d.name.eq_ignore_ascii_case(kw)) { | |
| 577 | + if let Some(idx) = dummy_args | |
| 578 | + .iter() | |
| 579 | + .position(|d| d.name.eq_ignore_ascii_case(kw)) | |
| 580 | + { | |
| 499 | 581 | if matched[idx] { |
| 500 | 582 | errors.push(format!("duplicate keyword argument '{}'", kw)); |
| 501 | 583 | } else { |
@@ -520,7 +602,10 @@ pub fn check_arguments( | ||
| 520 | 602 | check_arg_type(&dummy_args[pos], actual_type, &mut errors); |
| 521 | 603 | pos += 1; |
| 522 | 604 | } else { |
| 523 | - errors.push(format!("too many arguments (expected at most {})", dummy_args.len())); | |
| 605 | + errors.push(format!( | |
| 606 | + "too many arguments (expected at most {})", | |
| 607 | + dummy_args.len() | |
| 608 | + )); | |
| 524 | 609 | break; |
| 525 | 610 | } |
| 526 | 611 | } |
@@ -624,10 +709,8 @@ pub fn intrinsic_result_type(name: &str, args: &[FortranType]) -> Option<Fortran | ||
| 624 | 709 | "sign" | "mod" | "modulo" => args.first().cloned(), |
| 625 | 710 | |
| 626 | 711 | // Real-valued math. |
| 627 | - "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | | |
| 628 | - "exp" | "log" | "log10" | "sqrt" | "atan2" => { | |
| 629 | - Some(args.first().cloned().unwrap_or(FortranType::default_real())) | |
| 630 | - } | |
| 712 | + "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "exp" | "log" | "log10" | "sqrt" | |
| 713 | + | "atan2" => Some(args.first().cloned().unwrap_or(FortranType::default_real())), | |
| 631 | 714 | |
| 632 | 715 | // Integer-valued. |
| 633 | 716 | "int" | "nint" | "floor" | "ceiling" => Some(FortranType::default_integer()), |
@@ -661,10 +744,14 @@ pub fn intrinsic_result_type(name: &str, args: &[FortranType]) -> Option<Fortran | ||
| 661 | 744 | "any" | "all" => Some(FortranType::default_logical()), |
| 662 | 745 | |
| 663 | 746 | // Character-valued. |
| 664 | - "trim" | "adjustl" | "adjustr" | "repeat" => { | |
| 665 | - Some(FortranType::Character { kind: 1, len: CharLen::Unknown }) | |
| 666 | - } | |
| 667 | - "char" | "achar" => Some(FortranType::Character { kind: 1, len: CharLen::Known(1) }), | |
| 747 | + "trim" | "adjustl" | "adjustr" | "repeat" => Some(FortranType::Character { | |
| 748 | + kind: 1, | |
| 749 | + len: CharLen::Unknown, | |
| 750 | + }), | |
| 751 | + "char" | "achar" => Some(FortranType::Character { | |
| 752 | + kind: 1, | |
| 753 | + len: CharLen::Known(1), | |
| 754 | + }), | |
| 668 | 755 | |
| 669 | 756 | // Complex-valued. |
| 670 | 757 | "cmplx" => Some(FortranType::default_complex()), |
@@ -683,18 +770,25 @@ pub fn intrinsic_result_type(name: &str, args: &[FortranType]) -> Option<Fortran | ||
| 683 | 770 | |
| 684 | 771 | // Inquiry intrinsics. |
| 685 | 772 | "huge" | "tiny" | "epsilon" => args.first().cloned(), |
| 686 | - "precision" | "range" | "digits" | "radix" | "exponent" => Some(FortranType::default_integer()), | |
| 773 | + "precision" | "range" | "digits" | "radix" | "exponent" => { | |
| 774 | + Some(FortranType::default_integer()) | |
| 775 | + } | |
| 687 | 776 | "storage_size" | "c_sizeof" => Some(FortranType::default_integer()), |
| 688 | 777 | "iachar" | "ichar" => Some(FortranType::default_integer()), |
| 689 | 778 | |
| 690 | 779 | // System / misc. |
| 691 | 780 | "command_argument_count" => Some(FortranType::default_integer()), |
| 692 | 781 | "null" => Some(FortranType::Unknown), // null pointer — type from context |
| 693 | - "new_line" => Some(FortranType::Character { kind: 1, len: CharLen::Known(1) }), | |
| 782 | + "new_line" => Some(FortranType::Character { | |
| 783 | + kind: 1, | |
| 784 | + len: CharLen::Known(1), | |
| 785 | + }), | |
| 694 | 786 | "logical" => Some(FortranType::default_logical()), |
| 695 | 787 | |
| 696 | 788 | // iso_c_binding. |
| 697 | - "c_loc" | "c_funloc" => Some(FortranType::Derived { name: "c_ptr".into() }), | |
| 789 | + "c_loc" | "c_funloc" => Some(FortranType::Derived { | |
| 790 | + name: "c_ptr".into(), | |
| 791 | + }), | |
| 698 | 792 | "c_associated" => Some(FortranType::default_logical()), |
| 699 | 793 | |
| 700 | 794 | // Status inquiry. |
@@ -715,7 +809,8 @@ mod tests { | ||
| 715 | 809 | let result = arithmetic_result_type( |
| 716 | 810 | &FortranType::Integer { kind: 4 }, |
| 717 | 811 | &FortranType::Integer { kind: 4 }, |
| 718 | - ).unwrap(); | |
| 812 | + ) | |
| 813 | + .unwrap(); | |
| 719 | 814 | assert_eq!(result, FortranType::Integer { kind: 4 }); |
| 720 | 815 | } |
| 721 | 816 | |
@@ -724,7 +819,8 @@ mod tests { | ||
| 724 | 819 | let result = arithmetic_result_type( |
| 725 | 820 | &FortranType::Integer { kind: 4 }, |
| 726 | 821 | &FortranType::Real { kind: 4 }, |
| 727 | - ).unwrap(); | |
| 822 | + ) | |
| 823 | + .unwrap(); | |
| 728 | 824 | assert_eq!(result, FortranType::Real { kind: 4 }); |
| 729 | 825 | } |
| 730 | 826 | |
@@ -733,7 +829,8 @@ mod tests { | ||
| 733 | 829 | let result = arithmetic_result_type( |
| 734 | 830 | &FortranType::Real { kind: 4 }, |
| 735 | 831 | &FortranType::Complex { kind: 4 }, |
| 736 | - ).unwrap(); | |
| 832 | + ) | |
| 833 | + .unwrap(); | |
| 737 | 834 | assert_eq!(result, FortranType::Complex { kind: 4 }); |
| 738 | 835 | } |
| 739 | 836 | |
@@ -742,7 +839,8 @@ mod tests { | ||
| 742 | 839 | let result = arithmetic_result_type( |
| 743 | 840 | &FortranType::Integer { kind: 4 }, |
| 744 | 841 | &FortranType::Real { kind: 8 }, |
| 745 | - ).unwrap(); | |
| 842 | + ) | |
| 843 | + .unwrap(); | |
| 746 | 844 | assert_eq!(result, FortranType::Real { kind: 8 }); |
| 747 | 845 | } |
| 748 | 846 | |
@@ -751,7 +849,8 @@ mod tests { | ||
| 751 | 849 | let result = arithmetic_result_type( |
| 752 | 850 | &FortranType::Integer { kind: 4 }, |
| 753 | 851 | &FortranType::Integer { kind: 8 }, |
| 754 | - ).unwrap(); | |
| 852 | + ) | |
| 853 | + .unwrap(); | |
| 755 | 854 | assert_eq!(result, FortranType::Integer { kind: 8 }); |
| 756 | 855 | } |
| 757 | 856 | |
@@ -760,7 +859,8 @@ mod tests { | ||
| 760 | 859 | assert!(arithmetic_result_type( |
| 761 | 860 | &FortranType::default_logical(), |
| 762 | 861 | &FortranType::default_integer(), |
| 763 | - ).is_none()); | |
| 862 | + ) | |
| 863 | + .is_none()); | |
| 764 | 864 | } |
| 765 | 865 | |
| 766 | 866 | // ---- Power ---- |
@@ -770,7 +870,8 @@ mod tests { | ||
| 770 | 870 | let result = power_result_type( |
| 771 | 871 | &FortranType::Integer { kind: 4 }, |
| 772 | 872 | &FortranType::Integer { kind: 4 }, |
| 773 | - ).unwrap(); | |
| 873 | + ) | |
| 874 | + .unwrap(); | |
| 774 | 875 | assert_eq!(result, FortranType::Integer { kind: 4 }); |
| 775 | 876 | } |
| 776 | 877 | |
@@ -779,7 +880,8 @@ mod tests { | ||
| 779 | 880 | let result = power_result_type( |
| 780 | 881 | &FortranType::Real { kind: 8 }, |
| 781 | 882 | &FortranType::Integer { kind: 4 }, |
| 782 | - ).unwrap(); | |
| 883 | + ) | |
| 884 | + .unwrap(); | |
| 783 | 885 | assert_eq!(result, FortranType::Real { kind: 8 }); |
| 784 | 886 | } |
| 785 | 887 | |
@@ -789,7 +891,8 @@ mod tests { | ||
| 789 | 891 | let result = power_result_type( |
| 790 | 892 | &FortranType::Integer { kind: 4 }, |
| 791 | 893 | &FortranType::Complex { kind: 8 }, |
| 792 | - ).unwrap(); | |
| 894 | + ) | |
| 895 | + .unwrap(); | |
| 793 | 896 | assert_eq!(result, FortranType::Complex { kind: 8 }); |
| 794 | 897 | } |
| 795 | 898 | |
@@ -805,18 +908,35 @@ mod tests { | ||
| 805 | 908 | #[test] |
| 806 | 909 | fn concat_known_lengths() { |
| 807 | 910 | let result = concat_result_type( |
| 808 | - &FortranType::Character { kind: 1, len: CharLen::Known(5) }, | |
| 809 | - &FortranType::Character { kind: 1, len: CharLen::Known(3) }, | |
| 810 | - ).unwrap(); | |
| 811 | - assert_eq!(result, FortranType::Character { kind: 1, len: CharLen::Known(8) }); | |
| 911 | + &FortranType::Character { | |
| 912 | + kind: 1, | |
| 913 | + len: CharLen::Known(5), | |
| 914 | + }, | |
| 915 | + &FortranType::Character { | |
| 916 | + kind: 1, | |
| 917 | + len: CharLen::Known(3), | |
| 918 | + }, | |
| 919 | + ) | |
| 920 | + .unwrap(); | |
| 921 | + assert_eq!( | |
| 922 | + result, | |
| 923 | + FortranType::Character { | |
| 924 | + kind: 1, | |
| 925 | + len: CharLen::Known(8) | |
| 926 | + } | |
| 927 | + ); | |
| 812 | 928 | } |
| 813 | 929 | |
| 814 | 930 | #[test] |
| 815 | 931 | fn concat_non_character_returns_none() { |
| 816 | 932 | assert!(concat_result_type( |
| 817 | 933 | &FortranType::default_integer(), |
| 818 | - &FortranType::Character { kind: 1, len: CharLen::Known(5) }, | |
| 819 | - ).is_none()); | |
| 934 | + &FortranType::Character { | |
| 935 | + kind: 1, | |
| 936 | + len: CharLen::Known(5) | |
| 937 | + }, | |
| 938 | + ) | |
| 939 | + .is_none()); | |
| 820 | 940 | } |
| 821 | 941 | |
| 822 | 942 | // ---- Conversion ---- |
@@ -826,7 +946,8 @@ mod tests { | ||
| 826 | 946 | assert!(needs_conversion( |
| 827 | 947 | &FortranType::Integer { kind: 4 }, |
| 828 | 948 | &FortranType::Integer { kind: 4 }, |
| 829 | - ).is_none()); | |
| 949 | + ) | |
| 950 | + .is_none()); | |
| 830 | 951 | } |
| 831 | 952 | |
| 832 | 953 | #[test] |
@@ -834,7 +955,8 @@ mod tests { | ||
| 834 | 955 | let conv = needs_conversion( |
| 835 | 956 | &FortranType::Integer { kind: 4 }, |
| 836 | 957 | &FortranType::Real { kind: 4 }, |
| 837 | - ).unwrap(); | |
| 958 | + ) | |
| 959 | + .unwrap(); | |
| 838 | 960 | assert_eq!(conv, FortranType::Real { kind: 4 }); |
| 839 | 961 | } |
| 840 | 962 | |
@@ -860,9 +982,14 @@ mod tests { | ||
| 860 | 982 | |
| 861 | 983 | #[test] |
| 862 | 984 | fn len_returns_integer() { |
| 863 | - let result = intrinsic_result_type("len", &[ | |
| 864 | - FortranType::Character { kind: 1, len: CharLen::Known(10) } | |
| 865 | - ]).unwrap(); | |
| 985 | + let result = intrinsic_result_type( | |
| 986 | + "len", | |
| 987 | + &[FortranType::Character { | |
| 988 | + kind: 1, | |
| 989 | + len: CharLen::Known(10), | |
| 990 | + }], | |
| 991 | + ) | |
| 992 | + .unwrap(); | |
| 866 | 993 | assert_eq!(result, FortranType::default_integer()); |
| 867 | 994 | } |
| 868 | 995 | |
@@ -874,15 +1001,22 @@ mod tests { | ||
| 874 | 1001 | |
| 875 | 1002 | #[test] |
| 876 | 1003 | fn trim_returns_character() { |
| 877 | - let result = intrinsic_result_type("trim", &[ | |
| 878 | - FortranType::Character { kind: 1, len: CharLen::Known(20) } | |
| 879 | - ]).unwrap(); | |
| 1004 | + let result = intrinsic_result_type( | |
| 1005 | + "trim", | |
| 1006 | + &[FortranType::Character { | |
| 1007 | + kind: 1, | |
| 1008 | + len: CharLen::Known(20), | |
| 1009 | + }], | |
| 1010 | + ) | |
| 1011 | + .unwrap(); | |
| 880 | 1012 | assert!(result.is_character()); |
| 881 | 1013 | } |
| 882 | 1014 | |
| 883 | 1015 | #[test] |
| 884 | 1016 | fn unknown_intrinsic_returns_none() { |
| 885 | - assert!(intrinsic_result_type("nonexistent_func", &[FortranType::default_integer()]).is_none()); | |
| 1017 | + assert!( | |
| 1018 | + intrinsic_result_type("nonexistent_func", &[FortranType::default_integer()]).is_none() | |
| 1019 | + ); | |
| 886 | 1020 | } |
| 887 | 1021 | |
| 888 | 1022 | // ---- Binary op result type ---- |
@@ -894,7 +1028,8 @@ mod tests { | ||
| 894 | 1028 | &BinaryOp::Add, |
| 895 | 1029 | &FortranType::Integer { kind: 4 }, |
| 896 | 1030 | &FortranType::Real { kind: 8 }, |
| 897 | - ).unwrap(); | |
| 1031 | + ) | |
| 1032 | + .unwrap(); | |
| 898 | 1033 | assert_eq!(result, FortranType::Real { kind: 8 }); |
| 899 | 1034 | } |
| 900 | 1035 | |
@@ -905,7 +1040,8 @@ mod tests { | ||
| 905 | 1040 | &BinaryOp::Pow, |
| 906 | 1041 | &FortranType::Real { kind: 4 }, |
| 907 | 1042 | &FortranType::Integer { kind: 4 }, |
| 908 | - ).unwrap(); | |
| 1043 | + ) | |
| 1044 | + .unwrap(); | |
| 909 | 1045 | assert_eq!(result, FortranType::Real { kind: 4 }); |
| 910 | 1046 | } |
| 911 | 1047 | |
@@ -914,10 +1050,23 @@ mod tests { | ||
| 914 | 1050 | use crate::ast::expr::BinaryOp; |
| 915 | 1051 | let result = binary_op_result_type( |
| 916 | 1052 | &BinaryOp::Concat, |
| 917 | - &FortranType::Character { kind: 1, len: CharLen::Known(3) }, | |
| 918 | - &FortranType::Character { kind: 1, len: CharLen::Known(4) }, | |
| 919 | - ).unwrap(); | |
| 920 | - assert_eq!(result, FortranType::Character { kind: 1, len: CharLen::Known(7) }); | |
| 1053 | + &FortranType::Character { | |
| 1054 | + kind: 1, | |
| 1055 | + len: CharLen::Known(3), | |
| 1056 | + }, | |
| 1057 | + &FortranType::Character { | |
| 1058 | + kind: 1, | |
| 1059 | + len: CharLen::Known(4), | |
| 1060 | + }, | |
| 1061 | + ) | |
| 1062 | + .unwrap(); | |
| 1063 | + assert_eq!( | |
| 1064 | + result, | |
| 1065 | + FortranType::Character { | |
| 1066 | + kind: 1, | |
| 1067 | + len: CharLen::Known(7) | |
| 1068 | + } | |
| 1069 | + ); | |
| 921 | 1070 | } |
| 922 | 1071 | |
| 923 | 1072 | #[test] |
@@ -927,7 +1076,8 @@ mod tests { | ||
| 927 | 1076 | &BinaryOp::Eq, |
| 928 | 1077 | &FortranType::Integer { kind: 4 }, |
| 929 | 1078 | &FortranType::Real { kind: 4 }, |
| 930 | - ).unwrap(); | |
| 1079 | + ) | |
| 1080 | + .unwrap(); | |
| 931 | 1081 | assert_eq!(result, FortranType::default_logical()); |
| 932 | 1082 | } |
| 933 | 1083 | |
@@ -936,9 +1086,16 @@ mod tests { | ||
| 936 | 1086 | use crate::ast::expr::BinaryOp; |
| 937 | 1087 | let result = binary_op_result_type( |
| 938 | 1088 | &BinaryOp::Lt, |
| 939 | - &FortranType::Character { kind: 1, len: CharLen::Known(5) }, | |
| 940 | - &FortranType::Character { kind: 1, len: CharLen::Known(5) }, | |
| 941 | - ).unwrap(); | |
| 1089 | + &FortranType::Character { | |
| 1090 | + kind: 1, | |
| 1091 | + len: CharLen::Known(5), | |
| 1092 | + }, | |
| 1093 | + &FortranType::Character { | |
| 1094 | + kind: 1, | |
| 1095 | + len: CharLen::Known(5), | |
| 1096 | + }, | |
| 1097 | + ) | |
| 1098 | + .unwrap(); | |
| 942 | 1099 | assert_eq!(result, FortranType::default_logical()); |
| 943 | 1100 | } |
| 944 | 1101 | |
@@ -948,8 +1105,12 @@ mod tests { | ||
| 948 | 1105 | assert!(binary_op_result_type( |
| 949 | 1106 | &BinaryOp::Eq, |
| 950 | 1107 | &FortranType::Integer { kind: 4 }, |
| 951 | - &FortranType::Character { kind: 1, len: CharLen::Known(3) }, | |
| 952 | - ).is_none()); | |
| 1108 | + &FortranType::Character { | |
| 1109 | + kind: 1, | |
| 1110 | + len: CharLen::Known(3) | |
| 1111 | + }, | |
| 1112 | + ) | |
| 1113 | + .is_none()); | |
| 953 | 1114 | } |
| 954 | 1115 | |
| 955 | 1116 | #[test] |
@@ -959,7 +1120,8 @@ mod tests { | ||
| 959 | 1120 | &BinaryOp::And, |
| 960 | 1121 | &FortranType::Logical { kind: 4 }, |
| 961 | 1122 | &FortranType::Logical { kind: 4 }, |
| 962 | - ).unwrap(); | |
| 1123 | + ) | |
| 1124 | + .unwrap(); | |
| 963 | 1125 | assert_eq!(result, FortranType::Logical { kind: 4 }); |
| 964 | 1126 | } |
| 965 | 1127 | |
@@ -970,7 +1132,8 @@ mod tests { | ||
| 970 | 1132 | &BinaryOp::And, |
| 971 | 1133 | &FortranType::Integer { kind: 4 }, |
| 972 | 1134 | &FortranType::Logical { kind: 4 }, |
| 973 | - ).is_none()); | |
| 1135 | + ) | |
| 1136 | + .is_none()); | |
| 974 | 1137 | } |
| 975 | 1138 | |
| 976 | 1139 | #[test] |
@@ -980,7 +1143,8 @@ mod tests { | ||
| 980 | 1143 | &BinaryOp::Or, |
| 981 | 1144 | &FortranType::Logical { kind: 1 }, |
| 982 | 1145 | &FortranType::Logical { kind: 4 }, |
| 983 | - ).unwrap(); | |
| 1146 | + ) | |
| 1147 | + .unwrap(); | |
| 984 | 1148 | assert_eq!(result, FortranType::Logical { kind: 4 }); |
| 985 | 1149 | } |
| 986 | 1150 | |
@@ -991,7 +1155,8 @@ mod tests { | ||
| 991 | 1155 | &BinaryOp::Defined("cross".into()), |
| 992 | 1156 | &FortranType::default_real(), |
| 993 | 1157 | &FortranType::default_real(), |
| 994 | - ).unwrap(); | |
| 1158 | + ) | |
| 1159 | + .unwrap(); | |
| 995 | 1160 | assert_eq!(result, FortranType::Unknown); |
| 996 | 1161 | } |
| 997 | 1162 | |
@@ -1000,39 +1165,28 @@ mod tests { | ||
| 1000 | 1165 | #[test] |
| 1001 | 1166 | fn unary_minus_real() { |
| 1002 | 1167 | use crate::ast::expr::UnaryOp; |
| 1003 | - let result = unary_op_result_type( | |
| 1004 | - &UnaryOp::Minus, | |
| 1005 | - &FortranType::Real { kind: 8 }, | |
| 1006 | - ).unwrap(); | |
| 1168 | + let result = unary_op_result_type(&UnaryOp::Minus, &FortranType::Real { kind: 8 }).unwrap(); | |
| 1007 | 1169 | assert_eq!(result, FortranType::Real { kind: 8 }); |
| 1008 | 1170 | } |
| 1009 | 1171 | |
| 1010 | 1172 | #[test] |
| 1011 | 1173 | fn unary_plus_non_numeric_returns_none() { |
| 1012 | 1174 | use crate::ast::expr::UnaryOp; |
| 1013 | - assert!(unary_op_result_type( | |
| 1014 | - &UnaryOp::Plus, | |
| 1015 | - &FortranType::default_logical(), | |
| 1016 | - ).is_none()); | |
| 1175 | + assert!(unary_op_result_type(&UnaryOp::Plus, &FortranType::default_logical(),).is_none()); | |
| 1017 | 1176 | } |
| 1018 | 1177 | |
| 1019 | 1178 | #[test] |
| 1020 | 1179 | fn unary_not_logical() { |
| 1021 | 1180 | use crate::ast::expr::UnaryOp; |
| 1022 | - let result = unary_op_result_type( | |
| 1023 | - &UnaryOp::Not, | |
| 1024 | - &FortranType::Logical { kind: 4 }, | |
| 1025 | - ).unwrap(); | |
| 1181 | + let result = | |
| 1182 | + unary_op_result_type(&UnaryOp::Not, &FortranType::Logical { kind: 4 }).unwrap(); | |
| 1026 | 1183 | assert_eq!(result, FortranType::Logical { kind: 4 }); |
| 1027 | 1184 | } |
| 1028 | 1185 | |
| 1029 | 1186 | #[test] |
| 1030 | 1187 | fn unary_not_non_logical_returns_none() { |
| 1031 | 1188 | use crate::ast::expr::UnaryOp; |
| 1032 | - assert!(unary_op_result_type( | |
| 1033 | - &UnaryOp::Not, | |
| 1034 | - &FortranType::Integer { kind: 4 }, | |
| 1035 | - ).is_none()); | |
| 1189 | + assert!(unary_op_result_type(&UnaryOp::Not, &FortranType::Integer { kind: 4 },).is_none()); | |
| 1036 | 1190 | } |
| 1037 | 1191 | |
| 1038 | 1192 | // ---- Disambiguation ---- |
@@ -1040,43 +1194,64 @@ mod tests { | ||
| 1040 | 1194 | #[test] |
| 1041 | 1195 | fn disambiguate_variable_element() { |
| 1042 | 1196 | use super::super::symtab::SymbolKind; |
| 1043 | - assert_eq!(disambiguate_call(&SymbolKind::Variable, false), CallKind::ArrayElement); | |
| 1197 | + assert_eq!( | |
| 1198 | + disambiguate_call(&SymbolKind::Variable, false), | |
| 1199 | + CallKind::ArrayElement | |
| 1200 | + ); | |
| 1044 | 1201 | } |
| 1045 | 1202 | |
| 1046 | 1203 | #[test] |
| 1047 | 1204 | fn disambiguate_variable_range_is_substring() { |
| 1048 | 1205 | use super::super::symtab::SymbolKind; |
| 1049 | - assert_eq!(disambiguate_call(&SymbolKind::Variable, true), CallKind::Substring); | |
| 1206 | + assert_eq!( | |
| 1207 | + disambiguate_call(&SymbolKind::Variable, true), | |
| 1208 | + CallKind::Substring | |
| 1209 | + ); | |
| 1050 | 1210 | } |
| 1051 | 1211 | |
| 1052 | 1212 | #[test] |
| 1053 | 1213 | fn disambiguate_function() { |
| 1054 | 1214 | use super::super::symtab::SymbolKind; |
| 1055 | - assert_eq!(disambiguate_call(&SymbolKind::Function, false), CallKind::FunctionCall); | |
| 1215 | + assert_eq!( | |
| 1216 | + disambiguate_call(&SymbolKind::Function, false), | |
| 1217 | + CallKind::FunctionCall | |
| 1218 | + ); | |
| 1056 | 1219 | } |
| 1057 | 1220 | |
| 1058 | 1221 | #[test] |
| 1059 | 1222 | fn disambiguate_external_proc() { |
| 1060 | 1223 | use super::super::symtab::SymbolKind; |
| 1061 | - assert_eq!(disambiguate_call(&SymbolKind::ExternalProc, false), CallKind::FunctionCall); | |
| 1224 | + assert_eq!( | |
| 1225 | + disambiguate_call(&SymbolKind::ExternalProc, false), | |
| 1226 | + CallKind::FunctionCall | |
| 1227 | + ); | |
| 1062 | 1228 | } |
| 1063 | 1229 | |
| 1064 | 1230 | #[test] |
| 1065 | 1231 | fn disambiguate_intrinsic_proc() { |
| 1066 | 1232 | use super::super::symtab::SymbolKind; |
| 1067 | - assert_eq!(disambiguate_call(&SymbolKind::IntrinsicProc, true), CallKind::FunctionCall); | |
| 1233 | + assert_eq!( | |
| 1234 | + disambiguate_call(&SymbolKind::IntrinsicProc, true), | |
| 1235 | + CallKind::FunctionCall | |
| 1236 | + ); | |
| 1068 | 1237 | } |
| 1069 | 1238 | |
| 1070 | 1239 | #[test] |
| 1071 | 1240 | fn disambiguate_named_interface() { |
| 1072 | 1241 | use super::super::symtab::SymbolKind; |
| 1073 | - assert_eq!(disambiguate_call(&SymbolKind::NamedInterface, false), CallKind::FunctionCall); | |
| 1242 | + assert_eq!( | |
| 1243 | + disambiguate_call(&SymbolKind::NamedInterface, false), | |
| 1244 | + CallKind::FunctionCall | |
| 1245 | + ); | |
| 1074 | 1246 | } |
| 1075 | 1247 | |
| 1076 | 1248 | #[test] |
| 1077 | 1249 | fn disambiguate_unknown_kind() { |
| 1078 | 1250 | use super::super::symtab::SymbolKind; |
| 1079 | - assert_eq!(disambiguate_call(&SymbolKind::Module, false), CallKind::Unknown); | |
| 1251 | + assert_eq!( | |
| 1252 | + disambiguate_call(&SymbolKind::Module, false), | |
| 1253 | + CallKind::Unknown | |
| 1254 | + ); | |
| 1080 | 1255 | } |
| 1081 | 1256 | |
| 1082 | 1257 | // ---- TypeInfo → FortranType conversion ---- |
@@ -1112,8 +1287,14 @@ mod tests { | ||
| 1112 | 1287 | fn type_info_character_with_len() { |
| 1113 | 1288 | use super::super::symtab::TypeInfo; |
| 1114 | 1289 | assert_eq!( |
| 1115 | - type_info_to_fortran_type(&TypeInfo::Character { len: Some(10), kind: None }), | |
| 1116 | - FortranType::Character { kind: 1, len: CharLen::Known(10) } | |
| 1290 | + type_info_to_fortran_type(&TypeInfo::Character { | |
| 1291 | + len: Some(10), | |
| 1292 | + kind: None | |
| 1293 | + }), | |
| 1294 | + FortranType::Character { | |
| 1295 | + kind: 1, | |
| 1296 | + len: CharLen::Known(10) | |
| 1297 | + } | |
| 1117 | 1298 | ); |
| 1118 | 1299 | } |
| 1119 | 1300 | |
@@ -1122,7 +1303,9 @@ mod tests { | ||
| 1122 | 1303 | use super::super::symtab::TypeInfo; |
| 1123 | 1304 | assert_eq!( |
| 1124 | 1305 | type_info_to_fortran_type(&TypeInfo::Derived("point".into())), |
| 1125 | - FortranType::Derived { name: "point".into() } | |
| 1306 | + FortranType::Derived { | |
| 1307 | + name: "point".into() | |
| 1308 | + } | |
| 1126 | 1309 | ); |
| 1127 | 1310 | } |
| 1128 | 1311 | |
@@ -1141,7 +1324,10 @@ mod tests { | ||
| 1141 | 1324 | fn literal_type_integer() { |
| 1142 | 1325 | use crate::ast::expr::Expr; |
| 1143 | 1326 | assert_eq!( |
| 1144 | - literal_type(&Expr::IntegerLiteral { text: "42".into(), kind: None }), | |
| 1327 | + literal_type(&Expr::IntegerLiteral { | |
| 1328 | + text: "42".into(), | |
| 1329 | + kind: None | |
| 1330 | + }), | |
| 1145 | 1331 | FortranType::Integer { kind: 4 } |
| 1146 | 1332 | ); |
| 1147 | 1333 | } |
@@ -1150,7 +1336,10 @@ mod tests { | ||
| 1150 | 1336 | fn literal_type_integer_kind8() { |
| 1151 | 1337 | use crate::ast::expr::Expr; |
| 1152 | 1338 | assert_eq!( |
| 1153 | - literal_type(&Expr::IntegerLiteral { text: "42".into(), kind: Some("8".into()) }), | |
| 1339 | + literal_type(&Expr::IntegerLiteral { | |
| 1340 | + text: "42".into(), | |
| 1341 | + kind: Some("8".into()) | |
| 1342 | + }), | |
| 1154 | 1343 | FortranType::Integer { kind: 8 } |
| 1155 | 1344 | ); |
| 1156 | 1345 | } |
@@ -1159,7 +1348,10 @@ mod tests { | ||
| 1159 | 1348 | fn literal_type_real_default() { |
| 1160 | 1349 | use crate::ast::expr::Expr; |
| 1161 | 1350 | assert_eq!( |
| 1162 | - literal_type(&Expr::RealLiteral { text: "3.14".into(), kind: None }), | |
| 1351 | + literal_type(&Expr::RealLiteral { | |
| 1352 | + text: "3.14".into(), | |
| 1353 | + kind: None | |
| 1354 | + }), | |
| 1163 | 1355 | FortranType::Real { kind: 4 } |
| 1164 | 1356 | ); |
| 1165 | 1357 | } |
@@ -1168,7 +1360,10 @@ mod tests { | ||
| 1168 | 1360 | fn literal_type_real_d_exponent() { |
| 1169 | 1361 | use crate::ast::expr::Expr; |
| 1170 | 1362 | assert_eq!( |
| 1171 | - literal_type(&Expr::RealLiteral { text: "1.0d0".into(), kind: None }), | |
| 1363 | + literal_type(&Expr::RealLiteral { | |
| 1364 | + text: "1.0d0".into(), | |
| 1365 | + kind: None | |
| 1366 | + }), | |
| 1172 | 1367 | FortranType::Real { kind: 8 } |
| 1173 | 1368 | ); |
| 1174 | 1369 | } |
@@ -1177,8 +1372,14 @@ mod tests { | ||
| 1177 | 1372 | fn literal_type_string() { |
| 1178 | 1373 | use crate::ast::expr::Expr; |
| 1179 | 1374 | assert_eq!( |
| 1180 | - literal_type(&Expr::StringLiteral { value: "hello".into(), kind: None }), | |
| 1181 | - FortranType::Character { kind: 1, len: CharLen::Known(5) } | |
| 1375 | + literal_type(&Expr::StringLiteral { | |
| 1376 | + value: "hello".into(), | |
| 1377 | + kind: None | |
| 1378 | + }), | |
| 1379 | + FortranType::Character { | |
| 1380 | + kind: 1, | |
| 1381 | + len: CharLen::Known(5) | |
| 1382 | + } | |
| 1182 | 1383 | ); |
| 1183 | 1384 | } |
| 1184 | 1385 | |
@@ -1186,7 +1387,10 @@ mod tests { | ||
| 1186 | 1387 | fn literal_type_logical() { |
| 1187 | 1388 | use crate::ast::expr::Expr; |
| 1188 | 1389 | assert_eq!( |
| 1189 | - literal_type(&Expr::LogicalLiteral { value: true, kind: None }), | |
| 1390 | + literal_type(&Expr::LogicalLiteral { | |
| 1391 | + value: true, | |
| 1392 | + kind: None | |
| 1393 | + }), | |
| 1190 | 1394 | FortranType::Logical { kind: 4 } |
| 1191 | 1395 | ); |
| 1192 | 1396 | } |
@@ -1197,20 +1401,34 @@ mod tests { | ||
| 1197 | 1401 | fn expr_type_integer_literal() { |
| 1198 | 1402 | use crate::ast::expr::Expr; |
| 1199 | 1403 | use crate::ast::Spanned; |
| 1200 | - use crate::lexer::{Span, Position}; | |
| 1201 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1202 | - let expr = Spanned::new(Expr::IntegerLiteral { text: "42".into(), kind: None }, span); | |
| 1404 | + use crate::lexer::{Position, Span}; | |
| 1405 | + let span = Span { | |
| 1406 | + file_id: 0, | |
| 1407 | + start: Position { line: 1, col: 1 }, | |
| 1408 | + end: Position { line: 1, col: 1 }, | |
| 1409 | + }; | |
| 1410 | + let expr = Spanned::new( | |
| 1411 | + Expr::IntegerLiteral { | |
| 1412 | + text: "42".into(), | |
| 1413 | + kind: None, | |
| 1414 | + }, | |
| 1415 | + span, | |
| 1416 | + ); | |
| 1203 | 1417 | let st = super::super::symtab::SymbolTable::new(); |
| 1204 | 1418 | assert_eq!(expr_type(&expr, &st), FortranType::Integer { kind: 4 }); |
| 1205 | 1419 | } |
| 1206 | 1420 | |
| 1207 | 1421 | #[test] |
| 1208 | 1422 | fn expr_type_name_lookup() { |
| 1423 | + use super::super::symtab::*; | |
| 1209 | 1424 | use crate::ast::expr::Expr; |
| 1210 | 1425 | use crate::ast::Spanned; |
| 1211 | - use crate::lexer::{Span, Position}; | |
| 1212 | - use super::super::symtab::*; | |
| 1213 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1426 | + use crate::lexer::{Position, Span}; | |
| 1427 | + let span = Span { | |
| 1428 | + file_id: 0, | |
| 1429 | + start: Position { line: 1, col: 1 }, | |
| 1430 | + end: Position { line: 1, col: 1 }, | |
| 1431 | + }; | |
| 1214 | 1432 | |
| 1215 | 1433 | let mut st = SymbolTable::new(); |
| 1216 | 1434 | st.push_scope(ScopeKind::Program("main".into())); |
@@ -1223,7 +1441,8 @@ mod tests { | ||
| 1223 | 1441 | scope: 0, |
| 1224 | 1442 | arg_names: vec![], |
| 1225 | 1443 | const_value: None, |
| 1226 | - }).unwrap(); | |
| 1444 | + }) | |
| 1445 | + .unwrap(); | |
| 1227 | 1446 | |
| 1228 | 1447 | let expr = Spanned::new(Expr::Name { name: "x".into() }, span); |
| 1229 | 1448 | assert_eq!(expr_type(&expr, &st), FortranType::Real { kind: 8 }); |
@@ -1231,11 +1450,15 @@ mod tests { | ||
| 1231 | 1450 | |
| 1232 | 1451 | #[test] |
| 1233 | 1452 | fn expr_type_name_implicit() { |
| 1453 | + use super::super::symtab::*; | |
| 1234 | 1454 | use crate::ast::expr::Expr; |
| 1235 | 1455 | use crate::ast::Spanned; |
| 1236 | - use crate::lexer::{Span, Position}; | |
| 1237 | - use super::super::symtab::*; | |
| 1238 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1456 | + use crate::lexer::{Position, Span}; | |
| 1457 | + let span = Span { | |
| 1458 | + file_id: 0, | |
| 1459 | + start: Position { line: 1, col: 1 }, | |
| 1460 | + end: Position { line: 1, col: 1 }, | |
| 1461 | + }; | |
| 1239 | 1462 | |
| 1240 | 1463 | let mut st = SymbolTable::new(); |
| 1241 | 1464 | st.push_scope(ScopeKind::Program("main".into())); |
@@ -1246,15 +1469,38 @@ mod tests { | ||
| 1246 | 1469 | |
| 1247 | 1470 | #[test] |
| 1248 | 1471 | fn expr_type_binary_add() { |
| 1249 | - use crate::ast::expr::{Expr, BinaryOp}; | |
| 1472 | + use crate::ast::expr::{BinaryOp, Expr}; | |
| 1250 | 1473 | use crate::ast::Spanned; |
| 1251 | - use crate::lexer::{Span, Position}; | |
| 1252 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1474 | + use crate::lexer::{Position, Span}; | |
| 1475 | + let span = Span { | |
| 1476 | + file_id: 0, | |
| 1477 | + start: Position { line: 1, col: 1 }, | |
| 1478 | + end: Position { line: 1, col: 1 }, | |
| 1479 | + }; | |
| 1253 | 1480 | let st = super::super::symtab::SymbolTable::new(); |
| 1254 | 1481 | |
| 1255 | - let left = Box::new(Spanned::new(Expr::IntegerLiteral { text: "1".into(), kind: None }, span)); | |
| 1256 | - let right = Box::new(Spanned::new(Expr::RealLiteral { text: "2.0".into(), kind: None }, span)); | |
| 1257 | - let expr = Spanned::new(Expr::BinaryOp { op: BinaryOp::Add, left, right }, span); | |
| 1482 | + let left = Box::new(Spanned::new( | |
| 1483 | + Expr::IntegerLiteral { | |
| 1484 | + text: "1".into(), | |
| 1485 | + kind: None, | |
| 1486 | + }, | |
| 1487 | + span, | |
| 1488 | + )); | |
| 1489 | + let right = Box::new(Spanned::new( | |
| 1490 | + Expr::RealLiteral { | |
| 1491 | + text: "2.0".into(), | |
| 1492 | + kind: None, | |
| 1493 | + }, | |
| 1494 | + span, | |
| 1495 | + )); | |
| 1496 | + let expr = Spanned::new( | |
| 1497 | + Expr::BinaryOp { | |
| 1498 | + op: BinaryOp::Add, | |
| 1499 | + left, | |
| 1500 | + right, | |
| 1501 | + }, | |
| 1502 | + span, | |
| 1503 | + ); | |
| 1258 | 1504 | assert_eq!(expr_type(&expr, &st), FortranType::Real { kind: 4 }); |
| 1259 | 1505 | } |
| 1260 | 1506 | |
@@ -1262,31 +1508,61 @@ mod tests { | ||
| 1262 | 1508 | fn expr_type_unary_minus() { |
| 1263 | 1509 | use crate::ast::expr::{Expr, UnaryOp}; |
| 1264 | 1510 | use crate::ast::Spanned; |
| 1265 | - use crate::lexer::{Span, Position}; | |
| 1266 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1511 | + use crate::lexer::{Position, Span}; | |
| 1512 | + let span = Span { | |
| 1513 | + file_id: 0, | |
| 1514 | + start: Position { line: 1, col: 1 }, | |
| 1515 | + end: Position { line: 1, col: 1 }, | |
| 1516 | + }; | |
| 1267 | 1517 | let st = super::super::symtab::SymbolTable::new(); |
| 1268 | 1518 | |
| 1269 | - let operand = Box::new(Spanned::new(Expr::RealLiteral { text: "3.14".into(), kind: None }, span)); | |
| 1270 | - let expr = Spanned::new(Expr::UnaryOp { op: UnaryOp::Minus, operand }, span); | |
| 1519 | + let operand = Box::new(Spanned::new( | |
| 1520 | + Expr::RealLiteral { | |
| 1521 | + text: "3.14".into(), | |
| 1522 | + kind: None, | |
| 1523 | + }, | |
| 1524 | + span, | |
| 1525 | + )); | |
| 1526 | + let expr = Spanned::new( | |
| 1527 | + Expr::UnaryOp { | |
| 1528 | + op: UnaryOp::Minus, | |
| 1529 | + operand, | |
| 1530 | + }, | |
| 1531 | + span, | |
| 1532 | + ); | |
| 1271 | 1533 | assert_eq!(expr_type(&expr, &st), FortranType::Real { kind: 4 }); |
| 1272 | 1534 | } |
| 1273 | 1535 | |
| 1274 | 1536 | #[test] |
| 1275 | 1537 | fn expr_type_intrinsic_call() { |
| 1276 | - use crate::ast::expr::{Expr, Argument, SectionSubscript}; | |
| 1538 | + use crate::ast::expr::{Argument, Expr, SectionSubscript}; | |
| 1277 | 1539 | use crate::ast::Spanned; |
| 1278 | - use crate::lexer::{Span, Position}; | |
| 1279 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1540 | + use crate::lexer::{Position, Span}; | |
| 1541 | + let span = Span { | |
| 1542 | + file_id: 0, | |
| 1543 | + start: Position { line: 1, col: 1 }, | |
| 1544 | + end: Position { line: 1, col: 1 }, | |
| 1545 | + }; | |
| 1280 | 1546 | let st = super::super::symtab::SymbolTable::new(); |
| 1281 | 1547 | |
| 1282 | 1548 | let callee = Box::new(Spanned::new(Expr::Name { name: "abs".into() }, span)); |
| 1283 | 1549 | let arg = Argument { |
| 1284 | 1550 | keyword: None, |
| 1285 | 1551 | value: SectionSubscript::Element(Spanned::new( |
| 1286 | - Expr::IntegerLiteral { text: "-5".into(), kind: None }, span | |
| 1552 | + Expr::IntegerLiteral { | |
| 1553 | + text: "-5".into(), | |
| 1554 | + kind: None, | |
| 1555 | + }, | |
| 1556 | + span, | |
| 1287 | 1557 | )), |
| 1288 | 1558 | }; |
| 1289 | - let expr = Spanned::new(Expr::FunctionCall { callee, args: vec![arg] }, span); | |
| 1559 | + let expr = Spanned::new( | |
| 1560 | + Expr::FunctionCall { | |
| 1561 | + callee, | |
| 1562 | + args: vec![arg], | |
| 1563 | + }, | |
| 1564 | + span, | |
| 1565 | + ); | |
| 1290 | 1566 | assert_eq!(expr_type(&expr, &st), FortranType::Integer { kind: 4 }); |
| 1291 | 1567 | } |
| 1292 | 1568 | |
@@ -1294,22 +1570,36 @@ mod tests { | ||
| 1294 | 1570 | fn expr_type_paren() { |
| 1295 | 1571 | use crate::ast::expr::Expr; |
| 1296 | 1572 | use crate::ast::Spanned; |
| 1297 | - use crate::lexer::{Span, Position}; | |
| 1298 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1573 | + use crate::lexer::{Position, Span}; | |
| 1574 | + let span = Span { | |
| 1575 | + file_id: 0, | |
| 1576 | + start: Position { line: 1, col: 1 }, | |
| 1577 | + end: Position { line: 1, col: 1 }, | |
| 1578 | + }; | |
| 1299 | 1579 | let st = super::super::symtab::SymbolTable::new(); |
| 1300 | 1580 | |
| 1301 | - let inner = Box::new(Spanned::new(Expr::RealLiteral { text: "1.0".into(), kind: None }, span)); | |
| 1581 | + let inner = Box::new(Spanned::new( | |
| 1582 | + Expr::RealLiteral { | |
| 1583 | + text: "1.0".into(), | |
| 1584 | + kind: None, | |
| 1585 | + }, | |
| 1586 | + span, | |
| 1587 | + )); | |
| 1302 | 1588 | let expr = Spanned::new(Expr::ParenExpr { inner }, span); |
| 1303 | 1589 | assert_eq!(expr_type(&expr, &st), FortranType::Real { kind: 4 }); |
| 1304 | 1590 | } |
| 1305 | 1591 | |
| 1306 | 1592 | #[test] |
| 1307 | 1593 | fn expr_type_array_element() { |
| 1308 | - use crate::ast::expr::{Expr, Argument, SectionSubscript}; | |
| 1309 | - use crate::ast::Spanned; | |
| 1310 | - use crate::lexer::{Span, Position}; | |
| 1311 | 1594 | use super::super::symtab::*; |
| 1312 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1595 | + use crate::ast::expr::{Argument, Expr, SectionSubscript}; | |
| 1596 | + use crate::ast::Spanned; | |
| 1597 | + use crate::lexer::{Position, Span}; | |
| 1598 | + let span = Span { | |
| 1599 | + file_id: 0, | |
| 1600 | + start: Position { line: 1, col: 1 }, | |
| 1601 | + end: Position { line: 1, col: 1 }, | |
| 1602 | + }; | |
| 1313 | 1603 | |
| 1314 | 1604 | let mut st = SymbolTable::new(); |
| 1315 | 1605 | st.push_scope(ScopeKind::Program("main".into())); |
@@ -1322,51 +1612,88 @@ mod tests { | ||
| 1322 | 1612 | scope: 0, |
| 1323 | 1613 | arg_names: vec![], |
| 1324 | 1614 | const_value: None, |
| 1325 | - }).unwrap(); | |
| 1615 | + }) | |
| 1616 | + .unwrap(); | |
| 1326 | 1617 | |
| 1327 | 1618 | let callee = Box::new(Spanned::new(Expr::Name { name: "arr".into() }, span)); |
| 1328 | 1619 | let arg = Argument { |
| 1329 | 1620 | keyword: None, |
| 1330 | 1621 | value: SectionSubscript::Element(Spanned::new( |
| 1331 | - Expr::IntegerLiteral { text: "3".into(), kind: None }, span | |
| 1622 | + Expr::IntegerLiteral { | |
| 1623 | + text: "3".into(), | |
| 1624 | + kind: None, | |
| 1625 | + }, | |
| 1626 | + span, | |
| 1332 | 1627 | )), |
| 1333 | 1628 | }; |
| 1334 | - let expr = Spanned::new(Expr::FunctionCall { callee, args: vec![arg] }, span); | |
| 1629 | + let expr = Spanned::new( | |
| 1630 | + Expr::FunctionCall { | |
| 1631 | + callee, | |
| 1632 | + args: vec![arg], | |
| 1633 | + }, | |
| 1634 | + span, | |
| 1635 | + ); | |
| 1335 | 1636 | // arr(3) where arr is a variable → array element → real(8) |
| 1336 | 1637 | assert_eq!(expr_type(&expr, &st), FortranType::Real { kind: 8 }); |
| 1337 | 1638 | } |
| 1338 | 1639 | |
| 1339 | 1640 | #[test] |
| 1340 | 1641 | fn expr_type_substring() { |
| 1341 | - use crate::ast::expr::{Expr, Argument, SectionSubscript}; | |
| 1342 | - use crate::ast::Spanned; | |
| 1343 | - use crate::lexer::{Span, Position}; | |
| 1344 | 1642 | use super::super::symtab::*; |
| 1345 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 1643 | + use crate::ast::expr::{Argument, Expr, SectionSubscript}; | |
| 1644 | + use crate::ast::Spanned; | |
| 1645 | + use crate::lexer::{Position, Span}; | |
| 1646 | + let span = Span { | |
| 1647 | + file_id: 0, | |
| 1648 | + start: Position { line: 1, col: 1 }, | |
| 1649 | + end: Position { line: 1, col: 1 }, | |
| 1650 | + }; | |
| 1346 | 1651 | |
| 1347 | 1652 | let mut st = SymbolTable::new(); |
| 1348 | 1653 | st.push_scope(ScopeKind::Program("main".into())); |
| 1349 | 1654 | st.define(Symbol { |
| 1350 | 1655 | name: "s".into(), |
| 1351 | 1656 | kind: SymbolKind::Variable, |
| 1352 | - type_info: Some(TypeInfo::Character { len: Some(20), kind: None }), | |
| 1657 | + type_info: Some(TypeInfo::Character { | |
| 1658 | + len: Some(20), | |
| 1659 | + kind: None, | |
| 1660 | + }), | |
| 1353 | 1661 | attrs: SymbolAttrs::default(), |
| 1354 | 1662 | defined_at: span, |
| 1355 | 1663 | scope: 0, |
| 1356 | 1664 | arg_names: vec![], |
| 1357 | 1665 | const_value: None, |
| 1358 | - }).unwrap(); | |
| 1666 | + }) | |
| 1667 | + .unwrap(); | |
| 1359 | 1668 | |
| 1360 | 1669 | let callee = Box::new(Spanned::new(Expr::Name { name: "s".into() }, span)); |
| 1361 | 1670 | let arg = Argument { |
| 1362 | 1671 | keyword: None, |
| 1363 | 1672 | value: SectionSubscript::Range { |
| 1364 | - start: Some(Spanned::new(Expr::IntegerLiteral { text: "1".into(), kind: None }, span)), | |
| 1365 | - end: Some(Spanned::new(Expr::IntegerLiteral { text: "5".into(), kind: None }, span)), | |
| 1673 | + start: Some(Spanned::new( | |
| 1674 | + Expr::IntegerLiteral { | |
| 1675 | + text: "1".into(), | |
| 1676 | + kind: None, | |
| 1677 | + }, | |
| 1678 | + span, | |
| 1679 | + )), | |
| 1680 | + end: Some(Spanned::new( | |
| 1681 | + Expr::IntegerLiteral { | |
| 1682 | + text: "5".into(), | |
| 1683 | + kind: None, | |
| 1684 | + }, | |
| 1685 | + span, | |
| 1686 | + )), | |
| 1366 | 1687 | stride: None, |
| 1367 | 1688 | }, |
| 1368 | 1689 | }; |
| 1369 | - let expr = Spanned::new(Expr::FunctionCall { callee, args: vec![arg] }, span); | |
| 1690 | + let expr = Spanned::new( | |
| 1691 | + Expr::FunctionCall { | |
| 1692 | + callee, | |
| 1693 | + args: vec![arg], | |
| 1694 | + }, | |
| 1695 | + span, | |
| 1696 | + ); | |
| 1370 | 1697 | // s(1:5) where s is character variable → substring |
| 1371 | 1698 | assert!(expr_type(&expr, &st).is_character()); |
| 1372 | 1699 | } |
@@ -1377,8 +1704,18 @@ mod tests { | ||
| 1377 | 1704 | fn check_args_positional_ok() { |
| 1378 | 1705 | use super::super::symtab::Intent; |
| 1379 | 1706 | let dummies = vec![ |
| 1380 | - DummyArgDesc { name: "a".into(), type_: FortranType::Real { kind: 4 }, intent: Some(Intent::In), optional: false }, | |
| 1381 | - DummyArgDesc { name: "n".into(), type_: FortranType::Integer { kind: 4 }, intent: Some(Intent::In), optional: false }, | |
| 1707 | + DummyArgDesc { | |
| 1708 | + name: "a".into(), | |
| 1709 | + type_: FortranType::Real { kind: 4 }, | |
| 1710 | + intent: Some(Intent::In), | |
| 1711 | + optional: false, | |
| 1712 | + }, | |
| 1713 | + DummyArgDesc { | |
| 1714 | + name: "n".into(), | |
| 1715 | + type_: FortranType::Integer { kind: 4 }, | |
| 1716 | + intent: Some(Intent::In), | |
| 1717 | + optional: false, | |
| 1718 | + }, | |
| 1382 | 1719 | ]; |
| 1383 | 1720 | let actuals = vec![ |
| 1384 | 1721 | (None, FortranType::Real { kind: 4 }), |
@@ -1391,8 +1728,18 @@ mod tests { | ||
| 1391 | 1728 | fn check_args_keyword_ok() { |
| 1392 | 1729 | use super::super::symtab::Intent; |
| 1393 | 1730 | let dummies = vec![ |
| 1394 | - DummyArgDesc { name: "a".into(), type_: FortranType::Real { kind: 4 }, intent: Some(Intent::In), optional: false }, | |
| 1395 | - DummyArgDesc { name: "n".into(), type_: FortranType::Integer { kind: 4 }, intent: Some(Intent::In), optional: false }, | |
| 1731 | + DummyArgDesc { | |
| 1732 | + name: "a".into(), | |
| 1733 | + type_: FortranType::Real { kind: 4 }, | |
| 1734 | + intent: Some(Intent::In), | |
| 1735 | + optional: false, | |
| 1736 | + }, | |
| 1737 | + DummyArgDesc { | |
| 1738 | + name: "n".into(), | |
| 1739 | + type_: FortranType::Integer { kind: 4 }, | |
| 1740 | + intent: Some(Intent::In), | |
| 1741 | + optional: false, | |
| 1742 | + }, | |
| 1396 | 1743 | ]; |
| 1397 | 1744 | let actuals = vec![ |
| 1398 | 1745 | (Some("n".into()), FortranType::Integer { kind: 4 }), |
@@ -1404,24 +1751,40 @@ mod tests { | ||
| 1404 | 1751 | #[test] |
| 1405 | 1752 | fn check_args_optional_omitted_ok() { |
| 1406 | 1753 | let dummies = vec![ |
| 1407 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1408 | - DummyArgDesc { name: "verbose".into(), type_: FortranType::default_logical(), intent: None, optional: true }, | |
| 1409 | - ]; | |
| 1410 | - let actuals = vec![ | |
| 1411 | - (None, FortranType::Real { kind: 4 }), | |
| 1754 | + DummyArgDesc { | |
| 1755 | + name: "x".into(), | |
| 1756 | + type_: FortranType::Real { kind: 4 }, | |
| 1757 | + intent: None, | |
| 1758 | + optional: false, | |
| 1759 | + }, | |
| 1760 | + DummyArgDesc { | |
| 1761 | + name: "verbose".into(), | |
| 1762 | + type_: FortranType::default_logical(), | |
| 1763 | + intent: None, | |
| 1764 | + optional: true, | |
| 1765 | + }, | |
| 1412 | 1766 | ]; |
| 1767 | + let actuals = vec![(None, FortranType::Real { kind: 4 })]; | |
| 1413 | 1768 | assert!(check_arguments(&dummies, &actuals).is_empty()); |
| 1414 | 1769 | } |
| 1415 | 1770 | |
| 1416 | 1771 | #[test] |
| 1417 | 1772 | fn check_args_missing_required() { |
| 1418 | 1773 | let dummies = vec![ |
| 1419 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1420 | - DummyArgDesc { name: "y".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1421 | - ]; | |
| 1422 | - let actuals = vec![ | |
| 1423 | - (None, FortranType::Real { kind: 4 }), | |
| 1774 | + DummyArgDesc { | |
| 1775 | + name: "x".into(), | |
| 1776 | + type_: FortranType::Real { kind: 4 }, | |
| 1777 | + intent: None, | |
| 1778 | + optional: false, | |
| 1779 | + }, | |
| 1780 | + DummyArgDesc { | |
| 1781 | + name: "y".into(), | |
| 1782 | + type_: FortranType::Real { kind: 4 }, | |
| 1783 | + intent: None, | |
| 1784 | + optional: false, | |
| 1785 | + }, | |
| 1424 | 1786 | ]; |
| 1787 | + let actuals = vec![(None, FortranType::Real { kind: 4 })]; | |
| 1425 | 1788 | let errs = check_arguments(&dummies, &actuals); |
| 1426 | 1789 | assert_eq!(errs.len(), 1); |
| 1427 | 1790 | assert!(errs[0].contains("missing required argument 'y'")); |
@@ -1429,9 +1792,12 @@ mod tests { | ||
| 1429 | 1792 | |
| 1430 | 1793 | #[test] |
| 1431 | 1794 | fn check_args_too_many() { |
| 1432 | - let dummies = vec![ | |
| 1433 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1434 | - ]; | |
| 1795 | + let dummies = vec![DummyArgDesc { | |
| 1796 | + name: "x".into(), | |
| 1797 | + type_: FortranType::Real { kind: 4 }, | |
| 1798 | + intent: None, | |
| 1799 | + optional: false, | |
| 1800 | + }]; | |
| 1435 | 1801 | let actuals = vec![ |
| 1436 | 1802 | (None, FortranType::Real { kind: 4 }), |
| 1437 | 1803 | (None, FortranType::Real { kind: 4 }), |
@@ -1443,12 +1809,13 @@ mod tests { | ||
| 1443 | 1809 | |
| 1444 | 1810 | #[test] |
| 1445 | 1811 | fn check_args_type_mismatch() { |
| 1446 | - let dummies = vec![ | |
| 1447 | - DummyArgDesc { name: "x".into(), type_: FortranType::default_logical(), intent: None, optional: false }, | |
| 1448 | - ]; | |
| 1449 | - let actuals = vec![ | |
| 1450 | - (None, FortranType::Integer { kind: 4 }), | |
| 1451 | - ]; | |
| 1812 | + let dummies = vec![DummyArgDesc { | |
| 1813 | + name: "x".into(), | |
| 1814 | + type_: FortranType::default_logical(), | |
| 1815 | + intent: None, | |
| 1816 | + optional: false, | |
| 1817 | + }]; | |
| 1818 | + let actuals = vec![(None, FortranType::Integer { kind: 4 })]; | |
| 1452 | 1819 | let errs = check_arguments(&dummies, &actuals); |
| 1453 | 1820 | assert_eq!(errs.len(), 1); |
| 1454 | 1821 | assert!(errs[0].contains("type mismatch")); |
@@ -1456,21 +1823,25 @@ mod tests { | ||
| 1456 | 1823 | |
| 1457 | 1824 | #[test] |
| 1458 | 1825 | fn check_args_numeric_conversion_allowed() { |
| 1459 | - let dummies = vec![ | |
| 1460 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 8 }, intent: None, optional: false }, | |
| 1461 | - ]; | |
| 1462 | - let actuals = vec![ | |
| 1463 | - (None, FortranType::Integer { kind: 4 }), | |
| 1464 | - ]; | |
| 1826 | + let dummies = vec![DummyArgDesc { | |
| 1827 | + name: "x".into(), | |
| 1828 | + type_: FortranType::Real { kind: 8 }, | |
| 1829 | + intent: None, | |
| 1830 | + optional: false, | |
| 1831 | + }]; | |
| 1832 | + let actuals = vec![(None, FortranType::Integer { kind: 4 })]; | |
| 1465 | 1833 | // integer → real conversion allowed |
| 1466 | 1834 | assert!(check_arguments(&dummies, &actuals).is_empty()); |
| 1467 | 1835 | } |
| 1468 | 1836 | |
| 1469 | 1837 | #[test] |
| 1470 | 1838 | fn check_args_duplicate_keyword() { |
| 1471 | - let dummies = vec![ | |
| 1472 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1473 | - ]; | |
| 1839 | + let dummies = vec![DummyArgDesc { | |
| 1840 | + name: "x".into(), | |
| 1841 | + type_: FortranType::Real { kind: 4 }, | |
| 1842 | + intent: None, | |
| 1843 | + optional: false, | |
| 1844 | + }]; | |
| 1474 | 1845 | let actuals = vec![ |
| 1475 | 1846 | (Some("x".into()), FortranType::Real { kind: 4 }), |
| 1476 | 1847 | (Some("x".into()), FortranType::Real { kind: 4 }), |
@@ -1481,12 +1852,13 @@ mod tests { | ||
| 1481 | 1852 | |
| 1482 | 1853 | #[test] |
| 1483 | 1854 | fn check_args_unknown_keyword() { |
| 1484 | - let dummies = vec![ | |
| 1485 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1486 | - ]; | |
| 1487 | - let actuals = vec![ | |
| 1488 | - (Some("bogus".into()), FortranType::Real { kind: 4 }), | |
| 1489 | - ]; | |
| 1855 | + let dummies = vec![DummyArgDesc { | |
| 1856 | + name: "x".into(), | |
| 1857 | + type_: FortranType::Real { kind: 4 }, | |
| 1858 | + intent: None, | |
| 1859 | + optional: false, | |
| 1860 | + }]; | |
| 1861 | + let actuals = vec![(Some("bogus".into()), FortranType::Real { kind: 4 })]; | |
| 1490 | 1862 | let errs = check_arguments(&dummies, &actuals); |
| 1491 | 1863 | assert!(errs.iter().any(|e| e.contains("unknown keyword"))); |
| 1492 | 1864 | } |
@@ -1499,43 +1871,77 @@ mod tests { | ||
| 1499 | 1871 | SpecificProc { |
| 1500 | 1872 | name: "swap_int".into(), |
| 1501 | 1873 | dummy_args: vec![ |
| 1502 | - DummyArgDesc { name: "a".into(), type_: FortranType::Integer { kind: 4 }, intent: None, optional: false }, | |
| 1503 | - DummyArgDesc { name: "b".into(), type_: FortranType::Integer { kind: 4 }, intent: None, optional: false }, | |
| 1874 | + DummyArgDesc { | |
| 1875 | + name: "a".into(), | |
| 1876 | + type_: FortranType::Integer { kind: 4 }, | |
| 1877 | + intent: None, | |
| 1878 | + optional: false, | |
| 1879 | + }, | |
| 1880 | + DummyArgDesc { | |
| 1881 | + name: "b".into(), | |
| 1882 | + type_: FortranType::Integer { kind: 4 }, | |
| 1883 | + intent: None, | |
| 1884 | + optional: false, | |
| 1885 | + }, | |
| 1504 | 1886 | ], |
| 1505 | 1887 | result_type: FortranType::Void, |
| 1506 | 1888 | }, |
| 1507 | 1889 | SpecificProc { |
| 1508 | 1890 | name: "swap_real".into(), |
| 1509 | 1891 | dummy_args: vec![ |
| 1510 | - DummyArgDesc { name: "a".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1511 | - DummyArgDesc { name: "b".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1892 | + DummyArgDesc { | |
| 1893 | + name: "a".into(), | |
| 1894 | + type_: FortranType::Real { kind: 4 }, | |
| 1895 | + intent: None, | |
| 1896 | + optional: false, | |
| 1897 | + }, | |
| 1898 | + DummyArgDesc { | |
| 1899 | + name: "b".into(), | |
| 1900 | + type_: FortranType::Real { kind: 4 }, | |
| 1901 | + intent: None, | |
| 1902 | + optional: false, | |
| 1903 | + }, | |
| 1512 | 1904 | ], |
| 1513 | 1905 | result_type: FortranType::Void, |
| 1514 | 1906 | }, |
| 1515 | 1907 | ]; |
| 1516 | 1908 | |
| 1517 | 1909 | // integer args → swap_int (index 0) |
| 1518 | - assert_eq!(resolve_generic(&specifics, &[ | |
| 1519 | - FortranType::Integer { kind: 4 }, FortranType::Integer { kind: 4 } | |
| 1520 | - ]).unwrap(), 0); | |
| 1910 | + assert_eq!( | |
| 1911 | + resolve_generic( | |
| 1912 | + &specifics, | |
| 1913 | + &[ | |
| 1914 | + FortranType::Integer { kind: 4 }, | |
| 1915 | + FortranType::Integer { kind: 4 } | |
| 1916 | + ] | |
| 1917 | + ) | |
| 1918 | + .unwrap(), | |
| 1919 | + 0 | |
| 1920 | + ); | |
| 1521 | 1921 | |
| 1522 | 1922 | // real args → swap_real (index 1) |
| 1523 | - assert_eq!(resolve_generic(&specifics, &[ | |
| 1524 | - FortranType::Real { kind: 4 }, FortranType::Real { kind: 4 } | |
| 1525 | - ]).unwrap(), 1); | |
| 1923 | + assert_eq!( | |
| 1924 | + resolve_generic( | |
| 1925 | + &specifics, | |
| 1926 | + &[FortranType::Real { kind: 4 }, FortranType::Real { kind: 4 }] | |
| 1927 | + ) | |
| 1928 | + .unwrap(), | |
| 1929 | + 1 | |
| 1930 | + ); | |
| 1526 | 1931 | } |
| 1527 | 1932 | |
| 1528 | 1933 | #[test] |
| 1529 | 1934 | fn resolve_generic_no_match() { |
| 1530 | - let specifics = vec![ | |
| 1531 | - SpecificProc { | |
| 1532 | - name: "foo_int".into(), | |
| 1533 | - dummy_args: vec![ | |
| 1534 | - DummyArgDesc { name: "x".into(), type_: FortranType::Integer { kind: 4 }, intent: None, optional: false }, | |
| 1535 | - ], | |
| 1536 | - result_type: FortranType::Void, | |
| 1537 | - }, | |
| 1538 | - ]; | |
| 1935 | + let specifics = vec![SpecificProc { | |
| 1936 | + name: "foo_int".into(), | |
| 1937 | + dummy_args: vec![DummyArgDesc { | |
| 1938 | + name: "x".into(), | |
| 1939 | + type_: FortranType::Integer { kind: 4 }, | |
| 1940 | + intent: None, | |
| 1941 | + optional: false, | |
| 1942 | + }], | |
| 1943 | + result_type: FortranType::Void, | |
| 1944 | + }]; | |
| 1539 | 1945 | assert!(resolve_generic(&specifics, &[FortranType::Real { kind: 4 }]).is_err()); |
| 1540 | 1946 | } |
| 1541 | 1947 | |
@@ -1545,42 +1951,76 @@ mod tests { | ||
| 1545 | 1951 | SpecificProc { |
| 1546 | 1952 | name: "swap_int".into(), |
| 1547 | 1953 | dummy_args: vec![ |
| 1548 | - DummyArgDesc { name: "a".into(), type_: FortranType::Integer { kind: 4 }, intent: None, optional: false }, | |
| 1549 | - DummyArgDesc { name: "b".into(), type_: FortranType::Integer { kind: 4 }, intent: None, optional: false }, | |
| 1954 | + DummyArgDesc { | |
| 1955 | + name: "a".into(), | |
| 1956 | + type_: FortranType::Integer { kind: 4 }, | |
| 1957 | + intent: None, | |
| 1958 | + optional: false, | |
| 1959 | + }, | |
| 1960 | + DummyArgDesc { | |
| 1961 | + name: "b".into(), | |
| 1962 | + type_: FortranType::Integer { kind: 4 }, | |
| 1963 | + intent: None, | |
| 1964 | + optional: false, | |
| 1965 | + }, | |
| 1550 | 1966 | ], |
| 1551 | 1967 | result_type: FortranType::Void, |
| 1552 | 1968 | }, |
| 1553 | 1969 | SpecificProc { |
| 1554 | 1970 | name: "swap_real".into(), |
| 1555 | 1971 | dummy_args: vec![ |
| 1556 | - DummyArgDesc { name: "a".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1557 | - DummyArgDesc { name: "b".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1972 | + DummyArgDesc { | |
| 1973 | + name: "a".into(), | |
| 1974 | + type_: FortranType::Real { kind: 4 }, | |
| 1975 | + intent: None, | |
| 1976 | + optional: false, | |
| 1977 | + }, | |
| 1978 | + DummyArgDesc { | |
| 1979 | + name: "b".into(), | |
| 1980 | + type_: FortranType::Real { kind: 4 }, | |
| 1981 | + intent: None, | |
| 1982 | + optional: false, | |
| 1983 | + }, | |
| 1558 | 1984 | ], |
| 1559 | 1985 | result_type: FortranType::Void, |
| 1560 | 1986 | }, |
| 1561 | 1987 | ]; |
| 1562 | 1988 | // mixed integer + real → no match (exact type required for disambiguation) |
| 1563 | - assert!(resolve_generic(&specifics, &[ | |
| 1564 | - FortranType::Integer { kind: 4 }, FortranType::Real { kind: 4 } | |
| 1565 | - ]).is_err()); | |
| 1989 | + assert!(resolve_generic( | |
| 1990 | + &specifics, | |
| 1991 | + &[ | |
| 1992 | + FortranType::Integer { kind: 4 }, | |
| 1993 | + FortranType::Real { kind: 4 } | |
| 1994 | + ] | |
| 1995 | + ) | |
| 1996 | + .is_err()); | |
| 1566 | 1997 | } |
| 1567 | 1998 | |
| 1568 | 1999 | #[test] |
| 1569 | 2000 | fn resolve_generic_with_optional() { |
| 1570 | - let specifics = vec![ | |
| 1571 | - SpecificProc { | |
| 1572 | - name: "process".into(), | |
| 1573 | - dummy_args: vec![ | |
| 1574 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1575 | - DummyArgDesc { name: "mask".into(), type_: FortranType::default_logical(), intent: None, optional: true }, | |
| 1576 | - ], | |
| 1577 | - result_type: FortranType::Void, | |
| 1578 | - }, | |
| 1579 | - ]; | |
| 2001 | + let specifics = vec![SpecificProc { | |
| 2002 | + name: "process".into(), | |
| 2003 | + dummy_args: vec![ | |
| 2004 | + DummyArgDesc { | |
| 2005 | + name: "x".into(), | |
| 2006 | + type_: FortranType::Real { kind: 4 }, | |
| 2007 | + intent: None, | |
| 2008 | + optional: false, | |
| 2009 | + }, | |
| 2010 | + DummyArgDesc { | |
| 2011 | + name: "mask".into(), | |
| 2012 | + type_: FortranType::default_logical(), | |
| 2013 | + intent: None, | |
| 2014 | + optional: true, | |
| 2015 | + }, | |
| 2016 | + ], | |
| 2017 | + result_type: FortranType::Void, | |
| 2018 | + }]; | |
| 1580 | 2019 | // Only required arg supplied — should match |
| 1581 | - assert_eq!(resolve_generic(&specifics, &[ | |
| 1582 | - FortranType::Real { kind: 4 } | |
| 1583 | - ]).unwrap(), 0); | |
| 2020 | + assert_eq!( | |
| 2021 | + resolve_generic(&specifics, &[FortranType::Real { kind: 4 }]).unwrap(), | |
| 2022 | + 0 | |
| 2023 | + ); | |
| 1584 | 2024 | } |
| 1585 | 2025 | |
| 1586 | 2026 | // ---- Logical result type ---- |
@@ -1601,7 +2041,8 @@ mod tests { | ||
| 1601 | 2041 | let result = binary_logical_result_type( |
| 1602 | 2042 | &FortranType::Logical { kind: 1 }, |
| 1603 | 2043 | &FortranType::Logical { kind: 8 }, |
| 1604 | - ).unwrap(); | |
| 2044 | + ) | |
| 2045 | + .unwrap(); | |
| 1605 | 2046 | assert_eq!(result, FortranType::Logical { kind: 8 }); |
| 1606 | 2047 | } |
| 1607 | 2048 | |
@@ -1613,7 +2054,8 @@ mod tests { | ||
| 1613 | 2054 | let result = arithmetic_result_type( |
| 1614 | 2055 | &FortranType::Real { kind: 8 }, |
| 1615 | 2056 | &FortranType::Complex { kind: 4 }, |
| 1616 | - ).unwrap(); | |
| 2057 | + ) | |
| 2058 | + .unwrap(); | |
| 1617 | 2059 | assert_eq!(result, FortranType::Complex { kind: 8 }); |
| 1618 | 2060 | } |
| 1619 | 2061 | |
@@ -1623,7 +2065,8 @@ mod tests { | ||
| 1623 | 2065 | let result = arithmetic_result_type( |
| 1624 | 2066 | &FortranType::Integer { kind: 8 }, |
| 1625 | 2067 | &FortranType::Real { kind: 4 }, |
| 1626 | - ).unwrap(); | |
| 2068 | + ) | |
| 2069 | + .unwrap(); | |
| 1627 | 2070 | assert_eq!(result, FortranType::Real { kind: 4 }); |
| 1628 | 2071 | } |
| 1629 | 2072 | |
@@ -1633,7 +2076,8 @@ mod tests { | ||
| 1633 | 2076 | let result = arithmetic_result_type( |
| 1634 | 2077 | &FortranType::Integer { kind: 8 }, |
| 1635 | 2078 | &FortranType::Complex { kind: 4 }, |
| 1636 | - ).unwrap(); | |
| 2079 | + ) | |
| 2080 | + .unwrap(); | |
| 1637 | 2081 | assert_eq!(result, FortranType::Complex { kind: 4 }); |
| 1638 | 2082 | } |
| 1639 | 2083 | |
@@ -1643,7 +2087,8 @@ mod tests { | ||
| 1643 | 2087 | let result = arithmetic_result_type( |
| 1644 | 2088 | &FortranType::Real { kind: 4 }, |
| 1645 | 2089 | &FortranType::Integer { kind: 8 }, |
| 1646 | - ).unwrap(); | |
| 2090 | + ) | |
| 2091 | + .unwrap(); | |
| 1647 | 2092 | assert_eq!(result, FortranType::Real { kind: 4 }); |
| 1648 | 2093 | } |
| 1649 | 2094 | |
@@ -1652,15 +2097,27 @@ mod tests { | ||
| 1652 | 2097 | #[test] |
| 1653 | 2098 | fn positional_after_keyword_rejected() { |
| 1654 | 2099 | let dummies = vec![ |
| 1655 | - DummyArgDesc { name: "a".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 1656 | - DummyArgDesc { name: "b".into(), type_: FortranType::Real { kind: 4 }, intent: None, optional: false }, | |
| 2100 | + DummyArgDesc { | |
| 2101 | + name: "a".into(), | |
| 2102 | + type_: FortranType::Real { kind: 4 }, | |
| 2103 | + intent: None, | |
| 2104 | + optional: false, | |
| 2105 | + }, | |
| 2106 | + DummyArgDesc { | |
| 2107 | + name: "b".into(), | |
| 2108 | + type_: FortranType::Real { kind: 4 }, | |
| 2109 | + intent: None, | |
| 2110 | + optional: false, | |
| 2111 | + }, | |
| 1657 | 2112 | ]; |
| 1658 | 2113 | let actuals = vec![ |
| 1659 | 2114 | (Some("a".into()), FortranType::Real { kind: 4 }), |
| 1660 | 2115 | (None, FortranType::Real { kind: 4 }), // positional after keyword |
| 1661 | 2116 | ]; |
| 1662 | 2117 | let errs = check_arguments(&dummies, &actuals); |
| 1663 | - assert!(errs.iter().any(|e| e.contains("positional argument after keyword"))); | |
| 2118 | + assert!(errs | |
| 2119 | + .iter() | |
| 2120 | + .any(|e| e.contains("positional argument after keyword"))); | |
| 1664 | 2121 | } |
| 1665 | 2122 | |
| 1666 | 2123 | // ---- Audit fix: M5 — intent(out/inout) rejects numeric conversion ---- |
@@ -1668,12 +2125,13 @@ mod tests { | ||
| 1668 | 2125 | #[test] |
| 1669 | 2126 | fn intent_inout_rejects_numeric_conversion() { |
| 1670 | 2127 | use super::super::symtab::Intent; |
| 1671 | - let dummies = vec![ | |
| 1672 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 8 }, intent: Some(Intent::InOut), optional: false }, | |
| 1673 | - ]; | |
| 1674 | - let actuals = vec![ | |
| 1675 | - (None, FortranType::Integer { kind: 4 }), | |
| 1676 | - ]; | |
| 2128 | + let dummies = vec![DummyArgDesc { | |
| 2129 | + name: "x".into(), | |
| 2130 | + type_: FortranType::Real { kind: 8 }, | |
| 2131 | + intent: Some(Intent::InOut), | |
| 2132 | + optional: false, | |
| 2133 | + }]; | |
| 2134 | + let actuals = vec![(None, FortranType::Integer { kind: 4 })]; | |
| 1677 | 2135 | let errs = check_arguments(&dummies, &actuals); |
| 1678 | 2136 | assert!(!errs.is_empty()); |
| 1679 | 2137 | assert!(errs[0].contains("intent(out/inout)")); |
@@ -1682,12 +2140,13 @@ mod tests { | ||
| 1682 | 2140 | #[test] |
| 1683 | 2141 | fn intent_in_allows_numeric_conversion() { |
| 1684 | 2142 | use super::super::symtab::Intent; |
| 1685 | - let dummies = vec![ | |
| 1686 | - DummyArgDesc { name: "x".into(), type_: FortranType::Real { kind: 8 }, intent: Some(Intent::In), optional: false }, | |
| 1687 | - ]; | |
| 1688 | - let actuals = vec![ | |
| 1689 | - (None, FortranType::Integer { kind: 4 }), | |
| 1690 | - ]; | |
| 2143 | + let dummies = vec![DummyArgDesc { | |
| 2144 | + name: "x".into(), | |
| 2145 | + type_: FortranType::Real { kind: 8 }, | |
| 2146 | + intent: Some(Intent::In), | |
| 2147 | + optional: false, | |
| 2148 | + }]; | |
| 2149 | + let actuals = vec![(None, FortranType::Integer { kind: 4 })]; | |
| 1691 | 2150 | assert!(check_arguments(&dummies, &actuals).is_empty()); |
| 1692 | 2151 | } |
| 1693 | 2152 | |
@@ -1728,7 +2187,8 @@ mod tests { | ||
| 1728 | 2187 | assert!(needs_conversion( |
| 1729 | 2188 | &FortranType::Complex { kind: 4 }, |
| 1730 | 2189 | &FortranType::Integer { kind: 4 }, |
| 1731 | - ).is_none()); | |
| 2190 | + ) | |
| 2191 | + .is_none()); | |
| 1732 | 2192 | } |
| 1733 | 2193 | |
| 1734 | 2194 | #[test] |
@@ -1736,7 +2196,8 @@ mod tests { | ||
| 1736 | 2196 | assert!(needs_conversion( |
| 1737 | 2197 | &FortranType::Complex { kind: 4 }, |
| 1738 | 2198 | &FortranType::Real { kind: 4 }, |
| 1739 | - ).is_none()); | |
| 2199 | + ) | |
| 2200 | + .is_none()); | |
| 1740 | 2201 | } |
| 1741 | 2202 | |
| 1742 | 2203 | #[test] |
@@ -1744,7 +2205,8 @@ mod tests { | ||
| 1744 | 2205 | let conv = needs_conversion( |
| 1745 | 2206 | &FortranType::Integer { kind: 4 }, |
| 1746 | 2207 | &FortranType::Complex { kind: 4 }, |
| 1747 | - ).unwrap(); | |
| 2208 | + ) | |
| 2209 | + .unwrap(); | |
| 1748 | 2210 | assert_eq!(conv, FortranType::Complex { kind: 4 }); |
| 1749 | 2211 | } |
| 1750 | 2212 | |
@@ -1753,9 +2215,16 @@ mod tests { | ||
| 1753 | 2215 | #[test] |
| 1754 | 2216 | fn concat_mismatched_kind_returns_none() { |
| 1755 | 2217 | assert!(concat_result_type( |
| 1756 | - &FortranType::Character { kind: 1, len: CharLen::Known(5) }, | |
| 1757 | - &FortranType::Character { kind: 4, len: CharLen::Known(5) }, | |
| 1758 | - ).is_none()); | |
| 2218 | + &FortranType::Character { | |
| 2219 | + kind: 1, | |
| 2220 | + len: CharLen::Known(5) | |
| 2221 | + }, | |
| 2222 | + &FortranType::Character { | |
| 2223 | + kind: 4, | |
| 2224 | + len: CharLen::Known(5) | |
| 2225 | + }, | |
| 2226 | + ) | |
| 2227 | + .is_none()); | |
| 1759 | 2228 | } |
| 1760 | 2229 | |
| 1761 | 2230 | // ---- New intrinsics ---- |
@@ -1768,9 +2237,13 @@ mod tests { | ||
| 1768 | 2237 | |
| 1769 | 2238 | #[test] |
| 1770 | 2239 | fn intrinsic_c_associated() { |
| 1771 | - let result = intrinsic_result_type("c_associated", &[ | |
| 1772 | - FortranType::Derived { name: "c_ptr".into() } | |
| 1773 | - ]).unwrap(); | |
| 2240 | + let result = intrinsic_result_type( | |
| 2241 | + "c_associated", | |
| 2242 | + &[FortranType::Derived { | |
| 2243 | + name: "c_ptr".into(), | |
| 2244 | + }], | |
| 2245 | + ) | |
| 2246 | + .unwrap(); | |
| 1774 | 2247 | assert_eq!(result, FortranType::default_logical()); |
| 1775 | 2248 | } |
| 1776 | 2249 | |
@@ -1782,11 +2255,15 @@ mod tests { | ||
| 1782 | 2255 | |
| 1783 | 2256 | #[test] |
| 1784 | 2257 | fn intrinsic_merge() { |
| 1785 | - let result = intrinsic_result_type("merge", &[ | |
| 1786 | - FortranType::Integer { kind: 4 }, | |
| 1787 | - FortranType::Integer { kind: 4 }, | |
| 1788 | - FortranType::default_logical(), | |
| 1789 | - ]).unwrap(); | |
| 2258 | + let result = intrinsic_result_type( | |
| 2259 | + "merge", | |
| 2260 | + &[ | |
| 2261 | + FortranType::Integer { kind: 4 }, | |
| 2262 | + FortranType::Integer { kind: 4 }, | |
| 2263 | + FortranType::default_logical(), | |
| 2264 | + ], | |
| 2265 | + ) | |
| 2266 | + .unwrap(); | |
| 1790 | 2267 | assert_eq!(result, FortranType::Integer { kind: 4 }); |
| 1791 | 2268 | } |
| 1792 | 2269 | } |
src/sema/validate.rsmodified@@ -4,12 +4,12 @@ | ||
| 4 | 4 | //! constraints, label validation, and standard conformance. Runs after |
| 5 | 5 | //! symbol resolution (resolve.rs) and type checking (types.rs). |
| 6 | 6 | |
| 7 | -use crate::ast::unit::*; | |
| 8 | -use crate::ast::stmt::*; | |
| 7 | +use super::symtab::*; | |
| 8 | +use crate::ast::decl::{Attribute, Decl, TypeAttr, TypeSpec}; | |
| 9 | 9 | use crate::ast::expr::Expr; |
| 10 | -use crate::ast::decl::{Decl, Attribute, TypeAttr, TypeSpec}; | |
| 10 | +use crate::ast::stmt::*; | |
| 11 | +use crate::ast::unit::*; | |
| 11 | 12 | use crate::lexer::Span; |
| 12 | -use super::symtab::*; | |
| 13 | 13 | use std::cell::RefCell; |
| 14 | 14 | |
| 15 | 15 | /// Fortran standard level for --std= conformance checking. |
@@ -60,7 +60,11 @@ impl std::fmt::Display for Diagnostic { | ||
| 60 | 60 | DiagKind::Error => "error", |
| 61 | 61 | DiagKind::Warning => "warning", |
| 62 | 62 | }; |
| 63 | - write!(f, "{}:{}: {}: {}", self.span.start.line, self.span.start.col, label, self.msg) | |
| 63 | + write!( | |
| 64 | + f, | |
| 65 | + "{}:{}: {}: {}", | |
| 66 | + self.span.start.line, self.span.start.col, label, self.msg | |
| 67 | + ) | |
| 64 | 68 | } |
| 65 | 69 | } |
| 66 | 70 | |
@@ -128,7 +132,10 @@ impl<'a> Ctx<'a> { | ||
| 128 | 132 | fn require_std(&mut self, span: Span, min: FortranStandard, feature: &str) { |
| 129 | 133 | if let Some(selected) = self.std { |
| 130 | 134 | if selected < min { |
| 131 | - self.error(span, format!("{} requires --std={:?} or later", feature, min)); | |
| 135 | + self.error( | |
| 136 | + span, | |
| 137 | + format!("{} requires --std={:?} or later", feature, min), | |
| 138 | + ); | |
| 132 | 139 | } |
| 133 | 140 | } |
| 134 | 141 | } |
@@ -145,11 +152,19 @@ impl<'a> Ctx<'a> { | ||
| 145 | 152 | } |
| 146 | 153 | |
| 147 | 154 | fn error(&mut self, span: Span, msg: impl Into<String>) { |
| 148 | - self.diags.push(Diagnostic { span, kind: DiagKind::Error, msg: msg.into() }); | |
| 155 | + self.diags.push(Diagnostic { | |
| 156 | + span, | |
| 157 | + kind: DiagKind::Error, | |
| 158 | + msg: msg.into(), | |
| 159 | + }); | |
| 149 | 160 | } |
| 150 | 161 | |
| 151 | 162 | fn warning(&mut self, span: Span, msg: impl Into<String>) { |
| 152 | - self.diags.push(Diagnostic { span, kind: DiagKind::Warning, msg: msg.into() }); | |
| 163 | + self.diags.push(Diagnostic { | |
| 164 | + span, | |
| 165 | + kind: DiagKind::Warning, | |
| 166 | + msg: msg.into(), | |
| 167 | + }); | |
| 153 | 168 | } |
| 154 | 169 | } |
| 155 | 170 | |
@@ -190,14 +205,7 @@ pub fn validate_file_with_layouts( | ||
| 190 | 205 | std: Option<FortranStandard>, |
| 191 | 206 | type_layouts: &crate::sema::type_layout::TypeLayoutRegistry, |
| 192 | 207 | ) -> Vec<Diagnostic> { |
| 193 | - validate_file_with_layouts_and_warning_groups( | |
| 194 | - units, | |
| 195 | - st, | |
| 196 | - std, | |
| 197 | - type_layouts, | |
| 198 | - false, | |
| 199 | - false, | |
| 200 | - ) | |
| 208 | + validate_file_with_layouts_and_warning_groups(units, st, std, type_layouts, false, false) | |
| 201 | 209 | } |
| 202 | 210 | |
| 203 | 211 | pub fn validate_file_with_layouts_and_warning_groups( |
@@ -208,13 +216,7 @@ pub fn validate_file_with_layouts_and_warning_groups( | ||
| 208 | 216 | warn_pedantic: bool, |
| 209 | 217 | warn_deprecated: bool, |
| 210 | 218 | ) -> Vec<Diagnostic> { |
| 211 | - let mut ctx = Ctx::new_with_layouts( | |
| 212 | - st, | |
| 213 | - std, | |
| 214 | - type_layouts, | |
| 215 | - warn_pedantic, | |
| 216 | - warn_deprecated, | |
| 217 | - ); | |
| 219 | + let mut ctx = Ctx::new_with_layouts(st, std, type_layouts, warn_pedantic, warn_deprecated); | |
| 218 | 220 | for unit in units { |
| 219 | 221 | validate_unit(&mut ctx, unit); |
| 220 | 222 | } |
@@ -231,10 +233,7 @@ fn decl_attrs_contain(attrs: &[Attribute], needle: Attribute) -> bool { | ||
| 231 | 233 | attrs.iter().any(|attr| *attr == needle) |
| 232 | 234 | } |
| 233 | 235 | |
| 234 | -fn is_deferred_char_pointer_component( | |
| 235 | - type_spec: &TypeSpec, | |
| 236 | - attrs: &[Attribute], | |
| 237 | -) -> bool { | |
| 236 | +fn is_deferred_char_pointer_component(type_spec: &TypeSpec, attrs: &[Attribute]) -> bool { | |
| 238 | 237 | decl_attrs_contain(attrs, Attribute::Pointer) |
| 239 | 238 | && matches!( |
| 240 | 239 | type_spec, |
@@ -340,15 +339,15 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 340 | 339 | } |
| 341 | 340 | for implicit_stmt in implicit { |
| 342 | 341 | if matches!(implicit_stmt.node, Decl::ImplicitNone { .. }) { |
| 343 | - ctx.require_std( | |
| 344 | - implicit_stmt.span, | |
| 345 | - FortranStandard::F90, | |
| 346 | - "IMPLICIT NONE", | |
| 347 | - ); | |
| 342 | + ctx.require_std(implicit_stmt.span, FortranStandard::F90, "IMPLICIT NONE"); | |
| 348 | 343 | } |
| 349 | 344 | } |
| 350 | 345 | if !contains.is_empty() { |
| 351 | - ctx.require_std(unit.span, FortranStandard::F90, "CONTAINS/internal procedures"); | |
| 346 | + ctx.require_std( | |
| 347 | + unit.span, | |
| 348 | + FortranStandard::F90, | |
| 349 | + "CONTAINS/internal procedures", | |
| 350 | + ); | |
| 352 | 351 | } |
| 353 | 352 | validate_decls(ctx, decls); |
| 354 | 353 | check_implicit_none(ctx, body, decls); |
@@ -361,7 +360,9 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 361 | 360 | ProgramUnit::Module { |
| 362 | 361 | uses, |
| 363 | 362 | implicit, |
| 364 | - decls, contains, .. | |
| 363 | + decls, | |
| 364 | + contains, | |
| 365 | + .. | |
| 365 | 366 | } => { |
| 366 | 367 | ctx.require_std(unit.span, FortranStandard::F90, "MODULE"); |
| 367 | 368 | for use_stmt in uses { |
@@ -369,11 +370,7 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 369 | 370 | } |
| 370 | 371 | for implicit_stmt in implicit { |
| 371 | 372 | if matches!(implicit_stmt.node, Decl::ImplicitNone { .. }) { |
| 372 | - ctx.require_std( | |
| 373 | - implicit_stmt.span, | |
| 374 | - FortranStandard::F90, | |
| 375 | - "IMPLICIT NONE", | |
| 376 | - ); | |
| 373 | + ctx.require_std(implicit_stmt.span, FortranStandard::F90, "IMPLICIT NONE"); | |
| 377 | 374 | } |
| 378 | 375 | } |
| 379 | 376 | validate_decls(ctx, decls); |
@@ -422,15 +419,15 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 422 | 419 | } |
| 423 | 420 | for implicit_stmt in implicit { |
| 424 | 421 | if matches!(implicit_stmt.node, Decl::ImplicitNone { .. }) { |
| 425 | - ctx.require_std( | |
| 426 | - implicit_stmt.span, | |
| 427 | - FortranStandard::F90, | |
| 428 | - "IMPLICIT NONE", | |
| 429 | - ); | |
| 422 | + ctx.require_std(implicit_stmt.span, FortranStandard::F90, "IMPLICIT NONE"); | |
| 430 | 423 | } |
| 431 | 424 | } |
| 432 | 425 | if !contains.is_empty() { |
| 433 | - ctx.require_std(unit.span, FortranStandard::F90, "CONTAINS/internal procedures"); | |
| 426 | + ctx.require_std( | |
| 427 | + unit.span, | |
| 428 | + FortranStandard::F90, | |
| 429 | + "CONTAINS/internal procedures", | |
| 430 | + ); | |
| 434 | 431 | } |
| 435 | 432 | validate_decls(ctx, decls); |
| 436 | 433 | check_implicit_none(ctx, body, decls); |
@@ -483,15 +480,15 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 483 | 480 | } |
| 484 | 481 | for implicit_stmt in implicit { |
| 485 | 482 | if matches!(implicit_stmt.node, Decl::ImplicitNone { .. }) { |
| 486 | - ctx.require_std( | |
| 487 | - implicit_stmt.span, | |
| 488 | - FortranStandard::F90, | |
| 489 | - "IMPLICIT NONE", | |
| 490 | - ); | |
| 483 | + ctx.require_std(implicit_stmt.span, FortranStandard::F90, "IMPLICIT NONE"); | |
| 491 | 484 | } |
| 492 | 485 | } |
| 493 | 486 | if !contains.is_empty() { |
| 494 | - ctx.require_std(unit.span, FortranStandard::F90, "CONTAINS/internal procedures"); | |
| 487 | + ctx.require_std( | |
| 488 | + unit.span, | |
| 489 | + FortranStandard::F90, | |
| 490 | + "CONTAINS/internal procedures", | |
| 491 | + ); | |
| 495 | 492 | } |
| 496 | 493 | validate_decls(ctx, decls); |
| 497 | 494 | check_implicit_none(ctx, body, decls); |
@@ -505,7 +502,9 @@ fn validate_unit(ctx: &mut Ctx, unit: &SpannedUnit) { | ||
| 505 | 502 | } |
| 506 | 503 | ProgramUnit::Submodule { |
| 507 | 504 | uses, |
| 508 | - decls, contains, .. | |
| 505 | + decls, | |
| 506 | + contains, | |
| 507 | + .. | |
| 509 | 508 | } => { |
| 510 | 509 | ctx.require_std(unit.span, FortranStandard::F2008, "SUBMODULE"); |
| 511 | 510 | for use_stmt in uses { |
@@ -701,7 +700,9 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 701 | 700 | Stmt::Assignment { target, value } => { |
| 702 | 701 | validate_assignment_target(ctx, target, stmt.span); |
| 703 | 702 | reject_pure_nonlocal_definition(ctx, target, stmt.span, "assignment"); |
| 704 | - if ctx.in_pure { check_pure_expr_calls(ctx, value); } | |
| 703 | + if ctx.in_pure { | |
| 704 | + check_pure_expr_calls(ctx, value); | |
| 705 | + } | |
| 705 | 706 | } |
| 706 | 707 | Stmt::PointerAssignment { target, value, .. } => { |
| 707 | 708 | validate_pointer_assignment(ctx, target, value, stmt.span); |
@@ -728,10 +729,17 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 728 | 729 | } |
| 729 | 730 | |
| 730 | 731 | // ---- I/O in pure ---- |
| 731 | - Stmt::Write { .. } | Stmt::Read { .. } | Stmt::Print { .. } | | |
| 732 | - Stmt::Open { .. } | Stmt::Close { .. } | Stmt::Inquire { .. } | | |
| 733 | - Stmt::Rewind { .. } | Stmt::Backspace { .. } | Stmt::Endfile { .. } | | |
| 734 | - Stmt::Flush { .. } | Stmt::Wait { .. } => { | |
| 732 | + Stmt::Write { .. } | |
| 733 | + | Stmt::Read { .. } | |
| 734 | + | Stmt::Print { .. } | |
| 735 | + | Stmt::Open { .. } | |
| 736 | + | Stmt::Close { .. } | |
| 737 | + | Stmt::Inquire { .. } | |
| 738 | + | Stmt::Rewind { .. } | |
| 739 | + | Stmt::Backspace { .. } | |
| 740 | + | Stmt::Endfile { .. } | |
| 741 | + | Stmt::Flush { .. } | |
| 742 | + | Stmt::Wait { .. } => { | |
| 735 | 743 | if ctx.in_pure { |
| 736 | 744 | ctx.error(stmt.span, "I/O statement not allowed in pure procedure"); |
| 737 | 745 | } |
@@ -775,7 +783,12 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 775 | 783 | } |
| 776 | 784 | |
| 777 | 785 | // ---- Control flow — recurse into bodies ---- |
| 778 | - Stmt::IfConstruct { then_body, else_ifs, else_body, .. } => { | |
| 786 | + Stmt::IfConstruct { | |
| 787 | + then_body, | |
| 788 | + else_ifs, | |
| 789 | + else_body, | |
| 790 | + .. | |
| 791 | + } => { | |
| 779 | 792 | validate_stmts(ctx, then_body); |
| 780 | 793 | for (_, body) in else_ifs { |
| 781 | 794 | validate_stmts(ctx, body); |
@@ -813,7 +826,14 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 813 | 826 | ctx.require_std(stmt.span, FortranStandard::F95, "FORALL statement"); |
| 814 | 827 | validate_stmt(ctx, inner); |
| 815 | 828 | } |
| 816 | - Stmt::Block { uses, ifaces, implicit, decls, body, .. } => { | |
| 829 | + Stmt::Block { | |
| 830 | + uses, | |
| 831 | + ifaces, | |
| 832 | + implicit, | |
| 833 | + decls, | |
| 834 | + body, | |
| 835 | + .. | |
| 836 | + } => { | |
| 817 | 837 | ctx.require_std(stmt.span, FortranStandard::F2008, "BLOCK construct"); |
| 818 | 838 | validate_decls(ctx, uses); |
| 819 | 839 | validate_decls(ctx, implicit); |
@@ -847,9 +867,10 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 847 | 867 | if let Some(ref name) = extract_base_name(item) { |
| 848 | 868 | let is_pointer = ctx.lookup(name).map(|s| s.attrs.pointer).unwrap_or(true); |
| 849 | 869 | if !is_pointer { |
| 850 | - ctx.error(item.span, format!( | |
| 851 | - "NULLIFY target '{}' must have pointer attribute", name | |
| 852 | - )); | |
| 870 | + ctx.error( | |
| 871 | + item.span, | |
| 872 | + format!("NULLIFY target '{}' must have pointer attribute", name), | |
| 873 | + ); | |
| 853 | 874 | } |
| 854 | 875 | } |
| 855 | 876 | } |
@@ -871,17 +892,22 @@ fn validate_stmt(ctx: &mut Ctx, stmt: &SpannedStmt) { | ||
| 871 | 892 | /// variable's intent/parameter status applies to all parts. |
| 872 | 893 | fn validate_assignment_target(ctx: &mut Ctx, target: &crate::ast::expr::SpannedExpr, span: Span) { |
| 873 | 894 | if let Some(name) = extract_base_name(target) { |
| 874 | - let (is_intent_in, is_parameter, is_pointer) = ctx.lookup(&name) | |
| 875 | - .map(|sym| ( | |
| 876 | - matches!(sym.attrs.intent, Some(Intent::In)), | |
| 877 | - sym.attrs.parameter, | |
| 878 | - sym.attrs.pointer, | |
| 879 | - )) | |
| 895 | + let (is_intent_in, is_parameter, is_pointer) = ctx | |
| 896 | + .lookup(&name) | |
| 897 | + .map(|sym| { | |
| 898 | + ( | |
| 899 | + matches!(sym.attrs.intent, Some(Intent::In)), | |
| 900 | + sym.attrs.parameter, | |
| 901 | + sym.attrs.pointer, | |
| 902 | + ) | |
| 903 | + }) | |
| 880 | 904 | .unwrap_or((false, false, false)); |
| 881 | - let writes_through_pointer_target = | |
| 882 | - is_pointer && !matches!(target.node, Expr::Name { .. }); | |
| 905 | + let writes_through_pointer_target = is_pointer && !matches!(target.node, Expr::Name { .. }); | |
| 883 | 906 | if is_intent_in && !writes_through_pointer_target { |
| 884 | - ctx.error(span, format!("cannot assign to intent(in) variable '{}'", name)); | |
| 907 | + ctx.error( | |
| 908 | + span, | |
| 909 | + format!("cannot assign to intent(in) variable '{}'", name), | |
| 910 | + ); | |
| 885 | 911 | } |
| 886 | 912 | if is_parameter { |
| 887 | 913 | ctx.error(span, format!("cannot assign to named constant '{}'", name)); |
@@ -903,16 +929,25 @@ fn validate_pointer_assignment( | ||
| 903 | 929 | if expr_selects_component(target) { |
| 904 | 930 | if let Some(leaf) = leaf_field_layout(ctx, target) { |
| 905 | 931 | if !leaf.field.pointer { |
| 906 | - ctx.error(span, format!( | |
| 907 | - "pointer assignment target component '{}' must have pointer attribute", | |
| 908 | - leaf.field.name | |
| 909 | - )); | |
| 932 | + ctx.error( | |
| 933 | + span, | |
| 934 | + format!( | |
| 935 | + "pointer assignment target component '{}' must have pointer attribute", | |
| 936 | + leaf.field.name | |
| 937 | + ), | |
| 938 | + ); | |
| 910 | 939 | } |
| 911 | 940 | } |
| 912 | 941 | } else if let Some(name) = extract_base_name(target) { |
| 913 | 942 | let is_pointer = ctx.lookup(&name).map(|s| s.attrs.pointer).unwrap_or(true); |
| 914 | 943 | if !is_pointer { |
| 915 | - ctx.error(span, format!("pointer assignment target '{}' must have pointer attribute", name)); | |
| 944 | + ctx.error( | |
| 945 | + span, | |
| 946 | + format!( | |
| 947 | + "pointer assignment target '{}' must have pointer attribute", | |
| 948 | + name | |
| 949 | + ), | |
| 950 | + ); | |
| 916 | 951 | } |
| 917 | 952 | } |
| 918 | 953 | |
@@ -956,9 +991,18 @@ fn validate_pointer_assignment( | ||
| 956 | 991 | return; |
| 957 | 992 | } |
| 958 | 993 | } |
| 959 | - let ok = ctx.lookup(&name).map(|s| s.attrs.target || s.attrs.pointer).unwrap_or(true); | |
| 994 | + let ok = ctx | |
| 995 | + .lookup(&name) | |
| 996 | + .map(|s| s.attrs.target || s.attrs.pointer) | |
| 997 | + .unwrap_or(true); | |
| 960 | 998 | if !ok { |
| 961 | - ctx.error(span, format!("pointer assignment source '{}' must have target or pointer attribute", name)); | |
| 999 | + ctx.error( | |
| 1000 | + span, | |
| 1001 | + format!( | |
| 1002 | + "pointer assignment source '{}' must have target or pointer attribute", | |
| 1003 | + name | |
| 1004 | + ), | |
| 1005 | + ); | |
| 962 | 1006 | } |
| 963 | 1007 | } |
| 964 | 1008 | } |
@@ -976,24 +1020,32 @@ fn validate_allocatable_item(ctx: &mut Ctx, item: &crate::ast::expr::SpannedExpr | ||
| 976 | 1020 | if expr_selects_component(item) { |
| 977 | 1021 | if let Some(leaf) = leaf_field_layout(ctx, item) { |
| 978 | 1022 | if !leaf.field.allocatable && !leaf.field.pointer { |
| 979 | - ctx.error(item.span, format!( | |
| 1023 | + ctx.error( | |
| 1024 | + item.span, | |
| 1025 | + format!( | |
| 980 | 1026 | "only allocatable or pointer components can appear in {}, but '{}' is neither", |
| 981 | 1027 | stmt_name.to_uppercase(), leaf.field.name |
| 982 | - )); | |
| 1028 | + ), | |
| 1029 | + ); | |
| 983 | 1030 | } |
| 984 | 1031 | } |
| 985 | 1032 | return; |
| 986 | 1033 | } |
| 987 | 1034 | let base_name = extract_base_name(item); |
| 988 | 1035 | if let Some(ref name) = base_name { |
| 989 | - let ok = ctx.lookup(name) | |
| 1036 | + let ok = ctx | |
| 1037 | + .lookup(name) | |
| 990 | 1038 | .map(|s| s.attrs.allocatable || s.attrs.pointer) |
| 991 | 1039 | .unwrap_or(true); // unknown symbol — skip |
| 992 | 1040 | if !ok { |
| 993 | - ctx.error(item.span, format!( | |
| 994 | - "only allocatable or pointer variables can appear in {}, but '{}' is neither", | |
| 995 | - stmt_name.to_uppercase(), name | |
| 996 | - )); | |
| 1041 | + ctx.error( | |
| 1042 | + item.span, | |
| 1043 | + format!( | |
| 1044 | + "only allocatable or pointer variables can appear in {}, but '{}' is neither", | |
| 1045 | + stmt_name.to_uppercase(), | |
| 1046 | + name | |
| 1047 | + ), | |
| 1048 | + ); | |
| 997 | 1049 | } |
| 998 | 1050 | } |
| 999 | 1051 | } |
@@ -1050,7 +1102,9 @@ fn leaf_field_layout<'a>( | ||
| 1050 | 1102 | } |
| 1051 | 1103 | }; |
| 1052 | 1104 | chain.reverse(); |
| 1053 | - if chain.is_empty() { return None; } | |
| 1105 | + if chain.is_empty() { | |
| 1106 | + return None; | |
| 1107 | + } | |
| 1054 | 1108 | // Resolve the base variable's derived type via the symbol table. |
| 1055 | 1109 | let sym = ctx.lookup(base_name)?; |
| 1056 | 1110 | let base_type = match sym.type_info.as_ref()? { |
@@ -1069,8 +1123,12 @@ fn leaf_field_layout<'a>( | ||
| 1069 | 1123 | // so the leaf check can honour inherited target-ness. |
| 1070 | 1124 | let is_terminal = i + 1 == chain.len(); |
| 1071 | 1125 | if !is_terminal { |
| 1072 | - if field.target { ancestor_is_target = true; } | |
| 1073 | - if field.allocatable { ancestor_is_allocatable = true; } | |
| 1126 | + if field.target { | |
| 1127 | + ancestor_is_target = true; | |
| 1128 | + } | |
| 1129 | + if field.allocatable { | |
| 1130 | + ancestor_is_allocatable = true; | |
| 1131 | + } | |
| 1074 | 1132 | } |
| 1075 | 1133 | leaf = Some(field); |
| 1076 | 1134 | match &field.type_info { |
@@ -1123,8 +1181,12 @@ fn validate_pure_call(ctx: &mut Ctx, callee: &crate::ast::expr::SpannedExpr, spa | ||
| 1123 | 1181 | // symbol that is NOT marked pure/elemental/intrinsic, reject. |
| 1124 | 1182 | // Unknown callees (external without an interface) are left |
| 1125 | 1183 | // alone — the programmer's responsibility per F2018 §15.4. |
| 1126 | - let Some(name) = extract_base_name(callee) else { return; }; | |
| 1127 | - let Some(sym) = ctx.lookup(&name) else { return; }; | |
| 1184 | + let Some(name) = extract_base_name(callee) else { | |
| 1185 | + return; | |
| 1186 | + }; | |
| 1187 | + let Some(sym) = ctx.lookup(&name) else { | |
| 1188 | + return; | |
| 1189 | + }; | |
| 1128 | 1190 | match sym.kind { |
| 1129 | 1191 | SymbolKind::Function | SymbolKind::Subroutine => { |
| 1130 | 1192 | if !sym.attrs.pure && !sym.attrs.elemental && !sym.attrs.intrinsic { |
@@ -1138,7 +1200,7 @@ fn validate_pure_call(ctx: &mut Ctx, callee: &crate::ast::expr::SpannedExpr, spa | ||
| 1138 | 1200 | } |
| 1139 | 1201 | } |
| 1140 | 1202 | SymbolKind::IntrinsicProc => {} // always OK |
| 1141 | - _ => {} // external / unknown — can't check | |
| 1203 | + _ => {} // external / unknown — can't check | |
| 1142 | 1204 | } |
| 1143 | 1205 | } |
| 1144 | 1206 | |
@@ -1183,12 +1245,19 @@ fn reject_pure_nonlocal_definition( | ||
| 1183 | 1245 | if !ctx.in_pure { |
| 1184 | 1246 | return; |
| 1185 | 1247 | } |
| 1186 | - let Some(name) = extract_base_name(target) else { return; }; | |
| 1187 | - let Some(sym) = ctx.lookup(&name) else { return; }; | |
| 1248 | + let Some(name) = extract_base_name(target) else { | |
| 1249 | + return; | |
| 1250 | + }; | |
| 1251 | + let Some(sym) = ctx.lookup(&name) else { | |
| 1252 | + return; | |
| 1253 | + }; | |
| 1188 | 1254 | // Only variables and COMMON blocks can be "defined"; function |
| 1189 | 1255 | // names get definition semantics too but those are the pure |
| 1190 | 1256 | // function's own result variable (always local). |
| 1191 | - if !matches!(sym.kind, SymbolKind::Variable | SymbolKind::Parameter | SymbolKind::CommonBlock) { | |
| 1257 | + if !matches!( | |
| 1258 | + sym.kind, | |
| 1259 | + SymbolKind::Variable | SymbolKind::Parameter | SymbolKind::CommonBlock | |
| 1260 | + ) { | |
| 1192 | 1261 | return; |
| 1193 | 1262 | } |
| 1194 | 1263 | if symbol_is_non_local_to_procedure(ctx.st, sym, ctx.scope_id) { |
@@ -1212,7 +1281,11 @@ fn validate_call_site_intent( | ||
| 1212 | 1281 | span: Span, |
| 1213 | 1282 | ) { |
| 1214 | 1283 | // Look up the callee to find its dummy argument intents. |
| 1215 | - let callee_name = if let Expr::Name { name } = &callee.node { name.clone() } else { return; }; | |
| 1284 | + let callee_name = if let Expr::Name { name } = &callee.node { | |
| 1285 | + name.clone() | |
| 1286 | + } else { | |
| 1287 | + return; | |
| 1288 | + }; | |
| 1216 | 1289 | |
| 1217 | 1290 | // For each actual argument, check if it's an lvalue when the dummy requires out/inout. |
| 1218 | 1291 | // We can only check this if the callee's dummy arg info is in the symbol table. |
@@ -1223,15 +1296,22 @@ fn validate_call_site_intent( | ||
| 1223 | 1296 | _ => continue, |
| 1224 | 1297 | }; |
| 1225 | 1298 | // Check if actual is a literal (not an lvalue). |
| 1226 | - let is_literal = matches!(actual.node, | |
| 1227 | - Expr::IntegerLiteral { .. } | Expr::RealLiteral { .. } | | |
| 1228 | - Expr::StringLiteral { .. } | Expr::LogicalLiteral { .. } | | |
| 1229 | - Expr::ComplexLiteral { .. } | |
| 1299 | + let is_literal = matches!( | |
| 1300 | + actual.node, | |
| 1301 | + Expr::IntegerLiteral { .. } | |
| 1302 | + | Expr::RealLiteral { .. } | |
| 1303 | + | Expr::StringLiteral { .. } | |
| 1304 | + | Expr::LogicalLiteral { .. } | |
| 1305 | + | Expr::ComplexLiteral { .. } | |
| 1230 | 1306 | ); |
| 1231 | 1307 | // Check if actual is a named constant (parameter). |
| 1232 | 1308 | let is_parameter = if let Some(name) = extract_base_name(actual) { |
| 1233 | - ctx.lookup(&name).map(|s| s.attrs.parameter).unwrap_or(false) | |
| 1234 | - } else { false }; | |
| 1309 | + ctx.lookup(&name) | |
| 1310 | + .map(|s| s.attrs.parameter) | |
| 1311 | + .unwrap_or(false) | |
| 1312 | + } else { | |
| 1313 | + false | |
| 1314 | + }; | |
| 1235 | 1315 | |
| 1236 | 1316 | if is_literal || is_parameter { |
| 1237 | 1317 | // We can't tell without the callee's interface whether this arg is |
@@ -1256,17 +1336,24 @@ fn validate_elemental_args( | ||
| 1256 | 1336 | for arg in args { |
| 1257 | 1337 | if let DummyArg::Name(arg_name) = arg { |
| 1258 | 1338 | for decl in decls { |
| 1259 | - if let Decl::TypeDecl { attrs, entities, .. } = &decl.node { | |
| 1339 | + if let Decl::TypeDecl { | |
| 1340 | + attrs, entities, .. | |
| 1341 | + } = &decl.node | |
| 1342 | + { | |
| 1260 | 1343 | for entity in entities { |
| 1261 | 1344 | if entity.name.eq_ignore_ascii_case(arg_name) { |
| 1262 | 1345 | // Check for dimension attribute or explicit array spec on entity. |
| 1263 | - let has_dimension = attrs.iter().any(|a| matches!(a, Attribute::Dimension(_))); | |
| 1346 | + let has_dimension = | |
| 1347 | + attrs.iter().any(|a| matches!(a, Attribute::Dimension(_))); | |
| 1264 | 1348 | let has_entity_dims = entity.array_spec.is_some(); |
| 1265 | 1349 | if has_dimension || has_entity_dims { |
| 1266 | - ctx.error(span, format!( | |
| 1267 | - "elemental procedure argument '{}' must be scalar", | |
| 1268 | - arg_name | |
| 1269 | - )); | |
| 1350 | + ctx.error( | |
| 1351 | + span, | |
| 1352 | + format!( | |
| 1353 | + "elemental procedure argument '{}' must be scalar", | |
| 1354 | + arg_name | |
| 1355 | + ), | |
| 1356 | + ); | |
| 1270 | 1357 | } |
| 1271 | 1358 | } |
| 1272 | 1359 | } |
@@ -1288,9 +1375,16 @@ fn register_label(ctx: &mut Ctx, label: u64, span: Span) { | ||
| 1288 | 1375 | /// At the end of a scope, verify all GOTO labels have targets. |
| 1289 | 1376 | fn validate_label_consistency(ctx: &mut Ctx, _scope_span: Span) { |
| 1290 | 1377 | // Collect errors first to avoid borrow conflict. |
| 1291 | - let errors: Vec<(Span, String)> = ctx.labels_referenced.iter() | |
| 1378 | + let errors: Vec<(Span, String)> = ctx | |
| 1379 | + .labels_referenced | |
| 1380 | + .iter() | |
| 1292 | 1381 | .filter(|(label, _)| !ctx.labels_defined.contains(label)) |
| 1293 | - .map(|(label, span)| (*span, format!("GOTO target label {} not defined in this scope", label))) | |
| 1382 | + .map(|(label, span)| { | |
| 1383 | + ( | |
| 1384 | + *span, | |
| 1385 | + format!("GOTO target label {} not defined in this scope", label), | |
| 1386 | + ) | |
| 1387 | + }) | |
| 1294 | 1388 | .collect(); |
| 1295 | 1389 | for (span, msg) in errors { |
| 1296 | 1390 | ctx.error(span, msg); |
@@ -1321,34 +1415,46 @@ fn validate_operator_interface( | ||
| 1321 | 1415 | match &sub.node { |
| 1322 | 1416 | ProgramUnit::Function { args, .. } => { |
| 1323 | 1417 | if is_assignment { |
| 1324 | - ctx.error(sub.span, format!( | |
| 1418 | + ctx.error( | |
| 1419 | + sub.span, | |
| 1420 | + format!( | |
| 1325 | 1421 | "ASSIGNMENT({}) interface must contain subroutines, not functions", |
| 1326 | 1422 | "=" |
| 1327 | - )); | |
| 1423 | + ), | |
| 1424 | + ); | |
| 1328 | 1425 | continue; |
| 1329 | 1426 | } |
| 1330 | 1427 | // Operator functions: unary = 1 arg, binary = 2 args. |
| 1331 | 1428 | let nargs = args.len(); |
| 1332 | 1429 | if !(1..=2).contains(&nargs) { |
| 1333 | - ctx.error(sub.span, format!( | |
| 1430 | + ctx.error( | |
| 1431 | + sub.span, | |
| 1432 | + format!( | |
| 1334 | 1433 | "operator interface function must have 1 or 2 arguments, got {}", |
| 1335 | 1434 | nargs |
| 1336 | - )); | |
| 1435 | + ), | |
| 1436 | + ); | |
| 1337 | 1437 | } |
| 1338 | 1438 | // All arguments must be intent(in) — checked by looking at decls. |
| 1339 | 1439 | // Deferred: would need to walk the function's decls to check intent. |
| 1340 | 1440 | } |
| 1341 | 1441 | ProgramUnit::Subroutine { args, .. } => { |
| 1342 | 1442 | if !is_assignment { |
| 1343 | - ctx.error(sub.span, "operator interface must contain functions, not subroutines"); | |
| 1443 | + ctx.error( | |
| 1444 | + sub.span, | |
| 1445 | + "operator interface must contain functions, not subroutines", | |
| 1446 | + ); | |
| 1344 | 1447 | continue; |
| 1345 | 1448 | } |
| 1346 | 1449 | // Assignment subroutines must have exactly 2 arguments. |
| 1347 | 1450 | if args.len() != 2 { |
| 1348 | - ctx.error(sub.span, format!( | |
| 1451 | + ctx.error( | |
| 1452 | + sub.span, | |
| 1453 | + format!( | |
| 1349 | 1454 | "ASSIGNMENT(=) interface subroutine must have 2 arguments, got {}", |
| 1350 | 1455 | args.len() |
| 1351 | - )); | |
| 1456 | + ), | |
| 1457 | + ); | |
| 1352 | 1458 | } |
| 1353 | 1459 | } |
| 1354 | 1460 | _ => { |
@@ -1380,10 +1486,13 @@ fn validate_derived_type( | ||
| 1380 | 1486 | // Deferred procedures only allowed in abstract types. |
| 1381 | 1487 | let is_deferred = tbp.attrs.iter().any(|a| a.eq_ignore_ascii_case("deferred")); |
| 1382 | 1488 | if is_deferred && !is_abstract { |
| 1383 | - ctx.error(span, format!( | |
| 1384 | - "type-bound procedure '{}' is DEFERRED but type '{}' is not ABSTRACT", | |
| 1385 | - tbp.name, name | |
| 1386 | - )); | |
| 1489 | + ctx.error( | |
| 1490 | + span, | |
| 1491 | + format!( | |
| 1492 | + "type-bound procedure '{}' is DEFERRED but type '{}' is not ABSTRACT", | |
| 1493 | + tbp.name, name | |
| 1494 | + ), | |
| 1495 | + ); | |
| 1387 | 1496 | } |
| 1388 | 1497 | |
| 1389 | 1498 | // PASS and NOPASS are mutually exclusive. |
@@ -1393,18 +1502,24 @@ fn validate_derived_type( | ||
| 1393 | 1502 | }); |
| 1394 | 1503 | let has_nopass = tbp.attrs.iter().any(|a| a.eq_ignore_ascii_case("nopass")); |
| 1395 | 1504 | if has_pass && has_nopass { |
| 1396 | - ctx.error(span, format!( | |
| 1397 | - "type-bound procedure '{}' cannot have both PASS and NOPASS", | |
| 1398 | - tbp.name | |
| 1399 | - )); | |
| 1505 | + ctx.error( | |
| 1506 | + span, | |
| 1507 | + format!( | |
| 1508 | + "type-bound procedure '{}' cannot have both PASS and NOPASS", | |
| 1509 | + tbp.name | |
| 1510 | + ), | |
| 1511 | + ); | |
| 1400 | 1512 | } |
| 1401 | 1513 | |
| 1402 | 1514 | // Deferred procedures must have an interface (binding). |
| 1403 | 1515 | if is_deferred && tbp.binding.is_none() { |
| 1404 | - ctx.error(span, format!( | |
| 1405 | - "DEFERRED type-bound procedure '{}' must specify an interface", | |
| 1406 | - tbp.name | |
| 1407 | - )); | |
| 1516 | + ctx.error( | |
| 1517 | + span, | |
| 1518 | + format!( | |
| 1519 | + "DEFERRED type-bound procedure '{}' must specify an interface", | |
| 1520 | + tbp.name | |
| 1521 | + ), | |
| 1522 | + ); | |
| 1408 | 1523 | } |
| 1409 | 1524 | } |
| 1410 | 1525 | } |
@@ -1438,8 +1553,14 @@ fn extract_base_name(expr: &crate::ast::expr::SpannedExpr) -> Option<String> { | ||
| 1438 | 1553 | |
| 1439 | 1554 | /// Check that all variable references in a statement list are declared |
| 1440 | 1555 | /// when IMPLICIT NONE is active in the current scope. |
| 1441 | -fn check_implicit_none(ctx: &mut Ctx, stmts: &[SpannedStmt], decls: &[crate::ast::decl::SpannedDecl]) { | |
| 1442 | - if !ctx.st.is_implicit_none(ctx.scope_id) { return; } | |
| 1556 | +fn check_implicit_none( | |
| 1557 | + ctx: &mut Ctx, | |
| 1558 | + stmts: &[SpannedStmt], | |
| 1559 | + decls: &[crate::ast::decl::SpannedDecl], | |
| 1560 | +) { | |
| 1561 | + if !ctx.st.is_implicit_none(ctx.scope_id) { | |
| 1562 | + return; | |
| 1563 | + } | |
| 1443 | 1564 | |
| 1444 | 1565 | // Collect declared names in this scope (from declarations). |
| 1445 | 1566 | let mut declared: std::collections::HashSet<String> = std::collections::HashSet::new(); |
@@ -1451,7 +1572,10 @@ fn check_implicit_none(ctx: &mut Ctx, stmts: &[SpannedStmt], decls: &[crate::ast | ||
| 1451 | 1572 | // should have them via resolve. We also check decls for |
| 1452 | 1573 | // EXTERNAL statements. |
| 1453 | 1574 | for decl in decls { |
| 1454 | - if let Decl::TypeDecl { attrs, entities, .. } = &decl.node { | |
| 1575 | + if let Decl::TypeDecl { | |
| 1576 | + attrs, entities, .. | |
| 1577 | + } = &decl.node | |
| 1578 | + { | |
| 1455 | 1579 | if attrs.iter().any(|a| matches!(a, Attribute::External)) { |
| 1456 | 1580 | for e in entities { |
| 1457 | 1581 | declared.insert(e.name.to_lowercase()); |
@@ -1463,8 +1587,7 @@ fn check_implicit_none(ctx: &mut Ctx, stmts: &[SpannedStmt], decls: &[crate::ast | ||
| 1463 | 1587 | let mut undeclared = Vec::new(); |
| 1464 | 1588 | let mut resolution_cache: std::collections::HashMap<String, bool> = |
| 1465 | 1589 | std::collections::HashMap::new(); |
| 1466 | - let outer_implicit_letters: std::collections::HashSet<char> = | |
| 1467 | - std::collections::HashSet::new(); | |
| 1590 | + let outer_implicit_letters: std::collections::HashSet<char> = std::collections::HashSet::new(); | |
| 1468 | 1591 | for stmt in stmts { |
| 1469 | 1592 | walk_stmt_for_undeclared( |
| 1470 | 1593 | ctx.st, |
@@ -1482,9 +1605,13 @@ fn check_implicit_none(ctx: &mut Ctx, stmts: &[SpannedStmt], decls: &[crate::ast | ||
| 1482 | 1605 | for (name, span) in &undeclared { |
| 1483 | 1606 | let key = name.to_lowercase(); |
| 1484 | 1607 | if reported.insert(key) { |
| 1485 | - ctx.error(*span, format!( | |
| 1486 | - "variable '{}' used but not declared (IMPLICIT NONE is active)", name | |
| 1487 | - )); | |
| 1608 | + ctx.error( | |
| 1609 | + *span, | |
| 1610 | + format!( | |
| 1611 | + "variable '{}' used but not declared (IMPLICIT NONE is active)", | |
| 1612 | + name | |
| 1613 | + ), | |
| 1614 | + ); | |
| 1488 | 1615 | } |
| 1489 | 1616 | } |
| 1490 | 1617 | } |
@@ -1524,8 +1651,7 @@ fn extend_declared_names_from_ifaces( | ||
| 1524 | 1651 | for body in bodies { |
| 1525 | 1652 | match body { |
| 1526 | 1653 | InterfaceBody::Subprogram(sub) => match &sub.node { |
| 1527 | - ProgramUnit::Function { name, .. } | |
| 1528 | - | ProgramUnit::Subroutine { name, .. } => { | |
| 1654 | + ProgramUnit::Function { name, .. } | ProgramUnit::Subroutine { name, .. } => { | |
| 1529 | 1655 | declared.insert(name.to_lowercase()); |
| 1530 | 1656 | } |
| 1531 | 1657 | _ => {} |
@@ -1577,41 +1703,80 @@ fn walk_stmt_for_undeclared( | ||
| 1577 | 1703 | } |
| 1578 | 1704 | match &stmt.node { |
| 1579 | 1705 | Stmt::Assignment { target, value } => { |
| 1580 | - chk!(target); chk!(value); | |
| 1706 | + chk!(target); | |
| 1707 | + chk!(value); | |
| 1581 | 1708 | } |
| 1582 | 1709 | Stmt::PointerAssignment { target, value, .. } => { |
| 1583 | - chk!(target); chk!(value); | |
| 1710 | + chk!(target); | |
| 1711 | + chk!(value); | |
| 1584 | 1712 | } |
| 1585 | 1713 | Stmt::Print { items, .. } => { |
| 1586 | - for item in items { chk!(item); } | |
| 1714 | + for item in items { | |
| 1715 | + chk!(item); | |
| 1716 | + } | |
| 1587 | 1717 | } |
| 1588 | - Stmt::Write { items, controls, .. } => { | |
| 1589 | - for item in items { chk!(item); } | |
| 1590 | - for ctrl in controls { chk!(&ctrl.value); } | |
| 1718 | + Stmt::Write { | |
| 1719 | + items, controls, .. | |
| 1720 | + } => { | |
| 1721 | + for item in items { | |
| 1722 | + chk!(item); | |
| 1723 | + } | |
| 1724 | + for ctrl in controls { | |
| 1725 | + chk!(&ctrl.value); | |
| 1726 | + } | |
| 1591 | 1727 | } |
| 1592 | - Stmt::Read { items, controls, .. } => { | |
| 1593 | - for item in items { chk!(item); } | |
| 1594 | - for ctrl in controls { chk!(&ctrl.value); } | |
| 1728 | + Stmt::Read { | |
| 1729 | + items, controls, .. | |
| 1730 | + } => { | |
| 1731 | + for item in items { | |
| 1732 | + chk!(item); | |
| 1733 | + } | |
| 1734 | + for ctrl in controls { | |
| 1735 | + chk!(&ctrl.value); | |
| 1736 | + } | |
| 1595 | 1737 | } |
| 1596 | - Stmt::IfConstruct { condition, then_body, else_ifs, else_body, .. } => { | |
| 1738 | + Stmt::IfConstruct { | |
| 1739 | + condition, | |
| 1740 | + then_body, | |
| 1741 | + else_ifs, | |
| 1742 | + else_body, | |
| 1743 | + .. | |
| 1744 | + } => { | |
| 1597 | 1745 | chk!(condition); |
| 1598 | - for s in then_body { recurse!(s); } | |
| 1746 | + for s in then_body { | |
| 1747 | + recurse!(s); | |
| 1748 | + } | |
| 1599 | 1749 | for (cond, body) in else_ifs { |
| 1600 | 1750 | chk!(cond); |
| 1601 | - for s in body { recurse!(s); } | |
| 1751 | + for s in body { | |
| 1752 | + recurse!(s); | |
| 1753 | + } | |
| 1602 | 1754 | } |
| 1603 | 1755 | if let Some(body) = else_body { |
| 1604 | - for s in body { recurse!(s); } | |
| 1756 | + for s in body { | |
| 1757 | + recurse!(s); | |
| 1758 | + } | |
| 1605 | 1759 | } |
| 1606 | 1760 | } |
| 1607 | 1761 | Stmt::IfStmt { condition, action } => { |
| 1608 | - chk!(condition); recurse!(action); | |
| 1762 | + chk!(condition); | |
| 1763 | + recurse!(action); | |
| 1609 | 1764 | } |
| 1610 | - Stmt::DoLoop { body, .. } | Stmt::DoWhile { body, .. } | | |
| 1611 | - Stmt::DoConcurrent { body, .. } => { | |
| 1612 | - for s in body { recurse!(s); } | |
| 1765 | + Stmt::DoLoop { body, .. } | |
| 1766 | + | Stmt::DoWhile { body, .. } | |
| 1767 | + | Stmt::DoConcurrent { body, .. } => { | |
| 1768 | + for s in body { | |
| 1769 | + recurse!(s); | |
| 1770 | + } | |
| 1613 | 1771 | } |
| 1614 | - Stmt::Block { uses, ifaces, implicit, decls, body, .. } => { | |
| 1772 | + Stmt::Block { | |
| 1773 | + uses, | |
| 1774 | + ifaces, | |
| 1775 | + implicit, | |
| 1776 | + decls, | |
| 1777 | + body, | |
| 1778 | + .. | |
| 1779 | + } => { | |
| 1615 | 1780 | // F2018 §11.1.4: a BLOCK construct establishes its own |
| 1616 | 1781 | // scope with an independent implicit-typing environment. |
| 1617 | 1782 | // Layer the block's declared names AND any IMPLICIT |
@@ -1672,10 +1837,14 @@ fn walk_stmt_for_undeclared( | ||
| 1672 | 1837 | ); |
| 1673 | 1838 | } |
| 1674 | 1839 | } |
| 1675 | - Stmt::SelectCase { selector, cases, .. } => { | |
| 1840 | + Stmt::SelectCase { | |
| 1841 | + selector, cases, .. | |
| 1842 | + } => { | |
| 1676 | 1843 | chk!(selector); |
| 1677 | 1844 | for case in cases { |
| 1678 | - for s in &case.body { recurse!(s); } | |
| 1845 | + for s in &case.body { | |
| 1846 | + recurse!(s); | |
| 1847 | + } | |
| 1679 | 1848 | } |
| 1680 | 1849 | } |
| 1681 | 1850 | Stmt::Call { args, .. } => { |
@@ -1685,13 +1854,26 @@ fn walk_stmt_for_undeclared( | ||
| 1685 | 1854 | } |
| 1686 | 1855 | } |
| 1687 | 1856 | } |
| 1688 | - Stmt::Labeled { stmt: inner, .. } => { recurse!(inner); } | |
| 1689 | - Stmt::WhereConstruct { mask, body, elsewhere, .. } => { | |
| 1857 | + Stmt::Labeled { stmt: inner, .. } => { | |
| 1858 | + recurse!(inner); | |
| 1859 | + } | |
| 1860 | + Stmt::WhereConstruct { | |
| 1861 | + mask, | |
| 1862 | + body, | |
| 1863 | + elsewhere, | |
| 1864 | + .. | |
| 1865 | + } => { | |
| 1690 | 1866 | chk!(mask); |
| 1691 | - for s in body { recurse!(s); } | |
| 1867 | + for s in body { | |
| 1868 | + recurse!(s); | |
| 1869 | + } | |
| 1692 | 1870 | for (m, b) in elsewhere { |
| 1693 | - if let Some(m) = m { chk!(m); } | |
| 1694 | - for s in b { recurse!(s); } | |
| 1871 | + if let Some(m) = m { | |
| 1872 | + chk!(m); | |
| 1873 | + } | |
| 1874 | + for s in b { | |
| 1875 | + recurse!(s); | |
| 1876 | + } | |
| 1695 | 1877 | } |
| 1696 | 1878 | } |
| 1697 | 1879 | _ => {} |
@@ -1707,7 +1889,13 @@ fn block_use_imported_names( | ||
| 1707 | 1889 | |
| 1708 | 1890 | let mut imported = std::collections::HashSet::new(); |
| 1709 | 1891 | for use_decl in uses { |
| 1710 | - let crate::ast::decl::Decl::UseStmt { module, renames, only, .. } = &use_decl.node else { | |
| 1892 | + let crate::ast::decl::Decl::UseStmt { | |
| 1893 | + module, | |
| 1894 | + renames, | |
| 1895 | + only, | |
| 1896 | + .. | |
| 1897 | + } = &use_decl.node | |
| 1898 | + else { | |
| 1711 | 1899 | continue; |
| 1712 | 1900 | }; |
| 1713 | 1901 | if let Some(only_items) = only { |
@@ -1752,9 +1940,15 @@ fn check_expr_names( | ||
| 1752 | 1940 | Expr::Name { name } => { |
| 1753 | 1941 | let key = name.to_lowercase(); |
| 1754 | 1942 | // Skip format specifier * (appears in WRITE(*, *) / READ(*, *)). |
| 1755 | - if key == "*" { return; } | |
| 1756 | - if declared.contains(&key) { return; } | |
| 1757 | - if is_intrinsic_name(&key) { return; } | |
| 1943 | + if key == "*" { | |
| 1944 | + return; | |
| 1945 | + } | |
| 1946 | + if declared.contains(&key) { | |
| 1947 | + return; | |
| 1948 | + } | |
| 1949 | + if is_intrinsic_name(&key) { | |
| 1950 | + return; | |
| 1951 | + } | |
| 1758 | 1952 | // F2018 §11.1.4: a BLOCK-scoped IMPLICIT statement gives |
| 1759 | 1953 | // names whose first letter is in the covered range an |
| 1760 | 1954 | // implicit type, even if the enclosing scope is |
@@ -1862,7 +2056,8 @@ fn check_expr_names( | ||
| 1862 | 2056 | } |
| 1863 | 2057 | |
| 1864 | 2058 | pub fn is_intrinsic_name(name: &str) -> bool { |
| 1865 | - matches!(name, | |
| 2059 | + matches!( | |
| 2060 | + name, | |
| 1866 | 2061 | "abs" | "iabs" | "dabs" | "cabs" | "acos" | "asin" | "atan" | "atan2" | |
| 1867 | 2062 | "cos" | "sin" | "tan" | "exp" | "log" | "log10" | "sqrt" | "dsqrt" | |
| 1868 | 2063 | "mod" | "modulo" | "max" | "min" | "sign" | "dim" | |
@@ -1906,12 +2101,14 @@ mod tests { | ||
| 1906 | 2101 | let tokens = Lexer::tokenize(src, 0).unwrap(); |
| 1907 | 2102 | let mut parser = Parser::new(&tokens); |
| 1908 | 2103 | let units = parser.parse_file().unwrap(); |
| 1909 | - let rr = resolve::resolve_file(&units, &[]).unwrap(); let st = rr.st; | |
| 2104 | + let rr = resolve::resolve_file(&units, &[]).unwrap(); | |
| 2105 | + let st = rr.st; | |
| 1910 | 2106 | validate_file(&units, &st) |
| 1911 | 2107 | } |
| 1912 | 2108 | |
| 1913 | 2109 | fn errors_from(src: &str) -> Vec<String> { |
| 1914 | - validate_source(src).iter() | |
| 2110 | + validate_source(src) | |
| 2111 | + .iter() | |
| 1915 | 2112 | .filter(|d| d.kind == DiagKind::Error) |
| 1916 | 2113 | .map(|d| d.msg.clone()) |
| 1917 | 2114 | .collect() |
@@ -1921,7 +2118,8 @@ mod tests { | ||
| 1921 | 2118 | let tokens = Lexer::tokenize(src, 0).unwrap(); |
| 1922 | 2119 | let mut parser = Parser::new(&tokens); |
| 1923 | 2120 | let units = parser.parse_file().unwrap(); |
| 1924 | - let rr = resolve::resolve_file(&units, &[]).unwrap(); let st = rr.st; | |
| 2121 | + let rr = resolve::resolve_file(&units, &[]).unwrap(); | |
| 2122 | + let st = rr.st; | |
| 1925 | 2123 | validate_file_with_std(&units, &st, Some(std)) |
| 1926 | 2124 | .iter() |
| 1927 | 2125 | .filter(|d| d.kind == DiagKind::Error) |
@@ -1933,29 +2131,34 @@ mod tests { | ||
| 1933 | 2131 | |
| 1934 | 2132 | #[test] |
| 1935 | 2133 | fn assign_to_intent_in_errors() { |
| 1936 | - let errs = errors_from("\ | |
| 2134 | + let errs = errors_from( | |
| 2135 | + "\ | |
| 1937 | 2136 | subroutine foo(x) |
| 1938 | 2137 | real, intent(in) :: x |
| 1939 | 2138 | x = 1.0 |
| 1940 | 2139 | end subroutine |
| 1941 | -"); | |
| 2140 | +", | |
| 2141 | + ); | |
| 1942 | 2142 | assert!(errs.iter().any(|e| e.contains("intent(in)"))); |
| 1943 | 2143 | } |
| 1944 | 2144 | |
| 1945 | 2145 | #[test] |
| 1946 | 2146 | fn assign_to_intent_inout_ok() { |
| 1947 | - let errs = errors_from("\ | |
| 2147 | + let errs = errors_from( | |
| 2148 | + "\ | |
| 1948 | 2149 | subroutine foo(x) |
| 1949 | 2150 | real, intent(inout) :: x |
| 1950 | 2151 | x = 1.0 |
| 1951 | 2152 | end subroutine |
| 1952 | -"); | |
| 2153 | +", | |
| 2154 | + ); | |
| 1953 | 2155 | assert!(errs.is_empty()); |
| 1954 | 2156 | } |
| 1955 | 2157 | |
| 1956 | 2158 | #[test] |
| 1957 | 2159 | fn assign_through_intent_in_pointer_target_ok() { |
| 1958 | - let errs = errors_from("\ | |
| 2160 | + let errs = errors_from( | |
| 2161 | + "\ | |
| 1959 | 2162 | module m |
| 1960 | 2163 | type :: t |
| 1961 | 2164 | integer :: x |
@@ -1966,19 +2169,22 @@ contains | ||
| 1966 | 2169 | p%x = 1 |
| 1967 | 2170 | end subroutine |
| 1968 | 2171 | end module |
| 1969 | -"); | |
| 2172 | +", | |
| 2173 | + ); | |
| 1970 | 2174 | assert!(!errs.iter().any(|e| e.contains("intent(in)")), "{:?}", errs); |
| 1971 | 2175 | } |
| 1972 | 2176 | |
| 1973 | 2177 | #[test] |
| 1974 | 2178 | fn assign_to_parameter_errors() { |
| 1975 | - let errs = errors_from("\ | |
| 2179 | + let errs = errors_from( | |
| 2180 | + "\ | |
| 1976 | 2181 | program test |
| 1977 | 2182 | implicit none |
| 1978 | 2183 | integer, parameter :: n = 10 |
| 1979 | 2184 | n = 20 |
| 1980 | 2185 | end program |
| 1981 | -"); | |
| 2186 | +", | |
| 2187 | + ); | |
| 1982 | 2188 | assert!(errs.iter().any(|e| e.contains("named constant"))); |
| 1983 | 2189 | } |
| 1984 | 2190 | |
@@ -1986,88 +2192,106 @@ end program | ||
| 1986 | 2192 | |
| 1987 | 2193 | #[test] |
| 1988 | 2194 | fn allocate_non_allocatable_errors() { |
| 1989 | - let errs = errors_from("\ | |
| 2195 | + let errs = errors_from( | |
| 2196 | + "\ | |
| 1990 | 2197 | program test |
| 1991 | 2198 | implicit none |
| 1992 | 2199 | real :: x(10) |
| 1993 | 2200 | allocate(x(20)) |
| 1994 | 2201 | end program |
| 1995 | -"); | |
| 2202 | +", | |
| 2203 | + ); | |
| 1996 | 2204 | assert!(errs.iter().any(|e| e.contains("allocatable or pointer"))); |
| 1997 | 2205 | } |
| 1998 | 2206 | |
| 1999 | 2207 | #[test] |
| 2000 | 2208 | fn allocate_allocatable_ok() { |
| 2001 | - let errs = errors_from("\ | |
| 2209 | + let errs = errors_from( | |
| 2210 | + "\ | |
| 2002 | 2211 | program test |
| 2003 | 2212 | implicit none |
| 2004 | 2213 | real, allocatable :: x(:) |
| 2005 | 2214 | allocate(x(10)) |
| 2006 | 2215 | end program |
| 2007 | -"); | |
| 2216 | +", | |
| 2217 | + ); | |
| 2008 | 2218 | assert!(errs.is_empty()); |
| 2009 | 2219 | } |
| 2010 | 2220 | |
| 2011 | 2221 | #[test] |
| 2012 | 2222 | fn allocatable_and_pointer_forbidden() { |
| 2013 | - let errs = errors_from("\ | |
| 2223 | + let errs = errors_from( | |
| 2224 | + "\ | |
| 2014 | 2225 | program test |
| 2015 | 2226 | implicit none |
| 2016 | 2227 | real, allocatable, pointer :: x |
| 2017 | 2228 | end program |
| 2018 | -"); | |
| 2019 | - assert!(errs.iter().any(|e| e.contains("both allocatable and pointer"))); | |
| 2229 | +", | |
| 2230 | + ); | |
| 2231 | + assert!(errs | |
| 2232 | + .iter() | |
| 2233 | + .any(|e| e.contains("both allocatable and pointer"))); | |
| 2020 | 2234 | } |
| 2021 | 2235 | |
| 2022 | 2236 | #[test] |
| 2023 | 2237 | fn parameter_allocatable_forbidden() { |
| 2024 | - let errs = errors_from("\ | |
| 2238 | + let errs = errors_from( | |
| 2239 | + "\ | |
| 2025 | 2240 | program test |
| 2026 | 2241 | implicit none |
| 2027 | 2242 | integer, parameter, allocatable :: x = 10 |
| 2028 | 2243 | end program |
| 2029 | -"); | |
| 2030 | - assert!(errs.iter().any(|e| e.contains("parameter") && e.contains("allocatable"))); | |
| 2244 | +", | |
| 2245 | + ); | |
| 2246 | + assert!(errs | |
| 2247 | + .iter() | |
| 2248 | + .any(|e| e.contains("parameter") && e.contains("allocatable"))); | |
| 2031 | 2249 | } |
| 2032 | 2250 | |
| 2033 | 2251 | // ---- Pointer assignment ---- |
| 2034 | 2252 | |
| 2035 | 2253 | #[test] |
| 2036 | 2254 | fn pointer_assignment_non_pointer_errors() { |
| 2037 | - let errs = errors_from("\ | |
| 2255 | + let errs = errors_from( | |
| 2256 | + "\ | |
| 2038 | 2257 | program test |
| 2039 | 2258 | implicit none |
| 2040 | 2259 | real :: x |
| 2041 | 2260 | real, target :: y |
| 2042 | 2261 | x => y |
| 2043 | 2262 | end program |
| 2044 | -"); | |
| 2263 | +", | |
| 2264 | + ); | |
| 2045 | 2265 | assert!(errs.iter().any(|e| e.contains("pointer attribute"))); |
| 2046 | 2266 | } |
| 2047 | 2267 | |
| 2048 | 2268 | #[test] |
| 2049 | 2269 | fn pointer_assignment_non_target_errors() { |
| 2050 | - let errs = errors_from("\ | |
| 2270 | + let errs = errors_from( | |
| 2271 | + "\ | |
| 2051 | 2272 | program test |
| 2052 | 2273 | implicit none |
| 2053 | 2274 | real, pointer :: p |
| 2054 | 2275 | real :: x |
| 2055 | 2276 | p => x |
| 2056 | 2277 | end program |
| 2057 | -"); | |
| 2278 | +", | |
| 2279 | + ); | |
| 2058 | 2280 | assert!(errs.iter().any(|e| e.contains("target or pointer"))); |
| 2059 | 2281 | } |
| 2060 | 2282 | |
| 2061 | 2283 | #[test] |
| 2062 | 2284 | fn pointer_assignment_ok() { |
| 2063 | - let errs = errors_from("\ | |
| 2285 | + let errs = errors_from( | |
| 2286 | + "\ | |
| 2064 | 2287 | program test |
| 2065 | 2288 | implicit none |
| 2066 | 2289 | real, pointer :: p |
| 2067 | 2290 | real, target :: x |
| 2068 | 2291 | p => x |
| 2069 | 2292 | end program |
| 2070 | -"); | |
| 2293 | +", | |
| 2294 | + ); | |
| 2071 | 2295 | assert!(errs.is_empty()); |
| 2072 | 2296 | } |
| 2073 | 2297 | |
@@ -2075,54 +2299,67 @@ end program | ||
| 2075 | 2299 | |
| 2076 | 2300 | #[test] |
| 2077 | 2301 | fn io_in_pure_errors() { |
| 2078 | - let errs = errors_from("\ | |
| 2302 | + let errs = errors_from( | |
| 2303 | + "\ | |
| 2079 | 2304 | pure subroutine foo(x) |
| 2080 | 2305 | real, intent(in) :: x |
| 2081 | 2306 | print *, x |
| 2082 | 2307 | end subroutine |
| 2083 | -"); | |
| 2308 | +", | |
| 2309 | + ); | |
| 2084 | 2310 | assert!(errs.iter().any(|e| e.contains("I/O") && e.contains("pure"))); |
| 2085 | 2311 | } |
| 2086 | 2312 | |
| 2087 | 2313 | #[test] |
| 2088 | 2314 | fn stop_in_pure_errors() { |
| 2089 | - let errs = errors_from("\ | |
| 2315 | + let errs = errors_from( | |
| 2316 | + "\ | |
| 2090 | 2317 | pure function bar(x) result(y) |
| 2091 | 2318 | real, intent(in) :: x |
| 2092 | 2319 | real :: y |
| 2093 | 2320 | y = x |
| 2094 | 2321 | stop |
| 2095 | 2322 | end function |
| 2096 | -"); | |
| 2097 | - assert!(errs.iter().any(|e| e.contains("STOP") && e.contains("pure"))); | |
| 2323 | +", | |
| 2324 | + ); | |
| 2325 | + assert!(errs | |
| 2326 | + .iter() | |
| 2327 | + .any(|e| e.contains("STOP") && e.contains("pure"))); | |
| 2098 | 2328 | } |
| 2099 | 2329 | |
| 2100 | 2330 | #[test] |
| 2101 | 2331 | fn save_in_pure_errors() { |
| 2102 | - let errs = errors_from("\ | |
| 2332 | + let errs = errors_from( | |
| 2333 | + "\ | |
| 2103 | 2334 | pure subroutine foo(x) |
| 2104 | 2335 | real, intent(in) :: x |
| 2105 | 2336 | real, save :: counter |
| 2106 | 2337 | end subroutine |
| 2107 | -"); | |
| 2108 | - assert!(errs.iter().any(|e| e.contains("SAVE") && e.contains("pure"))); | |
| 2338 | +", | |
| 2339 | + ); | |
| 2340 | + assert!(errs | |
| 2341 | + .iter() | |
| 2342 | + .any(|e| e.contains("SAVE") && e.contains("pure"))); | |
| 2109 | 2343 | } |
| 2110 | 2344 | |
| 2111 | 2345 | #[test] |
| 2112 | 2346 | fn pure_without_violations_ok() { |
| 2113 | - let errs = errors_from("\ | |
| 2347 | + let errs = errors_from( | |
| 2348 | + "\ | |
| 2114 | 2349 | pure function square(x) result(y) |
| 2115 | 2350 | real, intent(in) :: x |
| 2116 | 2351 | real :: y |
| 2117 | 2352 | y = x * x |
| 2118 | 2353 | end function |
| 2119 | -"); | |
| 2354 | +", | |
| 2355 | + ); | |
| 2120 | 2356 | assert!(errs.is_empty()); |
| 2121 | 2357 | } |
| 2122 | 2358 | |
| 2123 | 2359 | #[test] |
| 2124 | 2360 | fn pure_write_to_module_variable_errors() { |
| 2125 | - let errs = errors_from("\ | |
| 2361 | + let errs = errors_from( | |
| 2362 | + "\ | |
| 2126 | 2363 | module m |
| 2127 | 2364 | integer :: counter = 0 |
| 2128 | 2365 | contains |
@@ -2131,9 +2368,12 @@ contains | ||
| 2131 | 2368 | r = counter |
| 2132 | 2369 | end function |
| 2133 | 2370 | end module |
| 2134 | -"); | |
| 2371 | +", | |
| 2372 | + ); | |
| 2135 | 2373 | assert!( |
| 2136 | - errs.iter().any(|e| e.contains("counter") && e.contains("pure") && e.contains("host or use association")), | |
| 2374 | + errs.iter().any(|e| e.contains("counter") | |
| 2375 | + && e.contains("pure") | |
| 2376 | + && e.contains("host or use association")), | |
| 2137 | 2377 | "expected pure+module-write error, got {:?}", |
| 2138 | 2378 | errs, |
| 2139 | 2379 | ); |
@@ -2144,7 +2384,8 @@ end module | ||
| 2144 | 2384 | // F2018 15.7 permits a pure procedure to *reference* a |
| 2145 | 2385 | // variable accessed by use association; only definition |
| 2146 | 2386 | // is forbidden. reads_counter is a legal pure function. |
| 2147 | - let errs = errors_from("\ | |
| 2387 | + let errs = errors_from( | |
| 2388 | + "\ | |
| 2148 | 2389 | module m |
| 2149 | 2390 | integer :: counter = 0 |
| 2150 | 2391 | contains |
@@ -2152,13 +2393,19 @@ contains | ||
| 2152 | 2393 | r = counter |
| 2153 | 2394 | end function |
| 2154 | 2395 | end module |
| 2155 | -"); | |
| 2156 | - assert!(errs.is_empty(), "pure read of module variable should be legal, got {:?}", errs); | |
| 2396 | +", | |
| 2397 | + ); | |
| 2398 | + assert!( | |
| 2399 | + errs.is_empty(), | |
| 2400 | + "pure read of module variable should be legal, got {:?}", | |
| 2401 | + errs | |
| 2402 | + ); | |
| 2157 | 2403 | } |
| 2158 | 2404 | |
| 2159 | 2405 | #[test] |
| 2160 | 2406 | fn pure_write_to_host_variable_errors() { |
| 2161 | - let errs = errors_from("\ | |
| 2407 | + let errs = errors_from( | |
| 2408 | + "\ | |
| 2162 | 2409 | program p |
| 2163 | 2410 | integer :: host_var |
| 2164 | 2411 | host_var = 0 |
@@ -2168,9 +2415,12 @@ contains | ||
| 2168 | 2415 | host_var = 42 |
| 2169 | 2416 | end subroutine |
| 2170 | 2417 | end program |
| 2171 | -"); | |
| 2418 | +", | |
| 2419 | + ); | |
| 2172 | 2420 | assert!( |
| 2173 | - errs.iter().any(|e| e.contains("host_var") && e.contains("pure") && e.contains("host or use association")), | |
| 2421 | + errs.iter().any(|e| e.contains("host_var") | |
| 2422 | + && e.contains("pure") | |
| 2423 | + && e.contains("host or use association")), | |
| 2174 | 2424 | "expected pure+host-write error, got {:?}", |
| 2175 | 2425 | errs, |
| 2176 | 2426 | ); |
@@ -2178,7 +2428,8 @@ end program | ||
| 2178 | 2428 | |
| 2179 | 2429 | #[test] |
| 2180 | 2430 | fn pure_pointer_reassoc_of_module_pointer_errors() { |
| 2181 | - let errs = errors_from("\ | |
| 2431 | + let errs = errors_from( | |
| 2432 | + "\ | |
| 2182 | 2433 | module m |
| 2183 | 2434 | integer, pointer :: module_p |
| 2184 | 2435 | contains |
@@ -2187,9 +2438,12 @@ contains | ||
| 2187 | 2438 | module_p => t |
| 2188 | 2439 | end subroutine |
| 2189 | 2440 | end module |
| 2190 | -"); | |
| 2441 | +", | |
| 2442 | + ); | |
| 2191 | 2443 | assert!( |
| 2192 | - errs.iter().any(|e| e.contains("module_p") && e.contains("pure") && e.contains("pointer assignment")), | |
| 2444 | + errs.iter().any(|e| e.contains("module_p") | |
| 2445 | + && e.contains("pure") | |
| 2446 | + && e.contains("pointer assignment")), | |
| 2193 | 2447 | "expected pure+module-pointer error, got {:?}", |
| 2194 | 2448 | errs, |
| 2195 | 2449 | ); |
@@ -2199,7 +2453,8 @@ end module | ||
| 2199 | 2453 | fn pure_local_pointer_reassoc_ok() { |
| 2200 | 2454 | // Associating a LOCAL pointer with a module TARGET is |
| 2201 | 2455 | // legal — `q => counter` does not modify `counter`. |
| 2202 | - let errs = errors_from("\ | |
| 2456 | + let errs = errors_from( | |
| 2457 | + "\ | |
| 2203 | 2458 | module m |
| 2204 | 2459 | integer, target :: counter = 0 |
| 2205 | 2460 | contains |
@@ -2209,42 +2464,57 @@ contains | ||
| 2209 | 2464 | r = 0 |
| 2210 | 2465 | end function |
| 2211 | 2466 | end module |
| 2212 | -"); | |
| 2213 | - assert!(errs.is_empty(), "pure local pointer reassoc should be legal, got {:?}", errs); | |
| 2467 | +", | |
| 2468 | + ); | |
| 2469 | + assert!( | |
| 2470 | + errs.is_empty(), | |
| 2471 | + "pure local pointer reassoc should be legal, got {:?}", | |
| 2472 | + errs | |
| 2473 | + ); | |
| 2214 | 2474 | } |
| 2215 | 2475 | |
| 2216 | 2476 | #[test] |
| 2217 | 2477 | fn pure_intent_out_dummy_ok() { |
| 2218 | - let errs = errors_from("\ | |
| 2478 | + let errs = errors_from( | |
| 2479 | + "\ | |
| 2219 | 2480 | pure subroutine zero_it(x) |
| 2220 | 2481 | integer, intent(out) :: x |
| 2221 | 2482 | x = 0 |
| 2222 | 2483 | end subroutine |
| 2223 | -"); | |
| 2224 | - assert!(errs.is_empty(), "pure write to intent(out) dummy should be legal, got {:?}", errs); | |
| 2484 | +", | |
| 2485 | + ); | |
| 2486 | + assert!( | |
| 2487 | + errs.is_empty(), | |
| 2488 | + "pure write to intent(out) dummy should be legal, got {:?}", | |
| 2489 | + errs | |
| 2490 | + ); | |
| 2225 | 2491 | } |
| 2226 | 2492 | |
| 2227 | 2493 | // ---- Deferred length character ---- |
| 2228 | 2494 | |
| 2229 | 2495 | #[test] |
| 2230 | 2496 | fn deferred_len_without_allocatable_errors() { |
| 2231 | - let errs = errors_from("\ | |
| 2497 | + let errs = errors_from( | |
| 2498 | + "\ | |
| 2232 | 2499 | program test |
| 2233 | 2500 | implicit none |
| 2234 | 2501 | character(len=:) :: s |
| 2235 | 2502 | end program |
| 2236 | -"); | |
| 2503 | +", | |
| 2504 | + ); | |
| 2237 | 2505 | assert!(errs.iter().any(|e| e.contains("deferred-length"))); |
| 2238 | 2506 | } |
| 2239 | 2507 | |
| 2240 | 2508 | #[test] |
| 2241 | 2509 | fn deferred_len_with_allocatable_ok() { |
| 2242 | - let errs = errors_from("\ | |
| 2510 | + let errs = errors_from( | |
| 2511 | + "\ | |
| 2243 | 2512 | program test |
| 2244 | 2513 | implicit none |
| 2245 | 2514 | character(len=:), allocatable :: s |
| 2246 | 2515 | end program |
| 2247 | -"); | |
| 2516 | +", | |
| 2517 | + ); | |
| 2248 | 2518 | assert!(errs.is_empty()); |
| 2249 | 2519 | } |
| 2250 | 2520 | |
@@ -2292,10 +2562,14 @@ end program | ||
| 2292 | 2562 | |
| 2293 | 2563 | #[test] |
| 2294 | 2564 | fn duplicate_label_detected() { |
| 2295 | - use crate::lexer::{Span, Position}; | |
| 2565 | + use crate::lexer::{Position, Span}; | |
| 2296 | 2566 | let st = SymbolTable::new(); |
| 2297 | 2567 | let mut ctx = Ctx::new(&st, None, false, false); |
| 2298 | - let span = Span { file_id: 0, start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } }; | |
| 2568 | + let span = Span { | |
| 2569 | + file_id: 0, | |
| 2570 | + start: Position { line: 1, col: 1 }, | |
| 2571 | + end: Position { line: 1, col: 1 }, | |
| 2572 | + }; | |
| 2299 | 2573 | |
| 2300 | 2574 | register_label(&mut ctx, 10, span); |
| 2301 | 2575 | register_label(&mut ctx, 10, span); // duplicate |
@@ -2306,7 +2580,8 @@ end program | ||
| 2306 | 2580 | |
| 2307 | 2581 | #[test] |
| 2308 | 2582 | fn clean_program_no_errors() { |
| 2309 | - let errs = errors_from("\ | |
| 2583 | + let errs = errors_from( | |
| 2584 | + "\ | |
| 2310 | 2585 | program test |
| 2311 | 2586 | implicit none |
| 2312 | 2587 | integer :: i, n |
@@ -2316,13 +2591,15 @@ program test | ||
| 2316 | 2591 | x = real(i) * 2.0 |
| 2317 | 2592 | end do |
| 2318 | 2593 | end program |
| 2319 | -"); | |
| 2594 | +", | |
| 2595 | + ); | |
| 2320 | 2596 | assert!(errs.is_empty(), "unexpected errors: {:?}", errs); |
| 2321 | 2597 | } |
| 2322 | 2598 | |
| 2323 | 2599 | #[test] |
| 2324 | 2600 | fn module_with_subroutine_no_errors() { |
| 2325 | - let errs = errors_from("\ | |
| 2601 | + let errs = errors_from( | |
| 2602 | + "\ | |
| 2326 | 2603 | module mymod |
| 2327 | 2604 | implicit none |
| 2328 | 2605 | integer :: shared |
@@ -2332,13 +2609,15 @@ contains | ||
| 2332 | 2609 | shared = val |
| 2333 | 2610 | end subroutine |
| 2334 | 2611 | end module |
| 2335 | -"); | |
| 2612 | +", | |
| 2613 | + ); | |
| 2336 | 2614 | assert!(errs.is_empty(), "unexpected errors: {:?}", errs); |
| 2337 | 2615 | } |
| 2338 | 2616 | |
| 2339 | 2617 | #[test] |
| 2340 | 2618 | fn module_parameter_visible_in_contained_subroutine() { |
| 2341 | - let errs = errors_from("\ | |
| 2619 | + let errs = errors_from( | |
| 2620 | + "\ | |
| 2342 | 2621 | module m |
| 2343 | 2622 | use iso_c_binding, only: c_int |
| 2344 | 2623 | implicit none |
@@ -2351,7 +2630,8 @@ contains | ||
| 2351 | 2630 | print *, color_red |
| 2352 | 2631 | end subroutine |
| 2353 | 2632 | end module |
| 2354 | -"); | |
| 2633 | +", | |
| 2634 | + ); | |
| 2355 | 2635 | assert!(errs.is_empty(), "unexpected errors: {:?}", errs); |
| 2356 | 2636 | } |
| 2357 | 2637 | |
@@ -2363,65 +2643,79 @@ end module | ||
| 2363 | 2643 | #[test] |
| 2364 | 2644 | fn operator_interface_subroutine_errors() { |
| 2365 | 2645 | // Parse a top-level interface block with operator name. |
| 2366 | - let errs = errors_from("\ | |
| 2646 | + let errs = errors_from( | |
| 2647 | + "\ | |
| 2367 | 2648 | interface operator(+) |
| 2368 | 2649 | subroutine bad_add(a, b) |
| 2369 | 2650 | integer, intent(in) :: a, b |
| 2370 | 2651 | end subroutine |
| 2371 | 2652 | end interface |
| 2372 | -"); | |
| 2373 | - assert!(errs.iter().any(|e| e.contains("functions, not subroutines"))); | |
| 2653 | +", | |
| 2654 | + ); | |
| 2655 | + assert!(errs | |
| 2656 | + .iter() | |
| 2657 | + .any(|e| e.contains("functions, not subroutines"))); | |
| 2374 | 2658 | } |
| 2375 | 2659 | |
| 2376 | 2660 | #[test] |
| 2377 | 2661 | fn operator_interface_wrong_arg_count() { |
| 2378 | - let errs = errors_from("\ | |
| 2662 | + let errs = errors_from( | |
| 2663 | + "\ | |
| 2379 | 2664 | interface operator(+) |
| 2380 | 2665 | function add3(a, b, c) result(r) |
| 2381 | 2666 | integer, intent(in) :: a, b, c |
| 2382 | 2667 | integer :: r |
| 2383 | 2668 | end function |
| 2384 | 2669 | end interface |
| 2385 | -"); | |
| 2670 | +", | |
| 2671 | + ); | |
| 2386 | 2672 | assert!(errs.iter().any(|e| e.contains("1 or 2 arguments"))); |
| 2387 | 2673 | } |
| 2388 | 2674 | |
| 2389 | 2675 | #[test] |
| 2390 | 2676 | fn operator_interface_valid_binary() { |
| 2391 | - let errs = errors_from("\ | |
| 2677 | + let errs = errors_from( | |
| 2678 | + "\ | |
| 2392 | 2679 | interface operator(+) |
| 2393 | 2680 | function add_vec(a, b) result(c) |
| 2394 | 2681 | integer, intent(in) :: a, b |
| 2395 | 2682 | integer :: c |
| 2396 | 2683 | end function |
| 2397 | 2684 | end interface |
| 2398 | -"); | |
| 2685 | +", | |
| 2686 | + ); | |
| 2399 | 2687 | assert!(errs.is_empty(), "unexpected errors: {:?}", errs); |
| 2400 | 2688 | } |
| 2401 | 2689 | |
| 2402 | 2690 | #[test] |
| 2403 | 2691 | fn assignment_interface_function_errors() { |
| 2404 | - let errs = errors_from("\ | |
| 2692 | + let errs = errors_from( | |
| 2693 | + "\ | |
| 2405 | 2694 | interface assignment(=) |
| 2406 | 2695 | function bad_assign(a, b) result(c) |
| 2407 | 2696 | integer, intent(in) :: a, b |
| 2408 | 2697 | integer :: c |
| 2409 | 2698 | end function |
| 2410 | 2699 | end interface |
| 2411 | -"); | |
| 2412 | - assert!(errs.iter().any(|e| e.contains("subroutines, not functions"))); | |
| 2700 | +", | |
| 2701 | + ); | |
| 2702 | + assert!(errs | |
| 2703 | + .iter() | |
| 2704 | + .any(|e| e.contains("subroutines, not functions"))); | |
| 2413 | 2705 | } |
| 2414 | 2706 | |
| 2415 | 2707 | #[test] |
| 2416 | 2708 | fn assignment_interface_wrong_arg_count() { |
| 2417 | - let errs = errors_from("\ | |
| 2709 | + let errs = errors_from( | |
| 2710 | + "\ | |
| 2418 | 2711 | interface assignment(=) |
| 2419 | 2712 | subroutine bad_assign(a, b, c) |
| 2420 | 2713 | integer, intent(inout) :: a |
| 2421 | 2714 | integer, intent(in) :: b, c |
| 2422 | 2715 | end subroutine |
| 2423 | 2716 | end interface |
| 2424 | -"); | |
| 2717 | +", | |
| 2718 | + ); | |
| 2425 | 2719 | assert!(errs.iter().any(|e| e.contains("2 arguments"))); |
| 2426 | 2720 | } |
| 2427 | 2721 | |
@@ -2429,7 +2723,8 @@ end interface | ||
| 2429 | 2723 | |
| 2430 | 2724 | #[test] |
| 2431 | 2725 | fn deferred_in_non_abstract_errors() { |
| 2432 | - let errs = errors_from("\ | |
| 2726 | + let errs = errors_from( | |
| 2727 | + "\ | |
| 2433 | 2728 | module m |
| 2434 | 2729 | implicit none |
| 2435 | 2730 | type :: shape |
@@ -2437,13 +2732,17 @@ module m | ||
| 2437 | 2732 | procedure, deferred :: area |
| 2438 | 2733 | end type |
| 2439 | 2734 | end module |
| 2440 | -"); | |
| 2441 | - assert!(errs.iter().any(|e| e.contains("DEFERRED") && e.contains("not ABSTRACT"))); | |
| 2735 | +", | |
| 2736 | + ); | |
| 2737 | + assert!(errs | |
| 2738 | + .iter() | |
| 2739 | + .any(|e| e.contains("DEFERRED") && e.contains("not ABSTRACT"))); | |
| 2442 | 2740 | } |
| 2443 | 2741 | |
| 2444 | 2742 | #[test] |
| 2445 | 2743 | fn deferred_in_abstract_ok() { |
| 2446 | - let errs = errors_from("\ | |
| 2744 | + let errs = errors_from( | |
| 2745 | + "\ | |
| 2447 | 2746 | module m |
| 2448 | 2747 | implicit none |
| 2449 | 2748 | type, abstract :: shape |
@@ -2451,7 +2750,8 @@ module m | ||
| 2451 | 2750 | procedure, deferred :: area |
| 2452 | 2751 | end type |
| 2453 | 2752 | end module |
| 2454 | -"); | |
| 2753 | +", | |
| 2754 | + ); | |
| 2455 | 2755 | // No error for deferred in abstract type (the "must specify interface" |
| 2456 | 2756 | // error is expected since our parser stores binding as None for simple |
| 2457 | 2757 | // deferred procedures — that's a parser representation issue). |
@@ -2460,7 +2760,8 @@ end module | ||
| 2460 | 2760 | |
| 2461 | 2761 | #[test] |
| 2462 | 2762 | fn pass_and_nopass_together_errors() { |
| 2463 | - let errs = errors_from("\ | |
| 2763 | + let errs = errors_from( | |
| 2764 | + "\ | |
| 2464 | 2765 | module m |
| 2465 | 2766 | implicit none |
| 2466 | 2767 | type :: thing |
@@ -2468,7 +2769,8 @@ module m | ||
| 2468 | 2769 | procedure, pass, nopass :: method |
| 2469 | 2770 | end type |
| 2470 | 2771 | end module |
| 2471 | -"); | |
| 2772 | +", | |
| 2773 | + ); | |
| 2472 | 2774 | assert!(errs.iter().any(|e| e.contains("both PASS and NOPASS"))); |
| 2473 | 2775 | } |
| 2474 | 2776 | |
@@ -2476,57 +2778,76 @@ end module | ||
| 2476 | 2778 | |
| 2477 | 2779 | #[test] |
| 2478 | 2780 | fn do_concurrent_requires_f2008() { |
| 2479 | - let errs = errors_with_std("\ | |
| 2781 | + let errs = errors_with_std( | |
| 2782 | + "\ | |
| 2480 | 2783 | program test |
| 2481 | 2784 | implicit none |
| 2482 | 2785 | integer :: i |
| 2483 | 2786 | do concurrent (i = 1:10) |
| 2484 | 2787 | end do |
| 2485 | 2788 | end program |
| 2486 | -", FortranStandard::F95); | |
| 2487 | - assert!(errs.iter().any(|e| e.contains("DO CONCURRENT") && e.contains("F2008"))); | |
| 2789 | +", | |
| 2790 | + FortranStandard::F95, | |
| 2791 | + ); | |
| 2792 | + assert!(errs | |
| 2793 | + .iter() | |
| 2794 | + .any(|e| e.contains("DO CONCURRENT") && e.contains("F2008"))); | |
| 2488 | 2795 | } |
| 2489 | 2796 | |
| 2490 | 2797 | #[test] |
| 2491 | 2798 | fn do_concurrent_ok_with_f2008() { |
| 2492 | - let errs = errors_with_std("\ | |
| 2799 | + let errs = errors_with_std( | |
| 2800 | + "\ | |
| 2493 | 2801 | program test |
| 2494 | 2802 | implicit none |
| 2495 | 2803 | integer :: i |
| 2496 | 2804 | do concurrent (i = 1:10) |
| 2497 | 2805 | end do |
| 2498 | 2806 | end program |
| 2499 | -", FortranStandard::F2008); | |
| 2807 | +", | |
| 2808 | + FortranStandard::F2008, | |
| 2809 | + ); | |
| 2500 | 2810 | assert!(!errs.iter().any(|e| e.contains("DO CONCURRENT"))); |
| 2501 | 2811 | } |
| 2502 | 2812 | |
| 2503 | 2813 | #[test] |
| 2504 | 2814 | fn error_stop_requires_f2008() { |
| 2505 | - let errs = errors_with_std("\ | |
| 2815 | + let errs = errors_with_std( | |
| 2816 | + "\ | |
| 2506 | 2817 | program test |
| 2507 | 2818 | implicit none |
| 2508 | 2819 | error stop |
| 2509 | 2820 | end program |
| 2510 | -", FortranStandard::F95); | |
| 2511 | - assert!(errs.iter().any(|e| e.contains("ERROR STOP") && e.contains("F2008"))); | |
| 2821 | +", | |
| 2822 | + FortranStandard::F95, | |
| 2823 | + ); | |
| 2824 | + assert!(errs | |
| 2825 | + .iter() | |
| 2826 | + .any(|e| e.contains("ERROR STOP") && e.contains("F2008"))); | |
| 2512 | 2827 | } |
| 2513 | 2828 | |
| 2514 | 2829 | #[test] |
| 2515 | 2830 | fn block_construct_requires_f2008() { |
| 2516 | - let errs = errors_with_std("\ | |
| 2831 | + let errs = errors_with_std( | |
| 2832 | + "\ | |
| 2517 | 2833 | program test |
| 2518 | 2834 | implicit none |
| 2519 | 2835 | block |
| 2520 | 2836 | x = 1 |
| 2521 | 2837 | end block |
| 2522 | 2838 | end program |
| 2523 | -", FortranStandard::F95); | |
| 2524 | - assert!(errs.iter().any(|e| e.contains("BLOCK") && e.contains("F2008"))); | |
| 2839 | +", | |
| 2840 | + FortranStandard::F95, | |
| 2841 | + ); | |
| 2842 | + assert!(errs | |
| 2843 | + .iter() | |
| 2844 | + .any(|e| e.contains("BLOCK") && e.contains("F2008"))); | |
| 2525 | 2845 | } |
| 2526 | 2846 | |
| 2527 | 2847 | #[test] |
| 2528 | 2848 | fn associate_requires_f2003() { |
| 2529 | - let errs = errors_with_std("\ | |
| 2849 | + let errs = errors_with_std( | |
| 2850 | + "\ | |
| 2530 | 2851 | program test |
| 2531 | 2852 | implicit none |
| 2532 | 2853 | integer :: n |
@@ -2534,14 +2855,19 @@ program test | ||
| 2534 | 2855 | associate (m => n) |
| 2535 | 2856 | end associate |
| 2536 | 2857 | end program |
| 2537 | -", FortranStandard::F95); | |
| 2538 | - assert!(errs.iter().any(|e| e.contains("ASSOCIATE") && e.contains("F2003"))); | |
| 2858 | +", | |
| 2859 | + FortranStandard::F95, | |
| 2860 | + ); | |
| 2861 | + assert!(errs | |
| 2862 | + .iter() | |
| 2863 | + .any(|e| e.contains("ASSOCIATE") && e.contains("F2003"))); | |
| 2539 | 2864 | } |
| 2540 | 2865 | |
| 2541 | 2866 | #[test] |
| 2542 | 2867 | fn no_std_violations_when_unset() { |
| 2543 | 2868 | // With no --std= set, everything is allowed. |
| 2544 | - let errs = errors_from("\ | |
| 2869 | + let errs = errors_from( | |
| 2870 | + "\ | |
| 2545 | 2871 | program test |
| 2546 | 2872 | implicit none |
| 2547 | 2873 | integer :: i |
@@ -2565,7 +2891,9 @@ end subroutine | ||
| 2565 | 2891 | ", |
| 2566 | 2892 | FortranStandard::F95, |
| 2567 | 2893 | ); |
| 2568 | - assert!(errs.iter().any(|e| e.contains("IMPURE") && e.contains("F2008"))); | |
| 2894 | + assert!(errs | |
| 2895 | + .iter() | |
| 2896 | + .any(|e| e.contains("IMPURE") && e.contains("F2008"))); | |
| 2569 | 2897 | } |
| 2570 | 2898 | |
| 2571 | 2899 | #[test] |
@@ -2588,13 +2916,16 @@ end subroutine | ||
| 2588 | 2916 | }, |
| 2589 | 2917 | span, |
| 2590 | 2918 | ); |
| 2591 | - let diags = validate_file_with_std(&[unit], &SymbolTable::new(), Some(FortranStandard::F95)); | |
| 2919 | + let diags = | |
| 2920 | + validate_file_with_std(&[unit], &SymbolTable::new(), Some(FortranStandard::F95)); | |
| 2592 | 2921 | let errs: Vec<_> = diags |
| 2593 | 2922 | .into_iter() |
| 2594 | 2923 | .filter(|d| d.kind == DiagKind::Error) |
| 2595 | 2924 | .map(|d| d.msg) |
| 2596 | 2925 | .collect(); |
| 2597 | - assert!(errs.iter().any(|e| e.contains("SUBMODULE") && e.contains("F2008"))); | |
| 2926 | + assert!(errs | |
| 2927 | + .iter() | |
| 2928 | + .any(|e| e.contains("SUBMODULE") && e.contains("F2008"))); | |
| 2598 | 2929 | } |
| 2599 | 2930 | |
| 2600 | 2931 | #[test] |
@@ -2701,7 +3032,9 @@ end program | ||
| 2701 | 3032 | ", |
| 2702 | 3033 | FortranStandard::F95, |
| 2703 | 3034 | ); |
| 2704 | - assert!(errs.iter().any(|e| e.contains("MOVE_ALLOC") && e.contains("F2003"))); | |
| 3035 | + assert!(errs | |
| 3036 | + .iter() | |
| 3037 | + .any(|e| e.contains("MOVE_ALLOC") && e.contains("F2003"))); | |
| 2705 | 3038 | } |
| 2706 | 3039 | |
| 2707 | 3040 | // ---- Elemental ---- |
src/testing.rsmodified@@ -13,15 +13,15 @@ use std::sync::atomic::{AtomicU64, Ordering}; | ||
| 13 | 13 | use std::time::SystemTime; |
| 14 | 14 | |
| 15 | 15 | use crate::codegen::mir::{ |
| 16 | - ArmCond, ConstPoolEntry, MachineFunction, MachineInst, MachineOperand, MBlockId, PhysReg, | |
| 16 | + ArmCond, ConstPoolEntry, MBlockId, MachineFunction, MachineInst, MachineOperand, PhysReg, | |
| 17 | 17 | RegClass, |
| 18 | 18 | }; |
| 19 | 19 | use crate::codegen::{emit, isel, linearscan}; |
| 20 | 20 | use crate::driver::OptLevel; |
| 21 | -use crate::opt::{build_i128_pipeline, build_pipeline}; | |
| 22 | -use crate::opt::pipeline::OptLevel as IrOptLevel; | |
| 23 | 21 | use crate::ir::{lower, printer as ir_printer, verify}; |
| 24 | 22 | use crate::lexer::{detect_source_form, tokenize, SourceForm, Token}; |
| 23 | +use crate::opt::pipeline::OptLevel as IrOptLevel; | |
| 24 | +use crate::opt::{build_i128_pipeline, build_pipeline}; | |
| 25 | 25 | use crate::parser::Parser; |
| 26 | 26 | use crate::sema::{resolve, validate}; |
| 27 | 27 | |
@@ -243,7 +243,10 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt | ||
| 243 | 243 | let mut stages = BTreeMap::new(); |
| 244 | 244 | let wants = |stage| request.requested.contains(&stage); |
| 245 | 245 | let needs_backend = request.requested.iter().any(|stage| { |
| 246 | - matches!(stage, Stage::Mir | Stage::Regalloc | Stage::Asm | Stage::Obj | Stage::Run) | |
| 246 | + matches!( | |
| 247 | + stage, | |
| 248 | + Stage::Mir | Stage::Regalloc | Stage::Asm | Stage::Obj | Stage::Run | |
| 249 | + ) | |
| 247 | 250 | }); |
| 248 | 251 | let input = request.input.clone(); |
| 249 | 252 | let source_form = detect_source_form(&input.to_string_lossy()); |
@@ -338,7 +341,13 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt | ||
| 338 | 341 | }); |
| 339 | 342 | } |
| 340 | 343 | |
| 341 | - let (ir_module, _module_globals) = lower::lower_file(&units, &st, &type_layouts, std::collections::HashMap::new(), std::collections::HashMap::new()); | |
| 344 | + let (ir_module, _module_globals) = lower::lower_file( | |
| 345 | + &units, | |
| 346 | + &st, | |
| 347 | + &type_layouts, | |
| 348 | + std::collections::HashMap::new(), | |
| 349 | + std::collections::HashMap::new(), | |
| 350 | + ); | |
| 342 | 351 | let ir_errors = verify::verify_module(&ir_module); |
| 343 | 352 | if !ir_errors.is_empty() { |
| 344 | 353 | let msg = ir_errors |
@@ -360,14 +369,12 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt | ||
| 360 | 369 | stages.insert(Stage::Ir, CapturedStage::Text(ir_text.clone())); |
| 361 | 370 | } |
| 362 | 371 | let module_has_i128 = ir_module.contains_i128(); |
| 363 | - let needs_optimized_pipeline = | |
| 364 | - request.requested.iter().any(|stage| { | |
| 365 | - matches!( | |
| 366 | - stage, | |
| 367 | - Stage::OptIr | Stage::Mir | Stage::Regalloc | Stage::Asm | Stage::Obj | Stage::Run | |
| 368 | - ) | |
| 369 | - }) | |
| 370 | - && request.opt_level != OptLevel::O0; | |
| 372 | + let needs_optimized_pipeline = request.requested.iter().any(|stage| { | |
| 373 | + matches!( | |
| 374 | + stage, | |
| 375 | + Stage::OptIr | Stage::Mir | Stage::Regalloc | Stage::Asm | Stage::Obj | Stage::Run | |
| 376 | + ) | |
| 377 | + }) && request.opt_level != OptLevel::O0; | |
| 371 | 378 | |
| 372 | 379 | let optimized_module = if needs_optimized_pipeline { |
| 373 | 380 | let mut optimized = ir_module.clone(); |
@@ -422,7 +429,9 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt | ||
| 422 | 429 | input: input.clone(), |
| 423 | 430 | opt_level: request.opt_level, |
| 424 | 431 | stage: FailureStage::Ir, |
| 425 | - detail: "backend does not yet support integer(16) / i128 codegen; capture IR at O0 for now".into(), | |
| 432 | + detail: | |
| 433 | + "backend does not yet support integer(16) / i128 codegen; capture IR at O0 for now" | |
| 434 | + .into(), | |
| 426 | 435 | stages, |
| 427 | 436 | }); |
| 428 | 437 | } |
@@ -560,10 +569,7 @@ fn collect_sandbox_files( | ||
| 560 | 569 | collect_sandbox_files(root, &path, out)?; |
| 561 | 570 | } else { |
| 562 | 571 | let rel = path.strip_prefix(root).unwrap(); |
| 563 | - out.insert( | |
| 564 | - rel.to_string_lossy().replace('\\', "/"), | |
| 565 | - fs::read(&path)?, | |
| 566 | - ); | |
| 572 | + out.insert(rel.to_string_lossy().replace('\\', "/"), fs::read(&path)?); | |
| 567 | 573 | } |
| 568 | 574 | } |
| 569 | 575 | Ok(()) |
@@ -942,7 +948,9 @@ fn maybe_refresh_runtime_lib(workspace_root: &Path) -> Result<(), String> { | ||
| 942 | 948 | return Ok(()); |
| 943 | 949 | }; |
| 944 | 950 | let debug_archive = workspace_root.join("target/debug/libarmfortas_rt.a"); |
| 945 | - let archive_mtime = fs::metadata(&debug_archive).ok().and_then(|meta| meta.modified().ok()); | |
| 951 | + let archive_mtime = fs::metadata(&debug_archive) | |
| 952 | + .ok() | |
| 953 | + .and_then(|meta| meta.modified().ok()); | |
| 946 | 954 | |
| 947 | 955 | if archive_mtime.is_some_and(|mtime| mtime >= source_mtime) { |
| 948 | 956 | return Ok(()); |
@@ -992,7 +1000,8 @@ fn find_workspace_root() -> Option<PathBuf> { | ||
| 992 | 1000 | |
| 993 | 1001 | for base in bases { |
| 994 | 1002 | for ancestor in base.ancestors() { |
| 995 | - if ancestor.join("Cargo.toml").exists() && ancestor.join("runtime/Cargo.toml").exists() { | |
| 1003 | + if ancestor.join("Cargo.toml").exists() && ancestor.join("runtime/Cargo.toml").exists() | |
| 1004 | + { | |
| 996 | 1005 | return Some(ancestor.to_path_buf()); |
| 997 | 1006 | } |
| 998 | 1007 | } |
tests/artifact_audit_29_10.rsmodified@@ -115,7 +115,14 @@ fn optimized_object_snapshot_removes_inlined_helper_symbol() { | ||
| 115 | 115 | fn object_snapshot_is_deterministic_with_module_globals_across_opt_levels() { |
| 116 | 116 | let source = fixture("module_init.f90"); |
| 117 | 117 | |
| 118 | - for opt in [OptLevel::O0, OptLevel::O1, OptLevel::O2, OptLevel::O3, OptLevel::Os, OptLevel::Ofast] { | |
| 118 | + for opt in [ | |
| 119 | + OptLevel::O0, | |
| 120 | + OptLevel::O1, | |
| 121 | + OptLevel::O2, | |
| 122 | + OptLevel::O3, | |
| 123 | + OptLevel::Os, | |
| 124 | + OptLevel::Ofast, | |
| 125 | + ] { | |
| 119 | 126 | let first = capture_text( |
| 120 | 127 | CaptureRequest { |
| 121 | 128 | input: source.clone(), |
@@ -133,7 +140,8 @@ fn object_snapshot_is_deterministic_with_module_globals_across_opt_levels() { | ||
| 133 | 140 | Stage::Obj, |
| 134 | 141 | ); |
| 135 | 142 | assert_eq!( |
| 136 | - first, second, | |
| 143 | + first, | |
| 144 | + second, | |
| 137 | 145 | "object snapshot should be deterministic for module globals at {}", |
| 138 | 146 | opt_name(opt) |
| 139 | 147 | ); |
tests/claims_audit_29_11.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -137,7 +139,10 @@ fn o2_realworld_ipo_chain_trims_dead_arg_and_removes_trivial_wrapper() { | ||
| 137 | 139 | "optimized IR should remove the trivial wrapper helper:\n{}", |
| 138 | 140 | opt_ir |
| 139 | 141 | ); |
| 140 | - assert_eq!(obj_a, obj_b, "IPO-audited O2 object snapshot should stay deterministic"); | |
| 142 | + assert_eq!( | |
| 143 | + obj_a, obj_b, | |
| 144 | + "IPO-audited O2 object snapshot should stay deterministic" | |
| 145 | + ); | |
| 141 | 146 | } |
| 142 | 147 | |
| 143 | 148 | #[test] |
@@ -236,5 +241,8 @@ fn o3_vectorizes_realworld_explicit_do_stage() { | ||
| 236 | 241 | "vectorized O3 assembly should reference the bulk add kernel:\n{}", |
| 237 | 242 | o3_asm |
| 238 | 243 | ); |
| 239 | - assert_eq!(o3_obj_a, o3_obj_b, "vectorized O3 object snapshot should stay deterministic"); | |
| 244 | + assert_eq!( | |
| 245 | + o3_obj_a, o3_obj_b, | |
| 246 | + "vectorized O3 object snapshot should stay deterministic" | |
| 247 | + ); | |
| 240 | 248 | } |
tests/determinism_sweep.rsmodified@@ -28,7 +28,9 @@ fn find_compiler() -> PathBuf { | ||
| 28 | 28 | fn find_test_programs() -> PathBuf { |
| 29 | 29 | for c in &["test_programs", "../test_programs"] { |
| 30 | 30 | let p = PathBuf::from(c); |
| 31 | - if p.is_dir() { return p; } | |
| 31 | + if p.is_dir() { | |
| 32 | + return p; | |
| 33 | + } | |
| 32 | 34 | } |
| 33 | 35 | panic!("cannot find test_programs/ directory"); |
| 34 | 36 | } |
@@ -36,7 +38,11 @@ fn find_test_programs() -> PathBuf { | ||
| 36 | 38 | fn unique_path(prefix: &str, ext: &str) -> PathBuf { |
| 37 | 39 | let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); |
| 38 | 40 | std::env::temp_dir().join(format!( |
| 39 | - "afs_det_{}_{}_{}{}", prefix, std::process::id(), id, ext | |
| 41 | + "afs_det_{}_{}_{}{}", | |
| 42 | + prefix, | |
| 43 | + std::process::id(), | |
| 44 | + id, | |
| 45 | + ext | |
| 40 | 46 | )) |
| 41 | 47 | } |
| 42 | 48 | |
@@ -45,7 +51,13 @@ fn unique_path(prefix: &str, ext: &str) -> PathBuf { | ||
| 45 | 51 | fn compile_to_asm(compiler: &Path, source: &Path, opt: &str) -> Option<Vec<u8>> { |
| 46 | 52 | let out = unique_path("sweep", ".s"); |
| 47 | 53 | let result = Command::new(compiler) |
| 48 | - .args([source.to_str().unwrap(), opt, "-S", "-o", out.to_str().unwrap()]) | |
| 54 | + .args([ | |
| 55 | + source.to_str().unwrap(), | |
| 56 | + opt, | |
| 57 | + "-S", | |
| 58 | + "-o", | |
| 59 | + out.to_str().unwrap(), | |
| 60 | + ]) | |
| 49 | 61 | .output() |
| 50 | 62 | .expect("compiler launch failed"); |
| 51 | 63 | if !result.status.success() { |
@@ -112,7 +124,9 @@ fn all_programs_deterministic_at_o2() { | ||
| 112 | 124 | if first != second { |
| 113 | 125 | failures.push(format!( |
| 114 | 126 | "{}: assembly differs between two -O2 compilations ({} vs {} bytes)", |
| 115 | - name, first.len(), second.len(), | |
| 127 | + name, | |
| 128 | + first.len(), | |
| 129 | + second.len(), | |
| 116 | 130 | )); |
| 117 | 131 | } else { |
| 118 | 132 | checked += 1; |
@@ -121,14 +135,14 @@ fn all_programs_deterministic_at_o2() { | ||
| 121 | 135 | |
| 122 | 136 | eprintln!( |
| 123 | 137 | "\nDeterminism sweep: {} checked, {} skipped, {} failures out of {} programs", |
| 124 | - checked, skipped, failures.len(), sources.len(), | |
| 138 | + checked, | |
| 139 | + skipped, | |
| 140 | + failures.len(), | |
| 141 | + sources.len(), | |
| 125 | 142 | ); |
| 126 | 143 | |
| 127 | 144 | if !failures.is_empty() { |
| 128 | - panic!( | |
| 129 | - "Determinism failures:\n{}", | |
| 130 | - failures.join("\n"), | |
| 131 | - ); | |
| 145 | + panic!("Determinism failures:\n{}", failures.join("\n"),); | |
| 132 | 146 | } |
| 133 | 147 | |
| 134 | 148 | // Sanity: we should have checked a substantial portion. |
tests/fortran_alias_licm.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -36,13 +38,13 @@ fn block_section<'a>(func_section: &'a str, prefix: &str) -> &'a str { | ||
| 36 | 38 | for (idx, _line) in func_section.match_indices('\n') { |
| 37 | 39 | let line_start = idx + 1; |
| 38 | 40 | let tail = &func_section[line_start..]; |
| 39 | - let line_text = tail | |
| 40 | - .split_once('\n') | |
| 41 | - .map(|(line, _)| line) | |
| 42 | - .unwrap_or(tail); | |
| 41 | + let line_text = tail.split_once('\n').map(|(line, _)| line).unwrap_or(tail); | |
| 43 | 42 | |
| 44 | 43 | if start.is_none() { |
| 45 | - if line_text.starts_with(" ") && !line_text.starts_with(" ") && line_text[4..].starts_with(prefix) { | |
| 44 | + if line_text.starts_with(" ") | |
| 45 | + && !line_text.starts_with(" ") | |
| 46 | + && line_text[4..].starts_with(prefix) | |
| 47 | + { | |
| 46 | 48 | start = Some(line_start); |
| 47 | 49 | } |
| 48 | 50 | continue; |
tests/fuzz_smoke.rsmodified@@ -65,15 +65,21 @@ fn fuzz_parser_deterministic(seed: u64, count: usize) { | ||
| 65 | 65 | /// Runs without threads — each input goes directly through the parser. |
| 66 | 66 | fn fuzz_parser_with_fortran_fragments(count: usize) { |
| 67 | 67 | let fragments = [ |
| 68 | - "program p\nend program\n", "module m\nend module\n", | |
| 69 | - "integer :: x\n", "real, allocatable :: a(:,:)\n", | |
| 70 | - "do i = 1, 10\nend do\n", "if (x > 0) then\nend if\n", | |
| 68 | + "program p\nend program\n", | |
| 69 | + "module m\nend module\n", | |
| 70 | + "integer :: x\n", | |
| 71 | + "real, allocatable :: a(:,:)\n", | |
| 72 | + "do i = 1, 10\nend do\n", | |
| 73 | + "if (x > 0) then\nend if\n", | |
| 71 | 74 | "select case (x)\ncase (1)\nend select\n", |
| 72 | 75 | "type :: t\n integer :: f\nend type\n", |
| 73 | 76 | "interface\nend interface\n", |
| 74 | - "goto 100\n", "100 continue\n", | |
| 77 | + "goto 100\n", | |
| 78 | + "100 continue\n", | |
| 75 | 79 | "use iso_c_binding, only: c_int\n", |
| 76 | - "", "!\n", "! comment\n", | |
| 80 | + "", | |
| 81 | + "!\n", | |
| 82 | + "! comment\n", | |
| 77 | 83 | "end\n", |
| 78 | 84 | "do concurrent (i = 1:10)\nend do\n", |
| 79 | 85 | "block\n integer :: local\nend block\n", |
@@ -98,7 +104,9 @@ fn fuzz_parser_with_fortran_fragments(count: usize) { | ||
| 98 | 104 | } |
| 99 | 105 | |
| 100 | 106 | if let Ok(tokens) = lexer::tokenize(&src, 0, SourceForm::FreeForm) { |
| 101 | - if tokens.len() > 100 { continue; } | |
| 107 | + if tokens.len() > 100 { | |
| 108 | + continue; | |
| 109 | + } | |
| 102 | 110 | let mut parser = Parser::new(&tokens); |
| 103 | 111 | let _ = parser.parse_file(); |
| 104 | 112 | } |
tests/gvn_dse_audit_29_11.rsmodified@@ -38,10 +38,7 @@ fn block_section<'a>(func_section: &'a str, prefix: &str) -> &'a str { | ||
| 38 | 38 | for (idx, _line) in func_section.match_indices('\n') { |
| 39 | 39 | let line_start = idx + 1; |
| 40 | 40 | let tail = &func_section[line_start..]; |
| 41 | - let line_text = tail | |
| 42 | - .split_once('\n') | |
| 43 | - .map(|(line, _)| line) | |
| 44 | - .unwrap_or(tail); | |
| 41 | + let line_text = tail.split_once('\n').map(|(line, _)| line).unwrap_or(tail); | |
| 45 | 42 | |
| 46 | 43 | if start.is_none() { |
| 47 | 44 | if line_text.starts_with(" ") |
@@ -74,10 +71,7 @@ fn last_block_section<'a>(func_section: &'a str, prefix: &str) -> &'a str { | ||
| 74 | 71 | for (idx, _line) in func_section.match_indices('\n') { |
| 75 | 72 | let line_start = idx + 1; |
| 76 | 73 | let tail = &func_section[line_start..]; |
| 77 | - let line_text = tail | |
| 78 | - .split_once('\n') | |
| 79 | - .map(|(line, _)| line) | |
| 80 | - .unwrap_or(tail); | |
| 74 | + let line_text = tail.split_once('\n').map(|(line, _)| line).unwrap_or(tail); | |
| 81 | 75 | if line_text.starts_with(" ") |
| 82 | 76 | && !line_text.starts_with(" ") |
| 83 | 77 | && line_text[4..].starts_with(prefix) |
tests/i128_cross_object.rsmodified@@ -1,8 +1,8 @@ | ||
| 1 | 1 | use std::fs; |
| 2 | 2 | use std::path::{Path, PathBuf}; |
| 3 | 3 | use std::process::Command; |
| 4 | -use std::sync::Mutex; | |
| 5 | 4 | use std::sync::atomic::{AtomicUsize, Ordering}; |
| 5 | +use std::sync::Mutex; | |
| 6 | 6 | use std::time::SystemTime; |
| 7 | 7 | |
| 8 | 8 | use armfortas::driver::{compile, OptLevel, Options}; |
@@ -100,7 +100,9 @@ fn maybe_refresh_runtime_lib(workspace_root: &Path) { | ||
| 100 | 100 | return; |
| 101 | 101 | }; |
| 102 | 102 | let debug_archive = workspace_root.join("target/debug/libarmfortas_rt.a"); |
| 103 | - let archive_mtime = fs::metadata(&debug_archive).ok().and_then(|meta| meta.modified().ok()); | |
| 103 | + let archive_mtime = fs::metadata(&debug_archive) | |
| 104 | + .ok() | |
| 105 | + .and_then(|meta| meta.modified().ok()); | |
| 104 | 106 | |
| 105 | 107 | if archive_mtime.is_some_and(|mtime| mtime >= source_mtime) { |
| 106 | 108 | return; |
@@ -208,13 +210,23 @@ fn run_cross_object_case_named( | ||
| 208 | 210 | opt_level: OptLevel, |
| 209 | 211 | expected_score: char, |
| 210 | 212 | ) { |
| 211 | - let _guard = CROSS_OBJECT_LOCK.lock().unwrap_or_else(|poison| poison.into_inner()); | |
| 213 | + let _guard = CROSS_OBJECT_LOCK | |
| 214 | + .lock() | |
| 215 | + .unwrap_or_else(|poison| poison.into_inner()); | |
| 212 | 216 | let program = fixture(program_name); |
| 213 | 217 | let helper = fixture(helper_name); |
| 214 | 218 | let stem = program.file_stem().unwrap().to_str().unwrap(); |
| 215 | - let fortran_obj = unique_temp_path("i128_cross_object", &format!("{}_{}", stem, opt_label(opt_level)), ".o"); | |
| 219 | + let fortran_obj = unique_temp_path( | |
| 220 | + "i128_cross_object", | |
| 221 | + &format!("{}_{}", stem, opt_label(opt_level)), | |
| 222 | + ".o", | |
| 223 | + ); | |
| 216 | 224 | let helper_obj = unique_temp_path("i128_cross_object_helper", stem, ".o"); |
| 217 | - let binary = unique_temp_path("i128_cross_object_bin", &format!("{}_{}", stem, opt_label(opt_level)), ""); | |
| 225 | + let binary = unique_temp_path( | |
| 226 | + "i128_cross_object_bin", | |
| 227 | + &format!("{}_{}", stem, opt_label(opt_level)), | |
| 228 | + "", | |
| 229 | + ); | |
| 218 | 230 | |
| 219 | 231 | compile_fortran_object(&program, &fortran_obj, opt_level); |
| 220 | 232 | compile_c_object(&helper, &helper_obj); |
@@ -249,14 +261,28 @@ fn run_cross_object_case(opt_level: OptLevel) { | ||
| 249 | 261 | ); |
| 250 | 262 | } |
| 251 | 263 | |
| 252 | -fn deterministic_cross_object_case_named(program_name: &str, helper_name: &str, opt_level: OptLevel) { | |
| 253 | - let _guard = CROSS_OBJECT_LOCK.lock().unwrap_or_else(|poison| poison.into_inner()); | |
| 264 | +fn deterministic_cross_object_case_named( | |
| 265 | + program_name: &str, | |
| 266 | + helper_name: &str, | |
| 267 | + opt_level: OptLevel, | |
| 268 | +) { | |
| 269 | + let _guard = CROSS_OBJECT_LOCK | |
| 270 | + .lock() | |
| 271 | + .unwrap_or_else(|poison| poison.into_inner()); | |
| 254 | 272 | let program = fixture(program_name); |
| 255 | 273 | let helper = fixture(helper_name); |
| 256 | 274 | let stem = program.file_stem().unwrap().to_str().unwrap(); |
| 257 | - let fortran_obj = unique_temp_path("i128_cross_object", &format!("{}_{}", stem, opt_label(opt_level)), ".o"); | |
| 275 | + let fortran_obj = unique_temp_path( | |
| 276 | + "i128_cross_object", | |
| 277 | + &format!("{}_{}", stem, opt_label(opt_level)), | |
| 278 | + ".o", | |
| 279 | + ); | |
| 258 | 280 | let helper_obj = unique_temp_path("i128_cross_object_helper", stem, ".o"); |
| 259 | - let binary = unique_temp_path("i128_cross_object_bin", &format!("{}_{}", stem, opt_label(opt_level)), ""); | |
| 281 | + let binary = unique_temp_path( | |
| 282 | + "i128_cross_object_bin", | |
| 283 | + &format!("{}_{}", stem, opt_label(opt_level)), | |
| 284 | + "", | |
| 285 | + ); | |
| 260 | 286 | |
| 261 | 287 | compile_fortran_object(&program, &fortran_obj, opt_level); |
| 262 | 288 | compile_c_object(&helper, &helper_obj); |
tests/i128_formatted_read.rsmodified@@ -75,15 +75,26 @@ fn integer16_formatted_read_runs_across_all_opt_levels() { | ||
| 75 | 75 | requested: BTreeSet::from([Stage::Run]), |
| 76 | 76 | opt_level: level, |
| 77 | 77 | }) |
| 78 | - .unwrap_or_else(|e| panic!("formatted integer(16) input should run at {:?}:\n{}", level, e)); | |
| 78 | + .unwrap_or_else(|e| { | |
| 79 | + panic!( | |
| 80 | + "formatted integer(16) input should run at {:?}:\n{}", | |
| 81 | + level, e | |
| 82 | + ) | |
| 83 | + }); | |
| 79 | 84 | |
| 80 | 85 | let run = result |
| 81 | 86 | .get(Stage::Run) |
| 82 | 87 | .and_then(CapturedStage::as_run) |
| 83 | 88 | .expect("missing run capture"); |
| 84 | 89 | |
| 85 | - assert_eq!(run.exit_code, 0, "expected successful formatted integer(16) read run at {:?}:\n{:#?}", level, run); | |
| 86 | - assert!(run.stdout.contains("170141183460469231731687303715884105727")); | |
| 90 | + assert_eq!( | |
| 91 | + run.exit_code, 0, | |
| 92 | + "expected successful formatted integer(16) read run at {:?}:\n{:#?}", | |
| 93 | + level, run | |
| 94 | + ); | |
| 95 | + assert!(run | |
| 96 | + .stdout | |
| 97 | + .contains("170141183460469231731687303715884105727")); | |
| 87 | 98 | assert!(run.stdout.contains("42")); |
| 88 | 99 | } |
| 89 | 100 | } |
@@ -161,14 +172,23 @@ fn integer16_formatted_read_targets_run_across_all_opt_levels() { | ||
| 161 | 172 | requested: BTreeSet::from([Stage::Run]), |
| 162 | 173 | opt_level: level, |
| 163 | 174 | }) |
| 164 | - .unwrap_or_else(|e| panic!("formatted integer(16) lvalue read should run at {:?}:\n{}", level, e)); | |
| 175 | + .unwrap_or_else(|e| { | |
| 176 | + panic!( | |
| 177 | + "formatted integer(16) lvalue read should run at {:?}:\n{}", | |
| 178 | + level, e | |
| 179 | + ) | |
| 180 | + }); | |
| 165 | 181 | |
| 166 | 182 | let run = result |
| 167 | 183 | .get(Stage::Run) |
| 168 | 184 | .and_then(CapturedStage::as_run) |
| 169 | 185 | .expect("missing run capture"); |
| 170 | 186 | |
| 171 | - assert_eq!(run.exit_code, 0, "expected successful formatted integer(16) lvalue read run at {:?}:\n{:#?}", level, run); | |
| 187 | + assert_eq!( | |
| 188 | + run.exit_code, 0, | |
| 189 | + "expected successful formatted integer(16) lvalue read run at {:?}:\n{:#?}", | |
| 190 | + level, run | |
| 191 | + ); | |
| 172 | 192 | for needle in [ |
| 173 | 193 | "11", |
| 174 | 194 | "-170141183460469231731687303715884105727", |
@@ -255,16 +275,29 @@ fn integer16_formatted_read_arrays_run_across_all_opt_levels() { | ||
| 255 | 275 | requested: BTreeSet::from([Stage::Run]), |
| 256 | 276 | opt_level: level, |
| 257 | 277 | }) |
| 258 | - .unwrap_or_else(|e| panic!("formatted integer(16) array reads should run at {:?}:\n{}", level, e)); | |
| 278 | + .unwrap_or_else(|e| { | |
| 279 | + panic!( | |
| 280 | + "formatted integer(16) array reads should run at {:?}:\n{}", | |
| 281 | + level, e | |
| 282 | + ) | |
| 283 | + }); | |
| 259 | 284 | |
| 260 | 285 | let run = result |
| 261 | 286 | .get(Stage::Run) |
| 262 | 287 | .and_then(CapturedStage::as_run) |
| 263 | 288 | .expect("missing run capture"); |
| 264 | 289 | |
| 265 | - assert_eq!(run.exit_code, 0, "expected successful formatted integer(16) array read run at {:?}:\n{:#?}", level, run); | |
| 266 | - assert!(run.stdout.contains("11 170141183460469231731687303715884105727 33")); | |
| 267 | - assert!(run.stdout.contains("66 -170141183460469231731687303715884105727 44")); | |
| 290 | + assert_eq!( | |
| 291 | + run.exit_code, 0, | |
| 292 | + "expected successful formatted integer(16) array read run at {:?}:\n{:#?}", | |
| 293 | + level, run | |
| 294 | + ); | |
| 295 | + assert!(run | |
| 296 | + .stdout | |
| 297 | + .contains("11 170141183460469231731687303715884105727 33")); | |
| 298 | + assert!(run | |
| 299 | + .stdout | |
| 300 | + .contains("66 -170141183460469231731687303715884105727 44")); | |
| 268 | 301 | } |
| 269 | 302 | } |
| 270 | 303 | |
@@ -335,14 +368,23 @@ fn integer16_formatted_read_sections_run_across_all_opt_levels() { | ||
| 335 | 368 | requested: BTreeSet::from([Stage::Run]), |
| 336 | 369 | opt_level: level, |
| 337 | 370 | }) |
| 338 | - .unwrap_or_else(|e| panic!("formatted integer(16) section reads should run at {:?}:\n{}", level, e)); | |
| 371 | + .unwrap_or_else(|e| { | |
| 372 | + panic!( | |
| 373 | + "formatted integer(16) section reads should run at {:?}:\n{}", | |
| 374 | + level, e | |
| 375 | + ) | |
| 376 | + }); | |
| 339 | 377 | |
| 340 | 378 | let run = result |
| 341 | 379 | .get(Stage::Run) |
| 342 | 380 | .and_then(CapturedStage::as_run) |
| 343 | 381 | .expect("missing run capture"); |
| 344 | 382 | |
| 345 | - assert_eq!(run.exit_code, 0, "expected successful formatted integer(16) section read run at {:?}:\n{:#?}", level, run); | |
| 383 | + assert_eq!( | |
| 384 | + run.exit_code, 0, | |
| 385 | + "expected successful formatted integer(16) section read run at {:?}:\n{:#?}", | |
| 386 | + level, run | |
| 387 | + ); | |
| 346 | 388 | assert!(run.stdout.contains("101 202")); |
| 347 | 389 | assert!(run.stdout.contains("606 505 404 303")); |
| 348 | 390 | } |
@@ -459,14 +501,23 @@ fn integer16_formatted_read_alloc_reverse_section_runs_across_all_opt_levels() { | ||
| 459 | 501 | requested: BTreeSet::from([Stage::Run]), |
| 460 | 502 | opt_level: level, |
| 461 | 503 | }) |
| 462 | - .unwrap_or_else(|e| panic!("allocatable formatted integer(16) reverse section read should run at {:?}:\n{}", level, e)); | |
| 504 | + .unwrap_or_else(|e| { | |
| 505 | + panic!( | |
| 506 | + "allocatable formatted integer(16) reverse section read should run at {:?}:\n{}", | |
| 507 | + level, e | |
| 508 | + ) | |
| 509 | + }); | |
| 463 | 510 | |
| 464 | 511 | let run = result |
| 465 | 512 | .get(Stage::Run) |
| 466 | 513 | .and_then(CapturedStage::as_run) |
| 467 | 514 | .expect("missing run capture"); |
| 468 | 515 | |
| 469 | - assert_eq!(run.exit_code, 0, "expected successful allocatable reverse section read run at {:?}:\n{:#?}", level, run); | |
| 516 | + assert_eq!( | |
| 517 | + run.exit_code, 0, | |
| 518 | + "expected successful allocatable reverse section read run at {:?}:\n{:#?}", | |
| 519 | + level, run | |
| 520 | + ); | |
| 470 | 521 | assert!(run.stdout.contains("8")); |
| 471 | 522 | } |
| 472 | 523 | } |
tests/i128_gates.rsmodified@@ -12,13 +12,24 @@ fn fixture(name: &str) -> PathBuf { | ||
| 12 | 12 | |
| 13 | 13 | #[test] |
| 14 | 14 | fn optimized_i128_capture_is_available_through_ofast() { |
| 15 | - for level in [OptLevel::O1, OptLevel::O2, OptLevel::O3, OptLevel::Os, OptLevel::Ofast] { | |
| 15 | + for level in [ | |
| 16 | + OptLevel::O1, | |
| 17 | + OptLevel::O2, | |
| 18 | + OptLevel::O3, | |
| 19 | + OptLevel::Os, | |
| 20 | + OptLevel::Ofast, | |
| 21 | + ] { | |
| 16 | 22 | capture_from_path(&CaptureRequest { |
| 17 | 23 | input: fixture("integer16_mul.f90"), |
| 18 | 24 | requested: BTreeSet::from([Stage::OptIr]), |
| 19 | 25 | opt_level: level, |
| 20 | 26 | }) |
| 21 | - .unwrap_or_else(|e| panic!("optimized i128 capture should succeed at {:?}:\n{}", level, e)); | |
| 27 | + .unwrap_or_else(|e| { | |
| 28 | + panic!( | |
| 29 | + "optimized i128 capture should succeed at {:?}:\n{}", | |
| 30 | + level, e | |
| 31 | + ) | |
| 32 | + }); | |
| 22 | 33 | } |
| 23 | 34 | } |
| 24 | 35 | |
@@ -33,7 +44,8 @@ fn backend_i128_capture_is_rejected_for_now() { | ||
| 33 | 44 | |
| 34 | 45 | assert_eq!(err.stage, FailureStage::Ir); |
| 35 | 46 | assert!( |
| 36 | - err.detail.contains("backend does not yet support integer(16) / i128 codegen"), | |
| 47 | + err.detail | |
| 48 | + .contains("backend does not yet support integer(16) / i128 codegen"), | |
| 37 | 49 | "unexpected capture failure:\n{}", |
| 38 | 50 | err |
| 39 | 51 | ); |
tests/i128_high_opt.rsmodified@@ -121,7 +121,11 @@ fn high_opt_backend_runs_internal_integer16_call() { | ||
| 121 | 121 | .and_then(CapturedStage::as_run) |
| 122 | 122 | .expect("missing run capture"); |
| 123 | 123 | |
| 124 | - assert_eq!(run.exit_code, 0, "expected successful {} integer(16) run:\n{:#?}", label, run); | |
| 124 | + assert_eq!( | |
| 125 | + run.exit_code, 0, | |
| 126 | + "expected successful {} integer(16) run:\n{:#?}", | |
| 127 | + label, run | |
| 128 | + ); | |
| 125 | 129 | assert!( |
| 126 | 130 | run.stdout.contains('1'), |
| 127 | 131 | "{} integer(16) internal call program should print score 1:\n{}", |
tests/i128_internal_io.rsmodified@@ -82,9 +82,17 @@ fn integer16_internal_io_runs_across_all_opt_levels() { | ||
| 82 | 82 | .and_then(CapturedStage::as_run) |
| 83 | 83 | .expect("missing run capture"); |
| 84 | 84 | |
| 85 | - assert_eq!(run.exit_code, 0, "expected successful internal integer(16) I/O run at {:?}:\n{:#?}", level, run); | |
| 86 | - assert!(run.stdout.contains("170141183460469231731687303715884105727")); | |
| 87 | - assert!(run.stdout.contains("-170141183460469231731687303715884105727")); | |
| 85 | + assert_eq!( | |
| 86 | + run.exit_code, 0, | |
| 87 | + "expected successful internal integer(16) I/O run at {:?}:\n{:#?}", | |
| 88 | + level, run | |
| 89 | + ); | |
| 90 | + assert!(run | |
| 91 | + .stdout | |
| 92 | + .contains("170141183460469231731687303715884105727")); | |
| 93 | + assert!(run | |
| 94 | + .stdout | |
| 95 | + .contains("-170141183460469231731687303715884105727")); | |
| 88 | 96 | } |
| 89 | 97 | } |
| 90 | 98 | |
@@ -152,15 +160,26 @@ fn integer16_internal_format_runs_across_all_opt_levels() { | ||
| 152 | 160 | requested: BTreeSet::from([Stage::Run]), |
| 153 | 161 | opt_level: level, |
| 154 | 162 | }) |
| 155 | - .unwrap_or_else(|e| panic!("formatted internal integer(16) write should run at {:?}:\n{}", level, e)); | |
| 163 | + .unwrap_or_else(|e| { | |
| 164 | + panic!( | |
| 165 | + "formatted internal integer(16) write should run at {:?}:\n{}", | |
| 166 | + level, e | |
| 167 | + ) | |
| 168 | + }); | |
| 156 | 169 | |
| 157 | 170 | let run = result |
| 158 | 171 | .get(Stage::Run) |
| 159 | 172 | .and_then(CapturedStage::as_run) |
| 160 | 173 | .expect("missing run capture"); |
| 161 | 174 | |
| 162 | - assert_eq!(run.exit_code, 0, "expected successful formatted internal integer(16) write run at {:?}:\n{:#?}", level, run); | |
| 163 | - assert!(run.stdout.contains("170141183460469231731687303715884105727")); | |
| 175 | + assert_eq!( | |
| 176 | + run.exit_code, 0, | |
| 177 | + "expected successful formatted internal integer(16) write run at {:?}:\n{:#?}", | |
| 178 | + level, run | |
| 179 | + ); | |
| 180 | + assert!(run | |
| 181 | + .stdout | |
| 182 | + .contains("170141183460469231731687303715884105727")); | |
| 164 | 183 | } |
| 165 | 184 | } |
| 166 | 185 | |
@@ -226,15 +245,26 @@ fn integer16_internal_format_read_runs_across_all_opt_levels() { | ||
| 226 | 245 | requested: BTreeSet::from([Stage::Run]), |
| 227 | 246 | opt_level: level, |
| 228 | 247 | }) |
| 229 | - .unwrap_or_else(|e| panic!("formatted internal integer(16) read should run at {:?}:\n{}", level, e)); | |
| 248 | + .unwrap_or_else(|e| { | |
| 249 | + panic!( | |
| 250 | + "formatted internal integer(16) read should run at {:?}:\n{}", | |
| 251 | + level, e | |
| 252 | + ) | |
| 253 | + }); | |
| 230 | 254 | |
| 231 | 255 | let run = result |
| 232 | 256 | .get(Stage::Run) |
| 233 | 257 | .and_then(CapturedStage::as_run) |
| 234 | 258 | .expect("missing run capture"); |
| 235 | 259 | |
| 236 | - assert_eq!(run.exit_code, 0, "expected successful formatted internal integer(16) read run at {:?}:\n{:#?}", level, run); | |
| 237 | - assert!(run.stdout.contains("170141183460469231731687303715884105727")); | |
| 260 | + assert_eq!( | |
| 261 | + run.exit_code, 0, | |
| 262 | + "expected successful formatted internal integer(16) read run at {:?}:\n{:#?}", | |
| 263 | + level, run | |
| 264 | + ); | |
| 265 | + assert!(run | |
| 266 | + .stdout | |
| 267 | + .contains("170141183460469231731687303715884105727")); | |
| 238 | 268 | } |
| 239 | 269 | } |
| 240 | 270 | |
@@ -302,14 +332,23 @@ fn integer16_internal_format_read_targets_run_across_all_opt_levels() { | ||
| 302 | 332 | requested: BTreeSet::from([Stage::Run]), |
| 303 | 333 | opt_level: level, |
| 304 | 334 | }) |
| 305 | - .unwrap_or_else(|e| panic!("formatted internal integer(16) lvalue read should run at {:?}:\n{}", level, e)); | |
| 335 | + .unwrap_or_else(|e| { | |
| 336 | + panic!( | |
| 337 | + "formatted internal integer(16) lvalue read should run at {:?}:\n{}", | |
| 338 | + level, e | |
| 339 | + ) | |
| 340 | + }); | |
| 306 | 341 | |
| 307 | 342 | let run = result |
| 308 | 343 | .get(Stage::Run) |
| 309 | 344 | .and_then(CapturedStage::as_run) |
| 310 | 345 | .expect("missing run capture"); |
| 311 | 346 | |
| 312 | - assert_eq!(run.exit_code, 0, "expected successful formatted internal integer(16) lvalue read run at {:?}:\n{:#?}", level, run); | |
| 347 | + assert_eq!( | |
| 348 | + run.exit_code, 0, | |
| 349 | + "expected successful formatted internal integer(16) lvalue read run at {:?}:\n{:#?}", | |
| 350 | + level, run | |
| 351 | + ); | |
| 313 | 352 | for needle in [ |
| 314 | 353 | "11", |
| 315 | 354 | "-170141183460469231731687303715884105727", |
@@ -391,16 +430,29 @@ fn integer16_internal_format_read_arrays_run_across_all_opt_levels() { | ||
| 391 | 430 | requested: BTreeSet::from([Stage::Run]), |
| 392 | 431 | opt_level: level, |
| 393 | 432 | }) |
| 394 | - .unwrap_or_else(|e| panic!("formatted internal integer(16) array reads should run at {:?}:\n{}", level, e)); | |
| 433 | + .unwrap_or_else(|e| { | |
| 434 | + panic!( | |
| 435 | + "formatted internal integer(16) array reads should run at {:?}:\n{}", | |
| 436 | + level, e | |
| 437 | + ) | |
| 438 | + }); | |
| 395 | 439 | |
| 396 | 440 | let run = result |
| 397 | 441 | .get(Stage::Run) |
| 398 | 442 | .and_then(CapturedStage::as_run) |
| 399 | 443 | .expect("missing run capture"); |
| 400 | 444 | |
| 401 | - assert_eq!(run.exit_code, 0, "expected successful formatted internal integer(16) array read run at {:?}:\n{:#?}", level, run); | |
| 402 | - assert!(run.stdout.contains("11 170141183460469231731687303715884105727 33")); | |
| 403 | - assert!(run.stdout.contains("66 -170141183460469231731687303715884105727 44")); | |
| 445 | + assert_eq!( | |
| 446 | + run.exit_code, 0, | |
| 447 | + "expected successful formatted internal integer(16) array read run at {:?}:\n{:#?}", | |
| 448 | + level, run | |
| 449 | + ); | |
| 450 | + assert!(run | |
| 451 | + .stdout | |
| 452 | + .contains("11 170141183460469231731687303715884105727 33")); | |
| 453 | + assert!(run | |
| 454 | + .stdout | |
| 455 | + .contains("66 -170141183460469231731687303715884105727 44")); | |
| 404 | 456 | } |
| 405 | 457 | } |
| 406 | 458 | |
@@ -466,14 +518,23 @@ fn integer16_internal_format_read_sections_run_across_all_opt_levels() { | ||
| 466 | 518 | requested: BTreeSet::from([Stage::Run]), |
| 467 | 519 | opt_level: level, |
| 468 | 520 | }) |
| 469 | - .unwrap_or_else(|e| panic!("formatted internal integer(16) section reads should run at {:?}:\n{}", level, e)); | |
| 521 | + .unwrap_or_else(|e| { | |
| 522 | + panic!( | |
| 523 | + "formatted internal integer(16) section reads should run at {:?}:\n{}", | |
| 524 | + level, e | |
| 525 | + ) | |
| 526 | + }); | |
| 470 | 527 | |
| 471 | 528 | let run = result |
| 472 | 529 | .get(Stage::Run) |
| 473 | 530 | .and_then(CapturedStage::as_run) |
| 474 | 531 | .expect("missing run capture"); |
| 475 | 532 | |
| 476 | - assert_eq!(run.exit_code, 0, "expected successful formatted internal integer(16) section read run at {:?}:\n{:#?}", level, run); | |
| 533 | + assert_eq!( | |
| 534 | + run.exit_code, 0, | |
| 535 | + "expected successful formatted internal integer(16) section read run at {:?}:\n{:#?}", | |
| 536 | + level, run | |
| 537 | + ); | |
| 477 | 538 | assert!(run.stdout.contains("101 202")); |
| 478 | 539 | assert!(run.stdout.contains("606 505 404 303")); |
| 479 | 540 | } |
@@ -541,14 +602,23 @@ fn integer16_internal_format_read_alloc_section_runs_across_all_opt_levels() { | ||
| 541 | 602 | requested: BTreeSet::from([Stage::Run]), |
| 542 | 603 | opt_level: level, |
| 543 | 604 | }) |
| 544 | - .unwrap_or_else(|e| panic!("allocatable internal formatted integer(16) section read should run at {:?}:\n{}", level, e)); | |
| 605 | + .unwrap_or_else(|e| { | |
| 606 | + panic!( | |
| 607 | + "allocatable internal formatted integer(16) section read should run at {:?}:\n{}", | |
| 608 | + level, e | |
| 609 | + ) | |
| 610 | + }); | |
| 545 | 611 | |
| 546 | 612 | let run = result |
| 547 | 613 | .get(Stage::Run) |
| 548 | 614 | .and_then(CapturedStage::as_run) |
| 549 | 615 | .expect("missing run capture"); |
| 550 | 616 | |
| 551 | - assert_eq!(run.exit_code, 0, "expected successful allocatable internal section read run at {:?}:\n{:#?}", level, run); | |
| 617 | + assert_eq!( | |
| 618 | + run.exit_code, 0, | |
| 619 | + "expected successful allocatable internal section read run at {:?}:\n{:#?}", | |
| 620 | + level, run | |
| 621 | + ); | |
| 552 | 622 | assert!(run.stdout.contains("11")); |
| 553 | 623 | } |
| 554 | 624 | } |
@@ -621,7 +691,11 @@ fn integer16_internal_format_read_alloc_reverse_section_runs_across_all_opt_leve | ||
| 621 | 691 | .and_then(CapturedStage::as_run) |
| 622 | 692 | .expect("missing run capture"); |
| 623 | 693 | |
| 624 | - assert_eq!(run.exit_code, 0, "expected successful allocatable internal reverse section read run at {:?}:\n{:#?}", level, run); | |
| 694 | + assert_eq!( | |
| 695 | + run.exit_code, 0, | |
| 696 | + "expected successful allocatable internal reverse section read run at {:?}:\n{:#?}", | |
| 697 | + level, run | |
| 698 | + ); | |
| 625 | 699 | assert!(run.stdout.contains("8")); |
| 626 | 700 | } |
| 627 | 701 | } |
tests/i128_memory_backend.rsmodified@@ -203,7 +203,11 @@ fn simple_local_i128_ordered_branch_runs_at_o0() { | ||
| 203 | 203 | .and_then(CapturedStage::as_run) |
| 204 | 204 | .expect("missing run capture"); |
| 205 | 205 | |
| 206 | - assert_eq!(run.exit_code, 0, "expected successful ordered branch run:\n{:#?}", run); | |
| 206 | + assert_eq!( | |
| 207 | + run.exit_code, 0, | |
| 208 | + "expected successful ordered branch run:\n{:#?}", | |
| 209 | + run | |
| 210 | + ); | |
| 207 | 211 | assert!( |
| 208 | 212 | run.stdout.contains('4'), |
| 209 | 213 | "ordered i128 branch program should print score 4:\n{}", |
@@ -257,7 +261,11 @@ fn simple_local_i128_select_runs_at_o0() { | ||
| 257 | 261 | .and_then(CapturedStage::as_run) |
| 258 | 262 | .expect("missing run capture"); |
| 259 | 263 | |
| 260 | - assert_eq!(run.exit_code, 0, "expected successful integer(16) select run:\n{:#?}", run); | |
| 264 | + assert_eq!( | |
| 265 | + run.exit_code, 0, | |
| 266 | + "expected successful integer(16) select run:\n{:#?}", | |
| 267 | + run | |
| 268 | + ); | |
| 261 | 269 | assert!( |
| 262 | 270 | run.stdout.contains('1'), |
| 263 | 271 | "integer(16) select program should print score 1:\n{}", |
@@ -333,7 +341,11 @@ fn simple_internal_i128_call_runs_at_o0() { | ||
| 333 | 341 | .and_then(CapturedStage::as_run) |
| 334 | 342 | .expect("missing run capture"); |
| 335 | 343 | |
| 336 | - assert_eq!(run.exit_code, 0, "expected successful internal integer(16) call run:\n{:#?}", run); | |
| 344 | + assert_eq!( | |
| 345 | + run.exit_code, 0, | |
| 346 | + "expected successful internal integer(16) call run:\n{:#?}", | |
| 347 | + run | |
| 348 | + ); | |
| 337 | 349 | assert!( |
| 338 | 350 | run.stdout.contains('1'), |
| 339 | 351 | "internal integer(16) call program should print score 1:\n{}", |
tests/i128_o1.rsmodified@@ -69,7 +69,11 @@ fn o1_backend_runs_internal_integer16_call() { | ||
| 69 | 69 | .and_then(CapturedStage::as_run) |
| 70 | 70 | .expect("missing run capture"); |
| 71 | 71 | |
| 72 | - assert_eq!(run.exit_code, 0, "expected successful O1 integer(16) run:\n{:#?}", run); | |
| 72 | + assert_eq!( | |
| 73 | + run.exit_code, 0, | |
| 74 | + "expected successful O1 integer(16) run:\n{:#?}", | |
| 75 | + run | |
| 76 | + ); | |
| 73 | 77 | assert!( |
| 74 | 78 | run.stdout.contains('1'), |
| 75 | 79 | "O1 integer(16) internal call program should print score 1:\n{}", |
@@ -158,7 +162,11 @@ fn o1_branchy_integer16_program_runs_after_mem2reg() { | ||
| 158 | 162 | .and_then(CapturedStage::as_run) |
| 159 | 163 | .expect("missing run capture"); |
| 160 | 164 | |
| 161 | - assert_eq!(run.exit_code, 0, "expected successful O1 branchy integer(16) run:\n{:#?}", run); | |
| 165 | + assert_eq!( | |
| 166 | + run.exit_code, 0, | |
| 167 | + "expected successful O1 branchy integer(16) run:\n{:#?}", | |
| 168 | + run | |
| 169 | + ); | |
| 162 | 170 | assert!( |
| 163 | 171 | run.stdout.contains('1'), |
| 164 | 172 | "branchy O1 integer(16) program should print score 1:\n{}", |
tests/i128_o2.rsmodified@@ -105,7 +105,11 @@ fn o2_backend_runs_internal_integer16_call() { | ||
| 105 | 105 | .and_then(CapturedStage::as_run) |
| 106 | 106 | .expect("missing run capture"); |
| 107 | 107 | |
| 108 | - assert_eq!(run.exit_code, 0, "expected successful O2 integer(16) run:\n{:#?}", run); | |
| 108 | + assert_eq!( | |
| 109 | + run.exit_code, 0, | |
| 110 | + "expected successful O2 integer(16) run:\n{:#?}", | |
| 111 | + run | |
| 112 | + ); | |
| 109 | 113 | assert!( |
| 110 | 114 | run.stdout.contains('1'), |
| 111 | 115 | "O2 integer(16) internal call program should print score 1:\n{}", |
tests/i128_runtime_io.rsmodified@@ -74,15 +74,21 @@ fn integer16_print_runs_across_all_opt_levels() { | ||
| 74 | 74 | .and_then(CapturedStage::as_run) |
| 75 | 75 | .expect("missing run capture"); |
| 76 | 76 | |
| 77 | - assert_eq!(run.exit_code, 0, "expected successful integer(16) print run at {:?}:\n{:#?}", level, run); | |
| 77 | + assert_eq!( | |
| 78 | + run.exit_code, 0, | |
| 79 | + "expected successful integer(16) print run at {:?}:\n{:#?}", | |
| 80 | + level, run | |
| 81 | + ); | |
| 78 | 82 | assert!( |
| 79 | - run.stdout.contains("170141183460469231731687303715884105727"), | |
| 83 | + run.stdout | |
| 84 | + .contains("170141183460469231731687303715884105727"), | |
| 80 | 85 | "wide positive integer(16) print should survive at {:?}:\n{}", |
| 81 | 86 | level, |
| 82 | 87 | run.stdout |
| 83 | 88 | ); |
| 84 | 89 | assert!( |
| 85 | - run.stdout.contains("-170141183460469231731687303715884105727"), | |
| 90 | + run.stdout | |
| 91 | + .contains("-170141183460469231731687303715884105727"), | |
| 86 | 92 | "wide negative integer(16) print should survive at {:?}:\n{}", |
| 87 | 93 | level, |
| 88 | 94 | run.stdout |
@@ -164,22 +170,33 @@ fn integer16_formatted_write_runs_across_all_opt_levels() { | ||
| 164 | 170 | requested: BTreeSet::from([Stage::Run]), |
| 165 | 171 | opt_level: level, |
| 166 | 172 | }) |
| 167 | - .unwrap_or_else(|e| panic!("formatted integer(16) write should run at {:?}:\n{}", level, e)); | |
| 173 | + .unwrap_or_else(|e| { | |
| 174 | + panic!( | |
| 175 | + "formatted integer(16) write should run at {:?}:\n{}", | |
| 176 | + level, e | |
| 177 | + ) | |
| 178 | + }); | |
| 168 | 179 | |
| 169 | 180 | let run = result |
| 170 | 181 | .get(Stage::Run) |
| 171 | 182 | .and_then(CapturedStage::as_run) |
| 172 | 183 | .expect("missing run capture"); |
| 173 | 184 | |
| 174 | - assert_eq!(run.exit_code, 0, "expected successful formatted integer(16) write run at {:?}:\n{:#?}", level, run); | |
| 185 | + assert_eq!( | |
| 186 | + run.exit_code, 0, | |
| 187 | + "expected successful formatted integer(16) write run at {:?}:\n{:#?}", | |
| 188 | + level, run | |
| 189 | + ); | |
| 175 | 190 | assert!( |
| 176 | - run.stdout.contains("170141183460469231731687303715884105727"), | |
| 191 | + run.stdout | |
| 192 | + .contains("170141183460469231731687303715884105727"), | |
| 177 | 193 | "wide positive formatted integer(16) output should survive at {:?}:\n{}", |
| 178 | 194 | level, |
| 179 | 195 | run.stdout |
| 180 | 196 | ); |
| 181 | 197 | assert!( |
| 182 | - run.stdout.contains("-170141183460469231731687303715884105727"), | |
| 198 | + run.stdout | |
| 199 | + .contains("-170141183460469231731687303715884105727"), | |
| 183 | 200 | "wide negative formatted integer(16) output should survive at {:?}:\n{}", |
| 184 | 201 | level, |
| 185 | 202 | run.stdout |
tests/i128_runtime_read.rsmodified@@ -74,15 +74,21 @@ fn integer16_read_runs_across_all_opt_levels() { | ||
| 74 | 74 | .and_then(CapturedStage::as_run) |
| 75 | 75 | .expect("missing run capture"); |
| 76 | 76 | |
| 77 | - assert_eq!(run.exit_code, 0, "expected successful integer(16) read run at {:?}:\n{:#?}", level, run); | |
| 77 | + assert_eq!( | |
| 78 | + run.exit_code, 0, | |
| 79 | + "expected successful integer(16) read run at {:?}:\n{:#?}", | |
| 80 | + level, run | |
| 81 | + ); | |
| 78 | 82 | assert!( |
| 79 | - run.stdout.contains("170141183460469231731687303715884105727"), | |
| 83 | + run.stdout | |
| 84 | + .contains("170141183460469231731687303715884105727"), | |
| 80 | 85 | "wide positive integer(16) read should survive at {:?}:\n{}", |
| 81 | 86 | level, |
| 82 | 87 | run.stdout |
| 83 | 88 | ); |
| 84 | 89 | assert!( |
| 85 | - run.stdout.contains("-170141183460469231731687303715884105727"), | |
| 90 | + run.stdout | |
| 91 | + .contains("-170141183460469231731687303715884105727"), | |
| 86 | 92 | "wide negative integer(16) read should survive at {:?}:\n{}", |
| 87 | 93 | level, |
| 88 | 94 | run.stdout |
tests/i128_stack_args.rsmodified@@ -66,7 +66,11 @@ fn internal_i128_stack_call_runs_at_o0() { | ||
| 66 | 66 | .and_then(CapturedStage::as_run) |
| 67 | 67 | .expect("missing run capture"); |
| 68 | 68 | |
| 69 | - assert_eq!(run.exit_code, 0, "expected successful integer(16) stack-call run:\n{:#?}", run); | |
| 69 | + assert_eq!( | |
| 70 | + run.exit_code, 0, | |
| 71 | + "expected successful integer(16) stack-call run:\n{:#?}", | |
| 72 | + run | |
| 73 | + ); | |
| 70 | 74 | assert!( |
| 71 | 75 | run.stdout.contains('1'), |
| 72 | 76 | "internal integer(16) stack-call program should print score 1:\n{}", |
@@ -76,20 +80,35 @@ fn internal_i128_stack_call_runs_at_o0() { | ||
| 76 | 80 | |
| 77 | 81 | #[test] |
| 78 | 82 | fn internal_i128_stack_call_runs_through_optimized_levels() { |
| 79 | - for level in [OptLevel::O1, OptLevel::O2, OptLevel::O3, OptLevel::Os, OptLevel::Ofast] { | |
| 83 | + for level in [ | |
| 84 | + OptLevel::O1, | |
| 85 | + OptLevel::O2, | |
| 86 | + OptLevel::O3, | |
| 87 | + OptLevel::Os, | |
| 88 | + OptLevel::Ofast, | |
| 89 | + ] { | |
| 80 | 90 | let result = capture_from_path(&CaptureRequest { |
| 81 | 91 | input: fixture("integer16_internal_stack_call.f90"), |
| 82 | 92 | requested: BTreeSet::from([Stage::Run]), |
| 83 | 93 | opt_level: level, |
| 84 | 94 | }) |
| 85 | - .unwrap_or_else(|e| panic!("optimized integer(16) stack-call should run at {:?}:\n{}", level, e)); | |
| 95 | + .unwrap_or_else(|e| { | |
| 96 | + panic!( | |
| 97 | + "optimized integer(16) stack-call should run at {:?}:\n{}", | |
| 98 | + level, e | |
| 99 | + ) | |
| 100 | + }); | |
| 86 | 101 | |
| 87 | 102 | let run = result |
| 88 | 103 | .get(Stage::Run) |
| 89 | 104 | .and_then(CapturedStage::as_run) |
| 90 | 105 | .expect("missing run capture"); |
| 91 | 106 | |
| 92 | - assert_eq!(run.exit_code, 0, "expected successful integer(16) stack-call run at {:?}:\n{:#?}", level, run); | |
| 107 | + assert_eq!( | |
| 108 | + run.exit_code, 0, | |
| 109 | + "expected successful integer(16) stack-call run at {:?}:\n{:#?}", | |
| 110 | + level, run | |
| 111 | + ); | |
| 93 | 112 | assert!( |
| 94 | 113 | run.stdout.contains('1'), |
| 95 | 114 | "integer(16) stack-call program should print score 1 at {:?}:\n{}", |
tests/incremental.rsmodified@@ -18,9 +18,7 @@ static NEXT_ID: AtomicU64 = AtomicU64::new(0); | ||
| 18 | 18 | |
| 19 | 19 | fn unique_dir() -> PathBuf { |
| 20 | 20 | let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); |
| 21 | - let dir = std::env::temp_dir().join(format!( | |
| 22 | - "afs_incr_{}_{}", std::process::id(), id | |
| 23 | - )); | |
| 21 | + let dir = std::env::temp_dir().join(format!("afs_incr_{}_{}", std::process::id(), id)); | |
| 24 | 22 | fs::create_dir_all(&dir).unwrap(); |
| 25 | 23 | dir |
| 26 | 24 | } |
@@ -28,7 +26,9 @@ fn unique_dir() -> PathBuf { | ||
| 28 | 26 | fn find_compiler() -> PathBuf { |
| 29 | 27 | for c in &["target/release/armfortas", "target/debug/armfortas"] { |
| 30 | 28 | let p = PathBuf::from(c); |
| 31 | - if p.exists() { return fs::canonicalize(&p).unwrap(); } | |
| 29 | + if p.exists() { | |
| 30 | + return fs::canonicalize(&p).unwrap(); | |
| 31 | + } | |
| 32 | 32 | } |
| 33 | 33 | panic!("armfortas binary not found"); |
| 34 | 34 | } |
@@ -36,8 +36,11 @@ fn find_compiler() -> PathBuf { | ||
| 36 | 36 | fn compile(compiler: &Path, source: &Path, obj: &Path, search: &Path) { |
| 37 | 37 | let result = Command::new(compiler) |
| 38 | 38 | .args([ |
| 39 | - source.to_str().unwrap(), "-c", "-O0", | |
| 40 | - "-o", obj.to_str().unwrap(), | |
| 39 | + source.to_str().unwrap(), | |
| 40 | + "-c", | |
| 41 | + "-O0", | |
| 42 | + "-o", | |
| 43 | + obj.to_str().unwrap(), | |
| 41 | 44 | &format!("-I{}", search.display()), |
| 42 | 45 | ]) |
| 43 | 46 | .output() |
@@ -96,7 +99,10 @@ fn changed_public_interface_changes_amod() { | ||
| 96 | 99 | let amod1 = compile_module(&compiler, &dir, "m", v1); |
| 97 | 100 | let amod2 = compile_module(&compiler, &dir, "m", v2); |
| 98 | 101 | |
| 99 | - assert_ne!(amod1, amod2, ".amod should differ when public interface changes"); | |
| 102 | + assert_ne!( | |
| 103 | + amod1, amod2, | |
| 104 | + ".amod should differ when public interface changes" | |
| 105 | + ); | |
| 100 | 106 | let _ = fs::remove_dir_all(&dir); |
| 101 | 107 | } |
| 102 | 108 | |
@@ -134,7 +140,10 @@ end module | ||
| 134 | 140 | // header from the body) should be identical. |
| 135 | 141 | let body1 = extract_amod_body(&amod1); |
| 136 | 142 | let body2 = extract_amod_body(&amod2); |
| 137 | - assert_eq!(body1, body2, ".amod interface body changed but only private impl differs"); | |
| 143 | + assert_eq!( | |
| 144 | + body1, body2, | |
| 145 | + ".amod interface body changed but only private impl differs" | |
| 146 | + ); | |
| 138 | 147 | let _ = fs::remove_dir_all(&dir); |
| 139 | 148 | } |
| 140 | 149 | |
@@ -160,7 +169,10 @@ fn consumer_object_stable_when_amod_unchanged() { | ||
| 160 | 169 | compile(&compiler, &main_f90, &main_o, &dir); |
| 161 | 170 | let obj2 = fs::read(&main_o).unwrap(); |
| 162 | 171 | |
| 163 | - assert_eq!(obj1, obj2, "consumer .o changed despite no source/amod change"); | |
| 172 | + assert_eq!( | |
| 173 | + obj1, obj2, | |
| 174 | + "consumer .o changed despite no source/amod change" | |
| 175 | + ); | |
| 164 | 176 | let _ = fs::remove_dir_all(&dir); |
| 165 | 177 | } |
| 166 | 178 | |
@@ -170,7 +182,8 @@ fn consumer_object_changes_when_amod_changes() { | ||
| 170 | 182 | let dir = unique_dir(); |
| 171 | 183 | |
| 172 | 184 | let mod_v1 = "module m\n implicit none\n integer :: x = 42\nend module\n"; |
| 173 | - let mod_v2 = "module m\n implicit none\n integer :: x = 42\n integer :: y = 99\nend module\n"; | |
| 185 | + let mod_v2 = | |
| 186 | + "module m\n implicit none\n integer :: x = 42\n integer :: y = 99\nend module\n"; | |
| 174 | 187 | let main_src = "program p\n use m\n implicit none\n print *, x\nend program\n"; |
| 175 | 188 | |
| 176 | 189 | // Compile module v1 and consumer. |
tests/ipo_const_arg.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -49,13 +51,12 @@ fn call_arg_counts_for(func_section: &str, callee_marker: &str) -> Vec<usize> { | ||
| 49 | 51 | .filter_map(|line| { |
| 50 | 52 | let line = line.trim(); |
| 51 | 53 | let call = line.find(&format!("call {}", callee_marker))?; |
| 52 | - let inside = line[call..] | |
| 53 | - .split_once('(')? | |
| 54 | - .1 | |
| 55 | - .split_once(')')? | |
| 56 | - .0 | |
| 57 | - .trim(); | |
| 58 | - Some(if inside.is_empty() { 0 } else { inside.split(", ").count() }) | |
| 54 | + let inside = line[call..].split_once('(')?.1.split_once(')')?.0.trim(); | |
| 55 | + Some(if inside.is_empty() { | |
| 56 | + 0 | |
| 57 | + } else { | |
| 58 | + inside.split(", ").count() | |
| 59 | + }) | |
| 59 | 60 | }) |
| 60 | 61 | .collect() |
| 61 | 62 | } |
@@ -102,8 +103,18 @@ fn o2_specializes_constant_dummy_and_trims_internal_calls() { | ||
| 102 | 103 | let opt_main = function_section(&opt_ir, "__prog_ipo_const_arg"); |
| 103 | 104 | let opt_compute = function_section(&opt_ir, "compute"); |
| 104 | 105 | |
| 105 | - assert_eq!(param_count(raw_compute), 2, "raw helper should keep both dummies:\n{}", raw_compute); | |
| 106 | - assert_eq!(param_count(opt_compute), 1, "optimized helper should specialize the constant dummy:\n{}", opt_compute); | |
| 106 | + assert_eq!( | |
| 107 | + param_count(raw_compute), | |
| 108 | + 2, | |
| 109 | + "raw helper should keep both dummies:\n{}", | |
| 110 | + raw_compute | |
| 111 | + ); | |
| 112 | + assert_eq!( | |
| 113 | + param_count(opt_compute), | |
| 114 | + 1, | |
| 115 | + "optimized helper should specialize the constant dummy:\n{}", | |
| 116 | + opt_compute | |
| 117 | + ); | |
| 107 | 118 | assert!( |
| 108 | 119 | opt_compute.contains("const_int 4"), |
| 109 | 120 | "optimized helper should materialize the specialized constant directly:\n{}", |
@@ -132,5 +143,8 @@ fn o2_specializes_constant_dummy_and_trims_internal_calls() { | ||
| 132 | 143 | opt_main |
| 133 | 144 | ); |
| 134 | 145 | |
| 135 | - assert_eq!(obj_o2_a, obj_o2_b, "specialized O2 object snapshot should stay deterministic"); | |
| 146 | + assert_eq!( | |
| 147 | + obj_o2_a, obj_o2_b, | |
| 148 | + "specialized O2 object snapshot should stay deterministic" | |
| 149 | + ); | |
| 136 | 150 | } |
tests/ipo_dead_arg.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -49,13 +51,12 @@ fn call_arg_counts_for(func_section: &str, callee_marker: &str) -> Vec<usize> { | ||
| 49 | 51 | .filter_map(|line| { |
| 50 | 52 | let line = line.trim(); |
| 51 | 53 | let call = line.find(&format!("call {}", callee_marker))?; |
| 52 | - let inside = line[call..] | |
| 53 | - .split_once('(')? | |
| 54 | - .1 | |
| 55 | - .split_once(')')? | |
| 56 | - .0 | |
| 57 | - .trim(); | |
| 58 | - Some(if inside.is_empty() { 0 } else { inside.split(", ").count() }) | |
| 54 | + let inside = line[call..].split_once('(')?.1.split_once(')')?.0.trim(); | |
| 55 | + Some(if inside.is_empty() { | |
| 56 | + 0 | |
| 57 | + } else { | |
| 58 | + inside.split(", ").count() | |
| 59 | + }) | |
| 59 | 60 | }) |
| 60 | 61 | .collect() |
| 61 | 62 | } |
@@ -86,8 +87,18 @@ fn o2_elides_dead_dummy_arg_from_recursive_internal_helper() { | ||
| 86 | 87 | let opt_main = function_section(&opt_ir, "__prog_ipo_dead_arg"); |
| 87 | 88 | let opt_helper = function_section(&opt_ir, "helper"); |
| 88 | 89 | |
| 89 | - assert_eq!(param_count(raw_helper), 3, "raw helper should keep all dummy args:\n{}", raw_helper); | |
| 90 | - assert_eq!(param_count(opt_helper), 2, "optimized helper should drop the dead dummy arg:\n{}", opt_helper); | |
| 90 | + assert_eq!( | |
| 91 | + param_count(raw_helper), | |
| 92 | + 3, | |
| 93 | + "raw helper should keep all dummy args:\n{}", | |
| 94 | + raw_helper | |
| 95 | + ); | |
| 96 | + assert_eq!( | |
| 97 | + param_count(opt_helper), | |
| 98 | + 2, | |
| 99 | + "optimized helper should drop the dead dummy arg:\n{}", | |
| 100 | + opt_helper | |
| 101 | + ); | |
| 91 | 102 | |
| 92 | 103 | let raw_call_counts = [ |
| 93 | 104 | call_arg_counts_for(raw_main, "@helper"), |
@@ -103,6 +114,14 @@ fn o2_elides_dead_dummy_arg_from_recursive_internal_helper() { | ||
| 103 | 114 | ] |
| 104 | 115 | .concat(); |
| 105 | 116 | |
| 106 | - assert_eq!(raw_call_counts, vec![3, 3], "raw IR should pass all three args at both call sites"); | |
| 107 | - assert_eq!(opt_call_counts, vec![2, 2], "optimized IR should trim the dead arg at both call sites"); | |
| 117 | + assert_eq!( | |
| 118 | + raw_call_counts, | |
| 119 | + vec![3, 3], | |
| 120 | + "raw IR should pass all three args at both call sites" | |
| 121 | + ); | |
| 122 | + assert_eq!( | |
| 123 | + opt_call_counts, | |
| 124 | + vec![2, 2], | |
| 125 | + "optimized IR should trim the dead arg at both call sites" | |
| 126 | + ); | |
| 108 | 127 | } |
tests/ipo_return_prop.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -35,13 +37,12 @@ fn call_arg_counts_for(func_section: &str, callee_marker: &str) -> Vec<usize> { | ||
| 35 | 37 | .filter_map(|line| { |
| 36 | 38 | let line = line.trim(); |
| 37 | 39 | let call = line.find(&format!("call {}", callee_marker))?; |
| 38 | - let inside = line[call..] | |
| 39 | - .split_once('(')? | |
| 40 | - .1 | |
| 41 | - .split_once(')')? | |
| 42 | - .0 | |
| 43 | - .trim(); | |
| 44 | - Some(if inside.is_empty() { 0 } else { inside.split(", ").count() }) | |
| 40 | + let inside = line[call..].split_once('(')?.1.split_once(')')?.0.trim(); | |
| 41 | + Some(if inside.is_empty() { | |
| 42 | + 0 | |
| 43 | + } else { | |
| 44 | + inside.split(", ").count() | |
| 45 | + }) | |
| 45 | 46 | }) |
| 46 | 47 | .collect() |
| 47 | 48 | } |
@@ -103,5 +104,8 @@ fn o2_propagates_trivial_return_and_deletes_helper() { | ||
| 103 | 104 | "optimized caller should no longer call the passthrough helper:\n{}", |
| 104 | 105 | opt_main |
| 105 | 106 | ); |
| 106 | - assert_eq!(obj_a, obj_b, "O2 object snapshot should stay deterministic after return propagation"); | |
| 107 | + assert_eq!( | |
| 108 | + obj_a, obj_b, | |
| 109 | + "O2 object snapshot should stay deterministic after return propagation" | |
| 110 | + ); | |
| 107 | 111 | } |
tests/licm_lsf_audit_29_11.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -36,10 +38,7 @@ fn block_section<'a>(func_section: &'a str, prefix: &str) -> &'a str { | ||
| 36 | 38 | for (idx, _line) in func_section.match_indices('\n') { |
| 37 | 39 | let line_start = idx + 1; |
| 38 | 40 | let tail = &func_section[line_start..]; |
| 39 | - let line_text = tail | |
| 40 | - .split_once('\n') | |
| 41 | - .map(|(line, _)| line) | |
| 42 | - .unwrap_or(tail); | |
| 41 | + let line_text = tail.split_once('\n').map(|(line, _)| line).unwrap_or(tail); | |
| 43 | 42 | |
| 44 | 43 | if start.is_none() { |
| 45 | 44 | if line_text.starts_with(" ") |
@@ -67,7 +66,9 @@ fn block_section<'a>(func_section: &'a str, prefix: &str) -> &'a str { | ||
| 67 | 66 | } |
| 68 | 67 | |
| 69 | 68 | fn tail_after<'a>(text: &'a str, needle: &str) -> &'a str { |
| 70 | - let start = text.find(needle).unwrap_or_else(|| panic!("missing '{}' in:\n{}", needle, text)); | |
| 69 | + let start = text | |
| 70 | + .find(needle) | |
| 71 | + .unwrap_or_else(|| panic!("missing '{}' in:\n{}", needle, text)); | |
| 71 | 72 | &text[start + needle.len()..] |
| 72 | 73 | } |
| 73 | 74 | |
tests/multifile.rsmodified@@ -20,7 +20,9 @@ fn unique_dir() -> PathBuf { | ||
| 20 | 20 | fn find_compiler() -> PathBuf { |
| 21 | 21 | for c in &["target/release/armfortas", "target/debug/armfortas"] { |
| 22 | 22 | let p = PathBuf::from(c); |
| 23 | - if p.exists() { return p; } | |
| 23 | + if p.exists() { | |
| 24 | + return p; | |
| 25 | + } | |
| 24 | 26 | } |
| 25 | 27 | panic!("armfortas binary not found"); |
| 26 | 28 | } |
@@ -28,7 +30,9 @@ fn find_compiler() -> PathBuf { | ||
| 28 | 30 | fn find_runtime() -> PathBuf { |
| 29 | 31 | for dir in &["target/release", "target/debug"] { |
| 30 | 32 | let p = PathBuf::from(dir).join("libarmfortas_rt.a"); |
| 31 | - if p.exists() { return p; } | |
| 33 | + if p.exists() { | |
| 34 | + return p; | |
| 35 | + } | |
| 32 | 36 | } |
| 33 | 37 | panic!("libarmfortas_rt.a not found"); |
| 34 | 38 | } |
@@ -44,7 +48,12 @@ fn sdk_path() -> String { | ||
| 44 | 48 | /// Compile a .f90 file with -c, producing .o and optionally .amod. |
| 45 | 49 | fn compile_file(compiler: &Path, source: &Path, output: &Path, search_dir: Option<&Path>) { |
| 46 | 50 | let mut cmd = Command::new(compiler); |
| 47 | - cmd.args([source.to_str().unwrap(), "-c", "-o", output.to_str().unwrap()]); | |
| 51 | + cmd.args([ | |
| 52 | + source.to_str().unwrap(), | |
| 53 | + "-c", | |
| 54 | + "-o", | |
| 55 | + output.to_str().unwrap(), | |
| 56 | + ]); | |
| 48 | 57 | if let Some(dir) = search_dir { |
| 49 | 58 | cmd.arg(format!("-I{}", dir.display())); |
| 50 | 59 | } |
@@ -66,8 +75,17 @@ fn link_files(objects: &[&Path], output: &Path) { | ||
| 66 | 75 | args.push(o.to_str().unwrap().into()); |
| 67 | 76 | } |
| 68 | 77 | args.push(runtime.to_str().unwrap().into()); |
| 69 | - args.extend(["-lSystem".into(), "-syslibroot".into(), sdk, "-arch".into(), "arm64".into()]); | |
| 70 | - let result = Command::new("ld").args(&args).output().expect("ld launch failed"); | |
| 78 | + args.extend([ | |
| 79 | + "-lSystem".into(), | |
| 80 | + "-syslibroot".into(), | |
| 81 | + sdk, | |
| 82 | + "-arch".into(), | |
| 83 | + "arm64".into(), | |
| 84 | + ]); | |
| 85 | + let result = Command::new("ld") | |
| 86 | + .args(&args) | |
| 87 | + .output() | |
| 88 | + .expect("ld launch failed"); | |
| 71 | 89 | assert!( |
| 72 | 90 | result.status.success(), |
| 73 | 91 | "link failed:\n{}", |
@@ -77,9 +95,7 @@ fn link_files(objects: &[&Path], output: &Path) { | ||
| 77 | 95 | |
| 78 | 96 | /// Run a binary and return its stdout. |
| 79 | 97 | fn run_binary(binary: &Path) -> String { |
| 80 | - let result = Command::new(binary) | |
| 81 | - .output() | |
| 82 | - .expect("binary launch failed"); | |
| 98 | + let result = Command::new(binary).output().expect("binary launch failed"); | |
| 83 | 99 | assert!( |
| 84 | 100 | result.status.success(), |
| 85 | 101 | "{} exited with {:?}\nstderr: {}", |
@@ -91,11 +107,7 @@ fn run_binary(binary: &Path) -> String { | ||
| 91 | 107 | } |
| 92 | 108 | |
| 93 | 109 | /// Full multi-file test: write sources, compile, link, run, check. |
| 94 | -fn multifile_test( | |
| 95 | - mod_source: &str, | |
| 96 | - main_source: &str, | |
| 97 | - expected_substring: &str, | |
| 98 | -) { | |
| 110 | +fn multifile_test(mod_source: &str, main_source: &str, expected_substring: &str) { | |
| 99 | 111 | let compiler = find_compiler(); |
| 100 | 112 | let dir = unique_dir(); |
| 101 | 113 | let mod_f90 = dir.join("mod.f90"); |
@@ -209,15 +221,27 @@ fn generic_interface_transitive_use() { | ||
| 209 | 221 | |
| 210 | 222 | std::fs::write(&base_f90, "module base\n implicit none\n interface add\n module procedure add_int, add_real\n end interface\ncontains\n integer function add_int(a, b)\n integer, intent(in) :: a, b\n add_int = a + b\n end function\n real function add_real(a, b)\n real, intent(in) :: a, b\n add_real = a + b\n end function\nend module\n").unwrap(); |
| 211 | 223 | std::fs::write(&middle_f90, "module middle\n use base\nend module\n").unwrap(); |
| 212 | - std::fs::write(&main_f90, "program p\n use middle\n print *, add(1, 2)\n print *, add(1.5, 2.5)\nend program\n").unwrap(); | |
| 224 | + std::fs::write( | |
| 225 | + &main_f90, | |
| 226 | + "program p\n use middle\n print *, add(1, 2)\n print *, add(1.5, 2.5)\nend program\n", | |
| 227 | + ) | |
| 228 | + .unwrap(); | |
| 213 | 229 | |
| 214 | 230 | compile_file(&compiler, &base_f90, &base_o, None); |
| 215 | 231 | compile_file(&compiler, &middle_f90, &middle_o, Some(&dir)); |
| 216 | 232 | compile_file(&compiler, &main_f90, &main_o, Some(&dir)); |
| 217 | 233 | link_files(&[&main_o, &middle_o, &base_o], &binary); |
| 218 | 234 | let output = run_binary(&binary); |
| 219 | - assert!(output.contains("3"), "expected '3' in output, got:\n{}", output); | |
| 220 | - assert!(output.contains("4.0000000E0"), "expected real add result in output, got:\n{}", output); | |
| 235 | + assert!( | |
| 236 | + output.contains("3"), | |
| 237 | + "expected '3' in output, got:\n{}", | |
| 238 | + output | |
| 239 | + ); | |
| 240 | + assert!( | |
| 241 | + output.contains("4.0000000E0"), | |
| 242 | + "expected real add result in output, got:\n{}", | |
| 243 | + output | |
| 244 | + ); | |
| 221 | 245 | |
| 222 | 246 | let _ = std::fs::remove_dir_all(&dir); |
| 223 | 247 | } |
@@ -238,7 +262,10 @@ fn module_private_default() { | ||
| 238 | 262 | // Check .amod only has pub_val. |
| 239 | 263 | let amod = std::fs::read_to_string(dir.join("priv_mod.amod")).unwrap(); |
| 240 | 264 | assert!(amod.contains("pub_val"), "pub_val should be in .amod"); |
| 241 | - assert!(!amod.contains("priv_val"), "priv_val should NOT be in .amod"); | |
| 265 | + assert!( | |
| 266 | + !amod.contains("priv_val"), | |
| 267 | + "priv_val should NOT be in .amod" | |
| 268 | + ); | |
| 242 | 269 | |
| 243 | 270 | let _ = std::fs::remove_dir_all(&dir); |
| 244 | 271 | } |
tests/multifile_gen.rsmodified@@ -13,9 +13,8 @@ static NEXT_ID: AtomicU64 = AtomicU64::new(0); | ||
| 13 | 13 | |
| 14 | 14 | fn unique_dir(prefix: &str) -> PathBuf { |
| 15 | 15 | let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); |
| 16 | - let dir = std::env::temp_dir().join(format!( | |
| 17 | - "afs_gen_{}_{}_{}", prefix, std::process::id(), id | |
| 18 | - )); | |
| 16 | + let dir = | |
| 17 | + std::env::temp_dir().join(format!("afs_gen_{}_{}_{}", prefix, std::process::id(), id)); | |
| 19 | 18 | fs::create_dir_all(&dir).unwrap(); |
| 20 | 19 | dir |
| 21 | 20 | } |
@@ -23,7 +22,9 @@ fn unique_dir(prefix: &str) -> PathBuf { | ||
| 23 | 22 | fn find_compiler() -> PathBuf { |
| 24 | 23 | for c in &["target/release/armfortas", "target/debug/armfortas"] { |
| 25 | 24 | let p = PathBuf::from(c); |
| 26 | - if p.exists() { return fs::canonicalize(&p).unwrap(); } | |
| 25 | + if p.exists() { | |
| 26 | + return fs::canonicalize(&p).unwrap(); | |
| 27 | + } | |
| 27 | 28 | } |
| 28 | 29 | panic!("armfortas binary not found"); |
| 29 | 30 | } |
@@ -31,7 +32,9 @@ fn find_compiler() -> PathBuf { | ||
| 31 | 32 | fn find_runtime() -> PathBuf { |
| 32 | 33 | for dir in &["target/release", "target/debug"] { |
| 33 | 34 | let p = PathBuf::from(dir).join("libarmfortas_rt.a"); |
| 34 | - if p.exists() { return p; } | |
| 35 | + if p.exists() { | |
| 36 | + return p; | |
| 37 | + } | |
| 35 | 38 | } |
| 36 | 39 | panic!("libarmfortas_rt.a not found"); |
| 37 | 40 | } |
@@ -47,8 +50,11 @@ fn sdk_path() -> String { | ||
| 47 | 50 | fn compile_file(compiler: &Path, source: &Path, output: &Path, search_dir: &Path, opt: &str) { |
| 48 | 51 | let result = Command::new(compiler) |
| 49 | 52 | .args([ |
| 50 | - source.to_str().unwrap(), "-c", opt, | |
| 51 | - "-o", output.to_str().unwrap(), | |
| 53 | + source.to_str().unwrap(), | |
| 54 | + "-c", | |
| 55 | + opt, | |
| 56 | + "-o", | |
| 57 | + output.to_str().unwrap(), | |
| 52 | 58 | &format!("-I{}", search_dir.display()), |
| 53 | 59 | ]) |
| 54 | 60 | .output() |
@@ -69,8 +75,17 @@ fn link_files(objects: &[PathBuf], output: &Path) { | ||
| 69 | 75 | args.push(o.to_str().unwrap().into()); |
| 70 | 76 | } |
| 71 | 77 | args.push(runtime.to_str().unwrap().into()); |
| 72 | - args.extend(["-lSystem".into(), "-syslibroot".into(), sdk, "-arch".into(), "arm64".into()]); | |
| 73 | - let result = Command::new("ld").args(&args).output().expect("ld launch failed"); | |
| 78 | + args.extend([ | |
| 79 | + "-lSystem".into(), | |
| 80 | + "-syslibroot".into(), | |
| 81 | + sdk, | |
| 82 | + "-arch".into(), | |
| 83 | + "arm64".into(), | |
| 84 | + ]); | |
| 85 | + let result = Command::new("ld") | |
| 86 | + .args(&args) | |
| 87 | + .output() | |
| 88 | + .expect("ld launch failed"); | |
| 74 | 89 | assert!( |
| 75 | 90 | result.status.success(), |
| 76 | 91 | "link failed:\n{}", |
@@ -83,7 +98,8 @@ fn run_binary(binary: &Path) -> String { | ||
| 83 | 98 | assert!( |
| 84 | 99 | result.status.success(), |
| 85 | 100 | "{} exited with {:?}\nstderr: {}", |
| 86 | - binary.display(), result.status.code(), | |
| 101 | + binary.display(), | |
| 102 | + result.status.code(), | |
| 87 | 103 | String::from_utf8_lossy(&result.stderr) |
| 88 | 104 | ); |
| 89 | 105 | String::from_utf8_lossy(&result.stdout).into_owned() |
@@ -114,16 +130,16 @@ fn gen_chain(depth: usize) -> (Vec<(String, String)>, String, &'static str) { | ||
| 114 | 130 | format!( |
| 115 | 131 | "module mod_{i}\n use mod_{next}\n implicit none\n \ |
| 116 | 132 | integer, parameter :: val_{i} = val_{next} + {i}\nend module\n", |
| 117 | - i = i, next = i + 1 | |
| 133 | + i = i, | |
| 134 | + next = i + 1 | |
| 118 | 135 | ), |
| 119 | 136 | )); |
| 120 | 137 | } |
| 121 | 138 | |
| 122 | 139 | // Main program uses mod_1 and prints the accumulated value. |
| 123 | 140 | let expected: usize = (1..=depth).sum(); |
| 124 | - let main_src = format!( | |
| 125 | - "program p\n use mod_1\n implicit none\n print *, val_1\nend program\n" | |
| 126 | - ); | |
| 141 | + let main_src = | |
| 142 | + format!("program p\n use mod_1\n implicit none\n print *, val_1\nend program\n"); | |
| 127 | 143 | |
| 128 | 144 | // Files are already in compilation order: leaf first, then towards root. |
| 129 | 145 | let expected_str = Box::leak(format!("{}", expected).into_boxed_str()); |
@@ -211,7 +227,10 @@ fn run_generated_test( | ||
| 211 | 227 | assert!( |
| 212 | 228 | output.contains(expected), |
| 213 | 229 | "{} [{}]: expected '{}' in output, got:\n{}", |
| 214 | - label, opt, expected, output | |
| 230 | + label, | |
| 231 | + opt, | |
| 232 | + expected, | |
| 233 | + output | |
| 215 | 234 | ); |
| 216 | 235 | |
| 217 | 236 | let _ = fs::remove_dir_all(&dir); |
@@ -303,7 +322,11 @@ fn run_cross_opt_test( | ||
| 303 | 322 | assert!( |
| 304 | 323 | output.contains(expected), |
| 305 | 324 | "{}: mod@{} + main@{}: expected '{}' in output, got:\n{}", |
| 306 | - label, mod_opt, main_opt, expected, output | |
| 325 | + label, | |
| 326 | + mod_opt, | |
| 327 | + main_opt, | |
| 328 | + expected, | |
| 329 | + output | |
| 307 | 330 | ); |
| 308 | 331 | |
| 309 | 332 | let _ = fs::remove_dir_all(&dir); |
@@ -336,11 +359,25 @@ fn abi_matrix_diamond4_mod_o2_main_o0() { | ||
| 336 | 359 | #[test] |
| 337 | 360 | fn abi_matrix_chain5_mod_o0_main_ofast() { |
| 338 | 361 | let (files, main_src, expected) = gen_chain(5); |
| 339 | - run_cross_opt_test(files, main_src, expected, "-O0", "-Ofast", "abi_chain5_0_fast"); | |
| 362 | + run_cross_opt_test( | |
| 363 | + files, | |
| 364 | + main_src, | |
| 365 | + expected, | |
| 366 | + "-O0", | |
| 367 | + "-Ofast", | |
| 368 | + "abi_chain5_0_fast", | |
| 369 | + ); | |
| 340 | 370 | } |
| 341 | 371 | |
| 342 | 372 | #[test] |
| 343 | 373 | fn abi_matrix_chain5_mod_ofast_main_o0() { |
| 344 | 374 | let (files, main_src, expected) = gen_chain(5); |
| 345 | - run_cross_opt_test(files, main_src, expected, "-Ofast", "-O0", "abi_chain5_fast_0"); | |
| 375 | + run_cross_opt_test( | |
| 376 | + files, | |
| 377 | + main_src, | |
| 378 | + expected, | |
| 379 | + "-Ofast", | |
| 380 | + "-O0", | |
| 381 | + "abi_chain5_fast_0", | |
| 382 | + ); | |
| 346 | 383 | } |
tests/ofast_fast_math.rsmodified@@ -30,7 +30,9 @@ fn capture_run(request: CaptureRequest) -> RunCapture { | ||
| 30 | 30 | |
| 31 | 31 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 32 | 32 | let header = format!(" func @{}", name); |
| 33 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 33 | + let start = ir | |
| 34 | + .find(&header) | |
| 35 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 34 | 36 | let rest = &ir[start..]; |
| 35 | 37 | let end = rest |
| 36 | 38 | .find("\n }\n") |
@@ -109,7 +111,18 @@ fn ofast_reassociates_float_constant_chain_but_o3_stays_strict() { | ||
| 109 | 111 | Stage::Obj, |
| 110 | 112 | ); |
| 111 | 113 | |
| 112 | - assert_eq!(parse_last_int(&o3_run.stdout), 0, "strict O3 run should keep IEEE-style rounding loss"); | |
| 113 | - assert_eq!(parse_last_int(&ofast_run.stdout), 1, "Ofast run should expose fast-math reassociation"); | |
| 114 | - assert_eq!(ofast_obj_a, ofast_obj_b, "Ofast object snapshot should stay deterministic"); | |
| 114 | + assert_eq!( | |
| 115 | + parse_last_int(&o3_run.stdout), | |
| 116 | + 0, | |
| 117 | + "strict O3 run should keep IEEE-style rounding loss" | |
| 118 | + ); | |
| 119 | + assert_eq!( | |
| 120 | + parse_last_int(&ofast_run.stdout), | |
| 121 | + 1, | |
| 122 | + "Ofast run should expose fast-math reassociation" | |
| 123 | + ); | |
| 124 | + assert_eq!( | |
| 125 | + ofast_obj_a, ofast_obj_b, | |
| 126 | + "Ofast object snapshot should stay deterministic" | |
| 127 | + ); | |
| 115 | 128 | } |
tests/opt_audit_29_11.rsmodified@@ -25,9 +25,7 @@ fn function_slice<'a>(ir: &'a str, name: &str) -> &'a str { | ||
| 25 | 25 | .find(&marker) |
| 26 | 26 | .unwrap_or_else(|| panic!("missing function {} in IR:\n{}", name, ir)); |
| 27 | 27 | let rest = &ir[start..]; |
| 28 | - let end = rest | |
| 29 | - .find("\n func @") | |
| 30 | - .unwrap_or(rest.len()); | |
| 28 | + let end = rest.find("\n func @").unwrap_or(rest.len()); | |
| 31 | 29 | &rest[..end] |
| 32 | 30 | } |
| 33 | 31 | |
@@ -112,7 +110,10 @@ fn sasum_cleanup_eliminates_chunked_loop_bounds_checks_at_o2() { | ||
| 112 | 110 | |
| 113 | 111 | #[test] |
| 114 | 112 | fn realworld_29_8_kernels_have_deterministic_o2_objects() { |
| 115 | - for name in ["realworld_sasum_cleanup.f90", "realworld_three_point_apply.f90"] { | |
| 113 | + for name in [ | |
| 114 | + "realworld_sasum_cleanup.f90", | |
| 115 | + "realworld_three_point_apply.f90", | |
| 116 | + ] { | |
| 116 | 117 | let source = fixture(name); |
| 117 | 118 | let first = capture_text( |
| 118 | 119 | CaptureRequest { |
tests/program_entry_audit.rsmodified@@ -67,7 +67,10 @@ fn capture_program_entry_fixture_runs_at_o2() { | ||
| 67 | 67 | opt_level: OptLevel::O2, |
| 68 | 68 | }); |
| 69 | 69 | |
| 70 | - assert_eq!(run.exit_code, 0, "program should run successfully:\n{run:#?}"); | |
| 70 | + assert_eq!( | |
| 71 | + run.exit_code, 0, | |
| 72 | + "program should run successfully:\n{run:#?}" | |
| 73 | + ); | |
| 71 | 74 | assert!( |
| 72 | 75 | run.stdout.split_whitespace().any(|field| field == "99"), |
| 73 | 76 | "program body should execute and print the helper-written value:\n{}", |
tests/pure_call_reuse.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
@@ -57,10 +59,8 @@ fn o2_reuses_pure_recursive_call_in_program_caller() { | ||
| 57 | 59 | let raw_main = function_section(&raw_ir, "__prog_pure_recursive_reuse"); |
| 58 | 60 | let opt_main = function_section(&opt_ir, "__prog_pure_recursive_reuse"); |
| 59 | 61 | |
| 60 | - let raw_pure_calls = | |
| 61 | - count(raw_main, "call @heavy_fact(") + count(raw_main, "call @func_"); | |
| 62 | - let opt_pure_calls = | |
| 63 | - count(opt_main, "call @heavy_fact(") + count(opt_main, "call @func_"); | |
| 62 | + let raw_pure_calls = count(raw_main, "call @heavy_fact(") + count(raw_main, "call @func_"); | |
| 63 | + let opt_pure_calls = count(opt_main, "call @heavy_fact(") + count(opt_main, "call @func_"); | |
| 64 | 64 | |
| 65 | 65 | assert_eq!( |
| 66 | 66 | raw_pure_calls, 2, |
tests/pure_dead_call_elim.rsmodified@@ -21,7 +21,9 @@ fn capture_text(request: CaptureRequest, stage: Stage) -> String { | ||
| 21 | 21 | |
| 22 | 22 | fn function_section<'a>(ir: &'a str, name: &str) -> &'a str { |
| 23 | 23 | let header = format!(" func @{}", name); |
| 24 | - let start = ir.find(&header).unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 24 | + let start = ir | |
| 25 | + .find(&header) | |
| 26 | + .unwrap_or_else(|| panic!("missing function section for {}", name)); | |
| 25 | 27 | let rest = &ir[start..]; |
| 26 | 28 | let end = rest |
| 27 | 29 | .find("\n }\n") |
tests/vectorize_do_loop.rsmodified@@ -92,5 +92,8 @@ fn o3_vectorizes_full_extent_do_loop_and_keeps_objects_deterministic() { | ||
| 92 | 92 | "O3 assembly should reference the bulk add kernel:\n{}", |
| 93 | 93 | o3_asm |
| 94 | 94 | ); |
| 95 | - assert_eq!(o3_obj_a, o3_obj_b, "O3 vectorized object snapshot should stay deterministic"); | |
| 95 | + assert_eq!( | |
| 96 | + o3_obj_a, o3_obj_b, | |
| 97 | + "O3 vectorized object snapshot should stay deterministic" | |
| 98 | + ); | |
| 96 | 99 | } |