|
1028 | 1028 |
|
1029 | 1029 | ;;;; Rules for `udiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
1030 | 1030 |
|
1031 |
| -;; Note that aarch64's `udiv` doesn't trap so to respect the semantics of |
1032 |
| -;; CLIF's `udiv` the check for zero needs to be manually performed. |
1033 |
| - |
1034 |
| -(rule udiv 1 (lower (has_type $I64 (udiv x y))) |
1035 |
| - (a64_udiv $I64 (put_in_reg x) (put_nonzero_in_reg y))) |
| 1031 | +;; Enum representing the types of extensions |
| 1032 | +(type ExtType |
| 1033 | + (enum |
| 1034 | + (Signed) |
| 1035 | + (Unsigned))) |
1036 | 1036 |
|
1037 |
| -(rule udiv (lower (has_type (fits_in_32 ty) (udiv x y))) |
1038 |
| - (a64_udiv $I32 (put_in_reg_zext32 x) (put_nonzero_in_reg y))) |
1039 |
| - |
1040 |
| -;; helpers for udiv: |
1041 | 1037 | ;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero.
|
1042 |
| -(decl put_nonzero_in_reg (Value) Reg) |
| 1038 | +;; It takes a value and extension type, and performs the appropriate checks. |
| 1039 | +;; TODO: restore spec |
| 1040 | +; (spec (put_nonzero_in_reg_sext64 x) |
| 1041 | +; (provide (= (sign_ext 64 x) result)) |
| 1042 | +; (require (not (= #x0000000000000000 result)))) |
| 1043 | +(decl put_nonzero_in_reg (Value ExtType Type) Reg) |
1043 | 1044 |
|
1044 | 1045 | ;; Special case where if a `Value` is known to be nonzero we can trivially
|
1045 | 1046 | ;; move it into a register.
|
1046 |
| -(rule (put_nonzero_in_reg (and (value_type ty) (iconst (nonzero_u64_from_imm64 n)))) |
| 1047 | + |
| 1048 | +;; zero-extend non-zero constant |
| 1049 | +(rule (put_nonzero_in_reg (iconst (nonzero_u64_from_imm64 n)) (ExtType.Unsigned) ty) |
1047 | 1050 | (imm ty (ImmExtend.Zero) n))
|
1048 | 1051 |
|
1049 |
| -(rule -1 (put_nonzero_in_reg (and (value_type $I64) val)) |
| 1052 | +;; sign-extend non-zero constant |
| 1053 | +(rule (put_nonzero_in_reg (iconst (nonzero_u64_from_imm64 n)) (ExtType.Signed) ty) |
| 1054 | + (imm ty (ImmExtend.Sign) n)) |
| 1055 | + |
| 1056 | +(rule -1 (put_nonzero_in_reg val _ $I64) |
1050 | 1057 | (trap_if_zero_divisor (put_in_reg val) (operand_size $I64)))
|
1051 | 1058 |
|
1052 |
| -(rule -2 (put_nonzero_in_reg (and (value_type (fits_in_32 _)) val)) |
| 1059 | +(rule -2 (put_nonzero_in_reg val (ExtType.Signed) (fits_in_32 _)) |
| 1060 | + (trap_if_zero_divisor (put_in_reg_sext32 val) (operand_size $I32))) |
| 1061 | + |
| 1062 | +(rule -2 (put_nonzero_in_reg val (ExtType.Unsigned) (fits_in_32 _)) |
1053 | 1063 | (trap_if_zero_divisor (put_in_reg_zext32 val) (operand_size $I32)))
|
1054 | 1064 |
|
1055 |
| -;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero and extending it to 64 bits. |
1056 |
| -(spec (put_nonzero_in_reg_zext64 x) |
1057 |
| - (provide (= result (zero_ext 64 x))) |
1058 |
| - (require (not (= result #x0000000000000000)))) |
1059 |
| -(decl put_nonzero_in_reg_zext64 (Value) Reg) |
1060 |
| -(rule -1 (put_nonzero_in_reg_zext64 (and (value_type ty) val)) |
1061 |
| - (trap_if_zero_divisor (put_in_reg_zext64 val) (operand_size ty))) |
| 1065 | +;; Note that aarch64's `udiv` doesn't trap so to respect the semantics of |
| 1066 | +;; CLIF's `udiv` the check for zero needs to be manually performed. |
1062 | 1067 |
|
1063 |
| -;; Special case where if a `Value` is known to be nonzero we can trivially |
1064 |
| -;; move it into a register. |
1065 |
| -(rule (put_nonzero_in_reg_zext64 (and (value_type ty) |
1066 |
| - (iconst (nonzero_u64_from_imm64 n)))) |
1067 |
| - (imm ty (ImmExtend.Zero) n)) |
| 1068 | +(rule udiv 1 (lower (has_type $I64 (udiv x y))) |
| 1069 | + (a64_udiv $I64 (put_in_reg x) (put_nonzero_in_reg y (ExtType.Unsigned) $I64))) |
| 1070 | + |
| 1071 | +(rule udiv (lower (has_type (fits_in_32 ty) (udiv x y))) |
| 1072 | + (a64_udiv $I32 (put_in_reg_zext32 x) (put_nonzero_in_reg y (ExtType.Unsigned) ty))) |
1068 | 1073 |
|
1069 | 1074 | ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
1070 | 1075 |
|
|
1088 | 1093 | ;;
|
1089 | 1094 | ;; TODO: if `y` is -1 then a check that `x` is not INT_MIN is all that's
|
1090 | 1095 | ;; necessary, but right now `y` is checked to not be -1 as well.
|
1091 |
| -(rule sdiv_base_case (lower (has_type (fits_in_64 ty) (sdiv x y))) |
| 1096 | + |
| 1097 | +(rule sdiv_base_case (lower (has_type $I64 (sdiv x y))) |
1092 | 1098 | (let ((x64 Reg (put_in_reg_sext64 x))
|
1093 |
| - (y64 Reg (put_nonzero_in_reg_sext64 y)) |
1094 |
| - (intmin_check_x Reg (intmin_check ty x64)) |
1095 |
| - (valid_x64 Reg (trap_if_div_overflow ty intmin_check_x x64 y64)) |
| 1099 | + (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) |
| 1100 | + (intmin_check_x Reg (intmin_check $I64 x64)) |
| 1101 | + (valid_x64 Reg (trap_if_div_overflow $I64 intmin_check_x x64 y64)) |
1096 | 1102 | (result Reg (a64_sdiv $I64 valid_x64 y64)))
|
1097 | 1103 | result))
|
1098 | 1104 |
|
| 1105 | +(rule sdiv_base_case -1 (lower (has_type (fits_in_32 ty) (sdiv x y))) |
| 1106 | + (let ((x32 Reg (put_in_reg_sext32 x)) |
| 1107 | + (y32 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) |
| 1108 | + (intmin_check_x Reg (intmin_check ty x32)) |
| 1109 | + (valid_x32 Reg (trap_if_div_overflow ty intmin_check_x x32 y32)) |
| 1110 | + (result Reg (a64_sdiv ty valid_x32 y32))) |
| 1111 | + result)) |
| 1112 | + |
1099 | 1113 | ;; Special case for `sdiv` where no checks are needed due to division by a
|
1100 | 1114 | ;; constant meaning the checks are always passed.
|
1101 |
| -(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_64 ty) (sdiv x (iconst imm)))) |
| 1115 | +(rule sdiv_safe_divisor 2 (lower (has_type $I64 (sdiv x (iconst imm)))) |
| 1116 | + (if-let y (safe_divisor_from_imm64 $I64 imm)) |
| 1117 | + (a64_sdiv $I64 (put_in_reg_sext64 x) (imm $I64 (ImmExtend.Sign) y))) |
| 1118 | + |
| 1119 | +(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_32 ty) (sdiv x (iconst imm)))) |
1102 | 1120 | (if-let y (safe_divisor_from_imm64 ty imm))
|
1103 |
| - (a64_sdiv $I64 (put_in_reg_sext64 x) (imm ty (ImmExtend.Sign) y))) |
| 1121 | + (a64_sdiv ty (put_in_reg_sext32 x) (imm ty (ImmExtend.Sign) y))) |
1104 | 1122 |
|
1105 | 1123 | ;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero.
|
1106 |
| - (spec (put_nonzero_in_reg_sext64 x) |
1107 |
| - (provide (= (sign_ext 64 x) result)) |
1108 |
| - (require (not (= #x0000000000000000 result)))) |
1109 |
| -(decl put_nonzero_in_reg_sext64 (Value) Reg) |
1110 |
| -(rule -1 (put_nonzero_in_reg_sext64 val) |
1111 |
| - (trap_if_zero_divisor (put_in_reg_sext64 val) (operand_size $I64))) |
1112 |
| - |
1113 |
| -;; Note that this has a special case where if the `Value` is a constant that's |
1114 |
| -;; not zero we can skip the zero check. |
1115 |
| -(rule (put_nonzero_in_reg_sext64 (and (value_type ty) |
1116 |
| - (iconst (nonzero_u64_from_imm64 n)))) |
1117 |
| - (imm ty (ImmExtend.Sign) n)) |
1118 | 1124 |
|
1119 | 1125 | ;;;; Rules for `urem` and `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
1120 | 1126 |
|
|
1130 | 1136 | ;; div rd, x, y ; rd = x / y
|
1131 | 1137 | ;; msub rd, rd, y, x ; rd = x - rd * y
|
1132 | 1138 |
|
1133 |
| -(rule urem (lower (has_type (fits_in_64 ty) (urem x y))) |
| 1139 | +;; TODO: we can avoid a 0 check, if the dividend is a non-0 constant |
| 1140 | + |
| 1141 | +(rule urem (lower (has_type $I64 (urem x y))) |
1134 | 1142 | (let ((x64 Reg (put_in_reg_zext64 x))
|
1135 |
| - (y64 Reg (put_nonzero_in_reg_zext64 y)) |
| 1143 | + (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) $I64)) |
1136 | 1144 | (div Reg (a64_udiv $I64 x64 y64))
|
1137 | 1145 | (result Reg (msub $I64 div y64 x64)))
|
1138 | 1146 | result))
|
1139 | 1147 |
|
1140 |
| -(rule srem (lower (has_type (fits_in_64 ty) (srem x y))) |
| 1148 | +(rule urem -1 (lower (has_type (fits_in_32 ty) (urem x y))) |
| 1149 | + (let ((x64 Reg (put_in_reg_zext32 x)) |
| 1150 | + (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) ty)) |
| 1151 | + (div Reg (a64_udiv ty x64 y64)) |
| 1152 | + (result Reg (msub ty div y64 x64))) |
| 1153 | + result)) |
| 1154 | + |
| 1155 | +(rule srem (lower (has_type $I64 (srem x y))) |
1141 | 1156 | (let ((x64 Reg (put_in_reg_sext64 x))
|
1142 |
| - (y64 Reg (put_nonzero_in_reg_sext64 y)) |
| 1157 | + (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) |
1143 | 1158 | (div Reg (a64_sdiv $I64 x64 y64))
|
1144 | 1159 | (result Reg (msub $I64 div y64 x64)))
|
1145 | 1160 | result))
|
1146 | 1161 |
|
| 1162 | +(rule srem -1 (lower (has_type (fits_in_32 ty) (srem x y))) |
| 1163 | + (let ((x64 Reg (put_in_reg_sext32 x)) |
| 1164 | + (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) |
| 1165 | + (div Reg (a64_sdiv ty x64 y64)) |
| 1166 | + (result Reg (msub ty div y64 x64))) |
| 1167 | + result)) |
| 1168 | + |
1147 | 1169 | ;;; Rules for integer min/max: umin, smin, umax, smax ;;;;;;;;;;;;;;;;;;;;;;;;;
|
1148 | 1170 |
|
1149 | 1171 | ;; `i64` and smaller.
|
|
0 commit comments