| ... | ... | @@ -875,10 +875,6 @@ fn analyzeBodyInner( |
| 875 | 875 | .add => try sema.zirArithmetic(block, inst, .add), |
| 876 | 876 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), |
| 877 | 877 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), |
| 878 | | .div => try sema.zirDiv(block, inst), |
| 879 | | .div_exact => try sema.zirArithmetic(block, inst, .div_exact), |
| 880 | | .div_floor => try sema.zirArithmetic(block, inst, .div_floor), |
| 881 | | .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc), |
| 882 | 878 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), |
| 883 | 879 | .mod => try sema.zirArithmetic(block, inst, .mod), |
| 884 | 880 | .rem => try sema.zirArithmetic(block, inst, .rem), |
| ... | ... | @@ -889,6 +885,11 @@ fn analyzeBodyInner( |
| 889 | 885 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), |
| 890 | 886 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), |
| 891 | 887 | |
| 888 | .div => try sema.zirDiv(block, inst), |
| 889 | .div_exact => try sema.zirDivExact(block, inst), |
| 890 | .div_floor => try sema.zirDivFloor(block, inst), |
| 891 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 892 | |
| 892 | 893 | .maximum => try sema.zirMinMax(block, inst, .max), |
| 893 | 894 | .minimum => try sema.zirMinMax(block, inst, .min), |
| 894 | 895 | |
| ... | ... | @@ -10999,6 +11000,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 10999 | 11000 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11000 | 11001 | return sema.failWithDivideByZero(block, rhs_src); |
| 11001 | 11002 | } |
| 11003 | // TODO: if the RHS is one, return the LHS directly |
| 11002 | 11004 | } |
| 11003 | 11005 | }, |
| 11004 | 11006 | else => {}, |
| ... | ... | @@ -11041,112 +11043,506 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 11041 | 11043 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11042 | 11044 | |
| 11043 | 11045 | if (block.wantSafety()) { |
| 11044 | | int_overflow: { |
| 11045 | | if (!is_int) break :int_overflow; |
| 11046 | try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 11047 | try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 11048 | } |
| 11046 | 11049 | |
| 11047 | | // If the LHS is unsigned, it cannot cause overflow. |
| 11048 | | if (!lhs_scalar_ty.isSignedInt()) break :int_overflow; |
| 11050 | const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) { |
| 11051 | .Optimized => Air.Inst.Tag.div_float_optimized, |
| 11052 | .Strict => Air.Inst.Tag.div_float, |
| 11053 | }; |
| 11054 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 11055 | } |
| 11049 | 11056 | |
| 11050 | | // If the LHS is widened to a larger integer type, no overflow is possible. |
| 11051 | | if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) { |
| 11052 | | break :int_overflow; |
| 11053 | | } |
| 11057 | fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11058 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 11059 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; |
| 11060 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 11061 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 11062 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 11063 | const lhs = try sema.resolveInst(extra.lhs); |
| 11064 | const rhs = try sema.resolveInst(extra.rhs); |
| 11065 | const lhs_ty = sema.typeOf(lhs); |
| 11066 | const rhs_ty = sema.typeOf(rhs); |
| 11067 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); |
| 11068 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); |
| 11069 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 11070 | try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_exact); |
| 11054 | 11071 | |
| 11055 | | const min_int = try resolved_type.minInt(sema.arena, target); |
| 11056 | | const neg_one = try Value.Tag.int_i64.create(sema.arena, -1); |
| 11072 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 11073 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| 11074 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, |
| 11075 | }); |
| 11057 | 11076 | |
| 11058 | | // If the LHS is comptime-known to be not equal to the min int, |
| 11059 | | // no overflow is possible. |
| 11060 | | if (maybe_lhs_val) |lhs_val| { |
| 11061 | | if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) break :int_overflow; |
| 11062 | | } |
| 11077 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 11078 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 11063 | 11079 | |
| 11064 | | // If the RHS is comptime-known to not be equal to -1, no overflow is possible. |
| 11065 | | if (maybe_rhs_val) |rhs_val| { |
| 11066 | | if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) break :int_overflow; |
| 11067 | | } |
| 11080 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 11081 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11068 | 11082 | |
| 11069 | | var ok: Air.Inst.Ref = .none; |
| 11070 | | if (resolved_type.zigTypeTag() == .Vector) { |
| 11071 | | const vector_ty_ref = try sema.addType(resolved_type); |
| 11072 | | if (maybe_lhs_val == null) { |
| 11073 | | const min_int_ref = try sema.addConstant( |
| 11083 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 11084 | |
| 11085 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); |
| 11086 | |
| 11087 | const mod = sema.mod; |
| 11088 | const target = mod.getTarget(); |
| 11089 | const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); |
| 11090 | const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); |
| 11091 | |
| 11092 | const runtime_src = rs: { |
| 11093 | // For integers: |
| 11094 | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11095 | // If the rhs is zero, compile error for division by zero. |
| 11096 | // If the rhs is undefined, compile error because there is a possible |
| 11097 | // value (zero) for which the division would be illegal behavior. |
| 11098 | // If the lhs is undefined, compile error because there is a possible |
| 11099 | // value for which the division would result in a remainder. |
| 11100 | // TODO: emit runtime safety for if there is a remainder |
| 11101 | // TODO: emit runtime safety for division by zero |
| 11102 | // |
| 11103 | // For floats: |
| 11104 | // If the rhs is zero, compile error for division by zero. |
| 11105 | // If the rhs is undefined, compile error because there is a possible |
| 11106 | // value (zero) for which the division would be illegal behavior. |
| 11107 | // If the lhs is undefined, compile error because there is a possible |
| 11108 | // value for which the division would result in a remainder. |
| 11109 | if (maybe_lhs_val) |lhs_val| { |
| 11110 | if (lhs_val.isUndef()) { |
| 11111 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11112 | } else { |
| 11113 | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11114 | return sema.addConstant(resolved_type, Value.zero); |
| 11115 | } |
| 11116 | } |
| 11117 | } |
| 11118 | if (maybe_rhs_val) |rhs_val| { |
| 11119 | if (rhs_val.isUndef()) { |
| 11120 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11121 | } |
| 11122 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11123 | return sema.failWithDivideByZero(block, rhs_src); |
| 11124 | } |
| 11125 | // TODO: if the RHS is one, return the LHS directly |
| 11126 | } |
| 11127 | if (maybe_lhs_val) |lhs_val| { |
| 11128 | if (maybe_rhs_val) |rhs_val| { |
| 11129 | if (is_int) { |
| 11130 | // TODO: emit compile error if there is a remainder |
| 11131 | return sema.addConstant( |
| 11074 | 11132 | resolved_type, |
| 11075 | | try Value.Tag.repeated.create(sema.arena, min_int), |
| 11133 | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11076 | 11134 | ); |
| 11077 | | ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref); |
| 11078 | | } |
| 11079 | | if (maybe_rhs_val == null) { |
| 11080 | | const neg_one_ref = try sema.addConstant( |
| 11135 | } else { |
| 11136 | // TODO: emit compile error if there is a remainder |
| 11137 | return sema.addConstant( |
| 11081 | 11138 | resolved_type, |
| 11082 | | try Value.Tag.repeated.create(sema.arena, neg_one), |
| 11139 | try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), |
| 11083 | 11140 | ); |
| 11084 | | const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref); |
| 11085 | | if (ok == .none) { |
| 11086 | | ok = rhs_ok; |
| 11087 | | } else { |
| 11088 | | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11089 | | } |
| 11090 | 11141 | } |
| 11091 | | assert(ok != .none); |
| 11092 | | ok = try block.addInst(.{ |
| 11093 | | .tag = .reduce, |
| 11142 | } else break :rs rhs_src; |
| 11143 | } else break :rs lhs_src; |
| 11144 | }; |
| 11145 | |
| 11146 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11147 | |
| 11148 | // Depending on whether safety is enabled, we will have a slightly different strategy |
| 11149 | // here. The `div_exact` AIR instruction causes undefined behavior if a remainder |
| 11150 | // is produced, so in the safety check case, it cannot be used. Instead we do a |
| 11151 | // div_trunc and check for remainder. |
| 11152 | |
| 11153 | if (block.wantSafety()) { |
| 11154 | try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 11155 | try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 11156 | |
| 11157 | const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); |
| 11158 | const ok = if (!is_int) ok: { |
| 11159 | const floored = try block.addUnOp(.floor, result); |
| 11160 | |
| 11161 | if (resolved_type.zigTypeTag() == .Vector) { |
| 11162 | const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type)); |
| 11163 | break :ok try block.addInst(.{ |
| 11164 | .tag = switch (block.float_mode) { |
| 11165 | .Strict => .reduce, |
| 11166 | .Optimized => .reduce_optimized, |
| 11167 | }, |
| 11094 | 11168 | .data = .{ .reduce = .{ |
| 11095 | | .operand = ok, |
| 11169 | .operand = eql, |
| 11096 | 11170 | .operation = .And, |
| 11097 | 11171 | } }, |
| 11098 | 11172 | }); |
| 11099 | 11173 | } else { |
| 11100 | | if (maybe_lhs_val == null) { |
| 11101 | | const min_int_ref = try sema.addConstant(resolved_type, min_int); |
| 11102 | | ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); |
| 11103 | | } |
| 11104 | | if (maybe_rhs_val == null) { |
| 11105 | | const neg_one_ref = try sema.addConstant(resolved_type, neg_one); |
| 11106 | | const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); |
| 11107 | | if (ok == .none) { |
| 11108 | | ok = rhs_ok; |
| 11109 | | } else { |
| 11110 | | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11111 | | } |
| 11112 | | } |
| 11113 | | assert(ok != .none); |
| 11174 | const is_in_range = try block.addBinOp(switch (block.float_mode) { |
| 11175 | .Strict => .cmp_eq, |
| 11176 | .Optimized => .cmp_eq_optimized, |
| 11177 | }, result, floored); |
| 11178 | break :ok is_in_range; |
| 11114 | 11179 | } |
| 11115 | | try sema.addSafetyCheck(block, ok, .integer_overflow); |
| 11116 | | } |
| 11117 | | |
| 11118 | | div_by_zero: { |
| 11119 | | // Strict IEEE floats have well-defined division by zero. |
| 11120 | | if (!is_int and block.float_mode == .Strict) break :div_by_zero; |
| 11180 | } else ok: { |
| 11181 | const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); |
| 11121 | 11182 | |
| 11122 | | // If rhs was comptime-known to be zero a compile error would have been |
| 11123 | | // emitted above. |
| 11124 | | if (maybe_rhs_val != null) break :div_by_zero; |
| 11125 | | |
| 11126 | | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 11183 | if (resolved_type.zigTypeTag() == .Vector) { |
| 11127 | 11184 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 11128 | 11185 | const zero = try sema.addConstant(resolved_type, zero_val); |
| 11129 | | const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); |
| 11186 | const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type)); |
| 11130 | 11187 | break :ok try block.addInst(.{ |
| 11131 | | .tag = if (is_int) .reduce else .reduce_optimized, |
| 11188 | .tag = .reduce, |
| 11132 | 11189 | .data = .{ .reduce = .{ |
| 11133 | | .operand = ok, |
| 11190 | .operand = eql, |
| 11134 | 11191 | .operation = .And, |
| 11135 | 11192 | } }, |
| 11136 | 11193 | }); |
| 11137 | | } else ok: { |
| 11194 | } else { |
| 11138 | 11195 | const zero = try sema.addConstant(resolved_type, Value.zero); |
| 11139 | | break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); |
| 11140 | | }; |
| 11141 | | try sema.addSafetyCheck(block, ok, .divide_by_zero); |
| 11196 | const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); |
| 11197 | break :ok is_in_range; |
| 11198 | } |
| 11199 | }; |
| 11200 | try sema.addSafetyCheck(block, ok, .exact_division_remainder); |
| 11201 | return result; |
| 11202 | } |
| 11203 | |
| 11204 | return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); |
| 11205 | } |
| 11206 | |
| 11207 | fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11208 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 11209 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; |
| 11210 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 11211 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 11212 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 11213 | const lhs = try sema.resolveInst(extra.lhs); |
| 11214 | const rhs = try sema.resolveInst(extra.rhs); |
| 11215 | const lhs_ty = sema.typeOf(lhs); |
| 11216 | const rhs_ty = sema.typeOf(rhs); |
| 11217 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); |
| 11218 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); |
| 11219 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 11220 | try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_floor); |
| 11221 | |
| 11222 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 11223 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| 11224 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, |
| 11225 | }); |
| 11226 | |
| 11227 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 11228 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 11229 | |
| 11230 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 11231 | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 11232 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11233 | |
| 11234 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 11235 | |
| 11236 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); |
| 11237 | |
| 11238 | const mod = sema.mod; |
| 11239 | const target = mod.getTarget(); |
| 11240 | const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); |
| 11241 | const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); |
| 11242 | |
| 11243 | const runtime_src = rs: { |
| 11244 | // For integers: |
| 11245 | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11246 | // If the rhs is zero, compile error for division by zero. |
| 11247 | // If the rhs is undefined, compile error because there is a possible |
| 11248 | // value (zero) for which the division would be illegal behavior. |
| 11249 | // If the lhs is undefined: |
| 11250 | // * if lhs type is signed: |
| 11251 | // * if rhs is comptime-known and not -1, result is undefined |
| 11252 | // * if rhs is -1 or runtime-known, compile error because there is a |
| 11253 | // possible value (-min_int / -1) for which division would be |
| 11254 | // illegal behavior. |
| 11255 | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 11256 | // TODO: emit runtime safety for division by zero |
| 11257 | // |
| 11258 | // For floats: |
| 11259 | // If the rhs is zero, compile error for division by zero. |
| 11260 | // If the rhs is undefined, compile error because there is a possible |
| 11261 | // value (zero) for which the division would be illegal behavior. |
| 11262 | // If the lhs is undefined, result is undefined. |
| 11263 | if (maybe_lhs_val) |lhs_val| { |
| 11264 | if (!lhs_val.isUndef()) { |
| 11265 | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11266 | return sema.addConstant(resolved_type, Value.zero); |
| 11267 | } |
| 11268 | } |
| 11269 | } |
| 11270 | if (maybe_rhs_val) |rhs_val| { |
| 11271 | if (rhs_val.isUndef()) { |
| 11272 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11273 | } |
| 11274 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11275 | return sema.failWithDivideByZero(block, rhs_src); |
| 11276 | } |
| 11277 | // TODO: if the RHS is one, return the LHS directly |
| 11142 | 11278 | } |
| 11279 | if (maybe_lhs_val) |lhs_val| { |
| 11280 | if (lhs_val.isUndef()) { |
| 11281 | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11282 | if (maybe_rhs_val) |rhs_val| { |
| 11283 | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11284 | return sema.addConstUndef(resolved_type); |
| 11285 | } |
| 11286 | } |
| 11287 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11288 | } |
| 11289 | return sema.addConstUndef(resolved_type); |
| 11290 | } |
| 11291 | |
| 11292 | if (maybe_rhs_val) |rhs_val| { |
| 11293 | if (is_int) { |
| 11294 | return sema.addConstant( |
| 11295 | resolved_type, |
| 11296 | try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target), |
| 11297 | ); |
| 11298 | } else { |
| 11299 | return sema.addConstant( |
| 11300 | resolved_type, |
| 11301 | try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target), |
| 11302 | ); |
| 11303 | } |
| 11304 | } else break :rs rhs_src; |
| 11305 | } else break :rs lhs_src; |
| 11306 | }; |
| 11307 | |
| 11308 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11309 | |
| 11310 | if (block.wantSafety()) { |
| 11311 | try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 11312 | try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 11143 | 11313 | } |
| 11144 | 11314 | |
| 11145 | | const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) { |
| 11146 | | .Optimized => Air.Inst.Tag.div_float_optimized, |
| 11147 | | .Strict => Air.Inst.Tag.div_float, |
| 11315 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); |
| 11316 | } |
| 11317 | |
| 11318 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11319 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 11320 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; |
| 11321 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 11322 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 11323 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 11324 | const lhs = try sema.resolveInst(extra.lhs); |
| 11325 | const rhs = try sema.resolveInst(extra.rhs); |
| 11326 | const lhs_ty = sema.typeOf(lhs); |
| 11327 | const rhs_ty = sema.typeOf(rhs); |
| 11328 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); |
| 11329 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); |
| 11330 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 11331 | try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_trunc); |
| 11332 | |
| 11333 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 11334 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| 11335 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, |
| 11336 | }); |
| 11337 | |
| 11338 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 11339 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 11340 | |
| 11341 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 11342 | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 11343 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11344 | |
| 11345 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 11346 | |
| 11347 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); |
| 11348 | |
| 11349 | const mod = sema.mod; |
| 11350 | const target = mod.getTarget(); |
| 11351 | const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); |
| 11352 | const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); |
| 11353 | |
| 11354 | const runtime_src = rs: { |
| 11355 | // For integers: |
| 11356 | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11357 | // If the rhs is zero, compile error for division by zero. |
| 11358 | // If the rhs is undefined, compile error because there is a possible |
| 11359 | // value (zero) for which the division would be illegal behavior. |
| 11360 | // If the lhs is undefined: |
| 11361 | // * if lhs type is signed: |
| 11362 | // * if rhs is comptime-known and not -1, result is undefined |
| 11363 | // * if rhs is -1 or runtime-known, compile error because there is a |
| 11364 | // possible value (-min_int / -1) for which division would be |
| 11365 | // illegal behavior. |
| 11366 | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 11367 | // TODO: emit runtime safety for division by zero |
| 11368 | // |
| 11369 | // For floats: |
| 11370 | // If the rhs is zero, compile error for division by zero. |
| 11371 | // If the rhs is undefined, compile error because there is a possible |
| 11372 | // value (zero) for which the division would be illegal behavior. |
| 11373 | // If the lhs is undefined, result is undefined. |
| 11374 | if (maybe_lhs_val) |lhs_val| { |
| 11375 | if (!lhs_val.isUndef()) { |
| 11376 | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11377 | return sema.addConstant(resolved_type, Value.zero); |
| 11378 | } |
| 11379 | } |
| 11380 | } |
| 11381 | if (maybe_rhs_val) |rhs_val| { |
| 11382 | if (rhs_val.isUndef()) { |
| 11383 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11384 | } |
| 11385 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11386 | return sema.failWithDivideByZero(block, rhs_src); |
| 11387 | } |
| 11388 | } |
| 11389 | if (maybe_lhs_val) |lhs_val| { |
| 11390 | if (lhs_val.isUndef()) { |
| 11391 | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11392 | if (maybe_rhs_val) |rhs_val| { |
| 11393 | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11394 | return sema.addConstUndef(resolved_type); |
| 11395 | } |
| 11396 | } |
| 11397 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11398 | } |
| 11399 | return sema.addConstUndef(resolved_type); |
| 11400 | } |
| 11401 | |
| 11402 | if (maybe_rhs_val) |rhs_val| { |
| 11403 | if (is_int) { |
| 11404 | return sema.addConstant( |
| 11405 | resolved_type, |
| 11406 | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11407 | ); |
| 11408 | } else { |
| 11409 | return sema.addConstant( |
| 11410 | resolved_type, |
| 11411 | try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target), |
| 11412 | ); |
| 11413 | } |
| 11414 | } else break :rs rhs_src; |
| 11415 | } else break :rs lhs_src; |
| 11148 | 11416 | }; |
| 11149 | | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 11417 | |
| 11418 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11419 | |
| 11420 | if (block.wantSafety()) { |
| 11421 | try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 11422 | try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 11423 | } |
| 11424 | |
| 11425 | return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); |
| 11426 | } |
| 11427 | |
| 11428 | fn addDivIntOverflowSafety( |
| 11429 | sema: *Sema, |
| 11430 | block: *Block, |
| 11431 | resolved_type: Type, |
| 11432 | lhs_scalar_ty: Type, |
| 11433 | maybe_lhs_val: ?Value, |
| 11434 | maybe_rhs_val: ?Value, |
| 11435 | casted_lhs: Air.Inst.Ref, |
| 11436 | casted_rhs: Air.Inst.Ref, |
| 11437 | is_int: bool, |
| 11438 | ) CompileError!void { |
| 11439 | if (!is_int) return; |
| 11440 | |
| 11441 | // If the LHS is unsigned, it cannot cause overflow. |
| 11442 | if (!lhs_scalar_ty.isSignedInt()) return; |
| 11443 | |
| 11444 | const mod = sema.mod; |
| 11445 | const target = mod.getTarget(); |
| 11446 | |
| 11447 | // If the LHS is widened to a larger integer type, no overflow is possible. |
| 11448 | if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) { |
| 11449 | return; |
| 11450 | } |
| 11451 | |
| 11452 | const min_int = try resolved_type.minInt(sema.arena, target); |
| 11453 | const neg_one = try Value.Tag.int_i64.create(sema.arena, -1); |
| 11454 | |
| 11455 | // If the LHS is comptime-known to be not equal to the min int, |
| 11456 | // no overflow is possible. |
| 11457 | if (maybe_lhs_val) |lhs_val| { |
| 11458 | if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) return; |
| 11459 | } |
| 11460 | |
| 11461 | // If the RHS is comptime-known to not be equal to -1, no overflow is possible. |
| 11462 | if (maybe_rhs_val) |rhs_val| { |
| 11463 | if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) return; |
| 11464 | } |
| 11465 | |
| 11466 | var ok: Air.Inst.Ref = .none; |
| 11467 | if (resolved_type.zigTypeTag() == .Vector) { |
| 11468 | const vector_ty_ref = try sema.addType(resolved_type); |
| 11469 | if (maybe_lhs_val == null) { |
| 11470 | const min_int_ref = try sema.addConstant( |
| 11471 | resolved_type, |
| 11472 | try Value.Tag.repeated.create(sema.arena, min_int), |
| 11473 | ); |
| 11474 | ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref); |
| 11475 | } |
| 11476 | if (maybe_rhs_val == null) { |
| 11477 | const neg_one_ref = try sema.addConstant( |
| 11478 | resolved_type, |
| 11479 | try Value.Tag.repeated.create(sema.arena, neg_one), |
| 11480 | ); |
| 11481 | const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref); |
| 11482 | if (ok == .none) { |
| 11483 | ok = rhs_ok; |
| 11484 | } else { |
| 11485 | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11486 | } |
| 11487 | } |
| 11488 | assert(ok != .none); |
| 11489 | ok = try block.addInst(.{ |
| 11490 | .tag = .reduce, |
| 11491 | .data = .{ .reduce = .{ |
| 11492 | .operand = ok, |
| 11493 | .operation = .And, |
| 11494 | } }, |
| 11495 | }); |
| 11496 | } else { |
| 11497 | if (maybe_lhs_val == null) { |
| 11498 | const min_int_ref = try sema.addConstant(resolved_type, min_int); |
| 11499 | ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); |
| 11500 | } |
| 11501 | if (maybe_rhs_val == null) { |
| 11502 | const neg_one_ref = try sema.addConstant(resolved_type, neg_one); |
| 11503 | const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); |
| 11504 | if (ok == .none) { |
| 11505 | ok = rhs_ok; |
| 11506 | } else { |
| 11507 | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11508 | } |
| 11509 | } |
| 11510 | assert(ok != .none); |
| 11511 | } |
| 11512 | try sema.addSafetyCheck(block, ok, .integer_overflow); |
| 11513 | } |
| 11514 | |
| 11515 | fn addDivByZeroSafety( |
| 11516 | sema: *Sema, |
| 11517 | block: *Block, |
| 11518 | resolved_type: Type, |
| 11519 | maybe_rhs_val: ?Value, |
| 11520 | casted_rhs: Air.Inst.Ref, |
| 11521 | is_int: bool, |
| 11522 | ) CompileError!void { |
| 11523 | // Strict IEEE floats have well-defined division by zero. |
| 11524 | if (!is_int and block.float_mode == .Strict) return; |
| 11525 | |
| 11526 | // If rhs was comptime-known to be zero a compile error would have been |
| 11527 | // emitted above. |
| 11528 | if (maybe_rhs_val != null) return; |
| 11529 | |
| 11530 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 11531 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 11532 | const zero = try sema.addConstant(resolved_type, zero_val); |
| 11533 | const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); |
| 11534 | break :ok try block.addInst(.{ |
| 11535 | .tag = if (is_int) .reduce else .reduce_optimized, |
| 11536 | .data = .{ .reduce = .{ |
| 11537 | .operand = ok, |
| 11538 | .operation = .And, |
| 11539 | } }, |
| 11540 | }); |
| 11541 | } else ok: { |
| 11542 | const zero = try sema.addConstant(resolved_type, Value.zero); |
| 11543 | break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); |
| 11544 | }; |
| 11545 | try sema.addSafetyCheck(block, ok, .divide_by_zero); |
| 11150 | 11546 | } |
| 11151 | 11547 | |
| 11152 | 11548 | fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { |
| ... | ... | @@ -11423,13 +11819,8 @@ fn analyzeArithmetic( |
| 11423 | 11819 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11424 | 11820 | |
| 11425 | 11821 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 11426 | | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; |
| 11427 | 11822 | |
| 11428 | | if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { |
| 11429 | | return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ |
| 11430 | | @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), |
| 11431 | | }); |
| 11432 | | } |
| 11823 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); |
| 11433 | 11824 | |
| 11434 | 11825 | const mod = sema.mod; |
| 11435 | 11826 | const target = mod.getTarget(); |
| ... | ... | @@ -11636,187 +12027,6 @@ fn analyzeArithmetic( |
| 11636 | 12027 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; |
| 11637 | 12028 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; |
| 11638 | 12029 | }, |
| 11639 | | .div_trunc => { |
| 11640 | | // For integers: |
| 11641 | | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11642 | | // If the rhs is zero, compile error for division by zero. |
| 11643 | | // If the rhs is undefined, compile error because there is a possible |
| 11644 | | // value (zero) for which the division would be illegal behavior. |
| 11645 | | // If the lhs is undefined: |
| 11646 | | // * if lhs type is signed: |
| 11647 | | // * if rhs is comptime-known and not -1, result is undefined |
| 11648 | | // * if rhs is -1 or runtime-known, compile error because there is a |
| 11649 | | // possible value (-min_int / -1) for which division would be |
| 11650 | | // illegal behavior. |
| 11651 | | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 11652 | | // TODO: emit runtime safety for division by zero |
| 11653 | | // |
| 11654 | | // For floats: |
| 11655 | | // If the rhs is zero, compile error for division by zero. |
| 11656 | | // If the rhs is undefined, compile error because there is a possible |
| 11657 | | // value (zero) for which the division would be illegal behavior. |
| 11658 | | // If the lhs is undefined, result is undefined. |
| 11659 | | if (maybe_lhs_val) |lhs_val| { |
| 11660 | | if (!lhs_val.isUndef()) { |
| 11661 | | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11662 | | return sema.addConstant(resolved_type, Value.zero); |
| 11663 | | } |
| 11664 | | } |
| 11665 | | } |
| 11666 | | if (maybe_rhs_val) |rhs_val| { |
| 11667 | | if (rhs_val.isUndef()) { |
| 11668 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11669 | | } |
| 11670 | | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11671 | | return sema.failWithDivideByZero(block, rhs_src); |
| 11672 | | } |
| 11673 | | } |
| 11674 | | const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_trunc_optimized else .div_trunc; |
| 11675 | | if (maybe_lhs_val) |lhs_val| { |
| 11676 | | if (lhs_val.isUndef()) { |
| 11677 | | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11678 | | if (maybe_rhs_val) |rhs_val| { |
| 11679 | | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11680 | | return sema.addConstUndef(resolved_type); |
| 11681 | | } |
| 11682 | | } |
| 11683 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11684 | | } |
| 11685 | | return sema.addConstUndef(resolved_type); |
| 11686 | | } |
| 11687 | | |
| 11688 | | if (maybe_rhs_val) |rhs_val| { |
| 11689 | | if (is_int) { |
| 11690 | | return sema.addConstant( |
| 11691 | | resolved_type, |
| 11692 | | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11693 | | ); |
| 11694 | | } else { |
| 11695 | | return sema.addConstant( |
| 11696 | | resolved_type, |
| 11697 | | try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target), |
| 11698 | | ); |
| 11699 | | } |
| 11700 | | } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; |
| 11701 | | } else break :rs .{ .src = lhs_src, .air_tag = air_tag }; |
| 11702 | | }, |
| 11703 | | .div_floor => { |
| 11704 | | // For integers: |
| 11705 | | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11706 | | // If the rhs is zero, compile error for division by zero. |
| 11707 | | // If the rhs is undefined, compile error because there is a possible |
| 11708 | | // value (zero) for which the division would be illegal behavior. |
| 11709 | | // If the lhs is undefined: |
| 11710 | | // * if lhs type is signed: |
| 11711 | | // * if rhs is comptime-known and not -1, result is undefined |
| 11712 | | // * if rhs is -1 or runtime-known, compile error because there is a |
| 11713 | | // possible value (-min_int / -1) for which division would be |
| 11714 | | // illegal behavior. |
| 11715 | | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 11716 | | // TODO: emit runtime safety for division by zero |
| 11717 | | // |
| 11718 | | // For floats: |
| 11719 | | // If the rhs is zero, compile error for division by zero. |
| 11720 | | // If the rhs is undefined, compile error because there is a possible |
| 11721 | | // value (zero) for which the division would be illegal behavior. |
| 11722 | | // If the lhs is undefined, result is undefined. |
| 11723 | | if (maybe_lhs_val) |lhs_val| { |
| 11724 | | if (!lhs_val.isUndef()) { |
| 11725 | | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11726 | | return sema.addConstant(resolved_type, Value.zero); |
| 11727 | | } |
| 11728 | | } |
| 11729 | | } |
| 11730 | | if (maybe_rhs_val) |rhs_val| { |
| 11731 | | if (rhs_val.isUndef()) { |
| 11732 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11733 | | } |
| 11734 | | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11735 | | return sema.failWithDivideByZero(block, rhs_src); |
| 11736 | | } |
| 11737 | | } |
| 11738 | | const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_floor_optimized else .div_floor; |
| 11739 | | if (maybe_lhs_val) |lhs_val| { |
| 11740 | | if (lhs_val.isUndef()) { |
| 11741 | | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11742 | | if (maybe_rhs_val) |rhs_val| { |
| 11743 | | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11744 | | return sema.addConstUndef(resolved_type); |
| 11745 | | } |
| 11746 | | } |
| 11747 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11748 | | } |
| 11749 | | return sema.addConstUndef(resolved_type); |
| 11750 | | } |
| 11751 | | |
| 11752 | | if (maybe_rhs_val) |rhs_val| { |
| 11753 | | if (is_int) { |
| 11754 | | return sema.addConstant( |
| 11755 | | resolved_type, |
| 11756 | | try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target), |
| 11757 | | ); |
| 11758 | | } else { |
| 11759 | | return sema.addConstant( |
| 11760 | | resolved_type, |
| 11761 | | try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target), |
| 11762 | | ); |
| 11763 | | } |
| 11764 | | } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; |
| 11765 | | } else break :rs .{ .src = lhs_src, .air_tag = air_tag }; |
| 11766 | | }, |
| 11767 | | .div_exact => { |
| 11768 | | // For integers: |
| 11769 | | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11770 | | // If the rhs is zero, compile error for division by zero. |
| 11771 | | // If the rhs is undefined, compile error because there is a possible |
| 11772 | | // value (zero) for which the division would be illegal behavior. |
| 11773 | | // If the lhs is undefined, compile error because there is a possible |
| 11774 | | // value for which the division would result in a remainder. |
| 11775 | | // TODO: emit runtime safety for if there is a remainder |
| 11776 | | // TODO: emit runtime safety for division by zero |
| 11777 | | // |
| 11778 | | // For floats: |
| 11779 | | // If the rhs is zero, compile error for division by zero. |
| 11780 | | // If the rhs is undefined, compile error because there is a possible |
| 11781 | | // value (zero) for which the division would be illegal behavior. |
| 11782 | | // If the lhs is undefined, compile error because there is a possible |
| 11783 | | // value for which the division would result in a remainder. |
| 11784 | | if (maybe_lhs_val) |lhs_val| { |
| 11785 | | if (lhs_val.isUndef()) { |
| 11786 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11787 | | } else { |
| 11788 | | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11789 | | return sema.addConstant(resolved_type, Value.zero); |
| 11790 | | } |
| 11791 | | } |
| 11792 | | } |
| 11793 | | if (maybe_rhs_val) |rhs_val| { |
| 11794 | | if (rhs_val.isUndef()) { |
| 11795 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11796 | | } |
| 11797 | | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11798 | | return sema.failWithDivideByZero(block, rhs_src); |
| 11799 | | } |
| 11800 | | } |
| 11801 | | const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_exact_optimized else .div_exact; |
| 11802 | | if (maybe_lhs_val) |lhs_val| { |
| 11803 | | if (maybe_rhs_val) |rhs_val| { |
| 11804 | | if (is_int) { |
| 11805 | | // TODO: emit compile error if there is a remainder |
| 11806 | | return sema.addConstant( |
| 11807 | | resolved_type, |
| 11808 | | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11809 | | ); |
| 11810 | | } else { |
| 11811 | | // TODO: emit compile error if there is a remainder |
| 11812 | | return sema.addConstant( |
| 11813 | | resolved_type, |
| 11814 | | try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), |
| 11815 | | ); |
| 11816 | | } |
| 11817 | | } else break :rs .{ .src = rhs_src, .air_tag = air_tag }; |
| 11818 | | } else break :rs .{ .src = lhs_src, .air_tag = air_tag }; |
| 11819 | | }, |
| 11820 | 12030 | .mul => { |
| 11821 | 12031 | // For integers: |
| 11822 | 12032 | // If either of the operands are zero, the result is zero. |
| ... | ... | @@ -12195,28 +12405,6 @@ fn analyzeArithmetic( |
| 12195 | 12405 | } |
| 12196 | 12406 | } |
| 12197 | 12407 | switch (rs.air_tag) { |
| 12198 | | // zig fmt: off |
| 12199 | | .div_float, .div_exact, .div_trunc, .div_floor, .div_float_optimized, |
| 12200 | | .div_exact_optimized, .div_trunc_optimized, .div_floor_optimized |
| 12201 | | // zig fmt: on |
| 12202 | | => if (scalar_tag == .Int or block.float_mode == .Optimized) { |
| 12203 | | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 12204 | | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 12205 | | const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val); |
| 12206 | | const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); |
| 12207 | | break :ok try block.addInst(.{ |
| 12208 | | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 12209 | | .data = .{ .reduce = .{ |
| 12210 | | .operand = ok, |
| 12211 | | .operation = .And, |
| 12212 | | } }, |
| 12213 | | }); |
| 12214 | | } else ok: { |
| 12215 | | const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero); |
| 12216 | | break :ok try block.addBinOp(if (block.float_mode == .Optimized) .cmp_neq_optimized else .cmp_neq, casted_rhs, zero); |
| 12217 | | }; |
| 12218 | | try sema.addSafetyCheck(block, ok, .divide_by_zero); |
| 12219 | | }, |
| 12220 | 12408 | .rem, .mod, .rem_optimized, .mod_optimized => { |
| 12221 | 12409 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 12222 | 12410 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| ... | ... | @@ -12243,47 +12431,6 @@ fn analyzeArithmetic( |
| 12243 | 12431 | }, |
| 12244 | 12432 | else => {}, |
| 12245 | 12433 | } |
| 12246 | | if (rs.air_tag == .div_exact or rs.air_tag == .div_exact_optimized) { |
| 12247 | | const result = try block.addBinOp(.div_exact, casted_lhs, casted_rhs); |
| 12248 | | const ok = if (scalar_tag == .Float) ok: { |
| 12249 | | const floored = try block.addUnOp(.floor, result); |
| 12250 | | |
| 12251 | | if (resolved_type.zigTypeTag() == .Vector) { |
| 12252 | | const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type)); |
| 12253 | | break :ok try block.addInst(.{ |
| 12254 | | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 12255 | | .data = .{ .reduce = .{ |
| 12256 | | .operand = eql, |
| 12257 | | .operation = .And, |
| 12258 | | } }, |
| 12259 | | }); |
| 12260 | | } else { |
| 12261 | | const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, result, floored); |
| 12262 | | break :ok is_in_range; |
| 12263 | | } |
| 12264 | | } else ok: { |
| 12265 | | const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); |
| 12266 | | |
| 12267 | | if (resolved_type.zigTypeTag() == .Vector) { |
| 12268 | | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 12269 | | const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val); |
| 12270 | | const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type)); |
| 12271 | | break :ok try block.addInst(.{ |
| 12272 | | .tag = .reduce, |
| 12273 | | .data = .{ .reduce = .{ |
| 12274 | | .operand = eql, |
| 12275 | | .operation = .And, |
| 12276 | | } }, |
| 12277 | | }); |
| 12278 | | } else { |
| 12279 | | const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero); |
| 12280 | | const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, remainder, zero); |
| 12281 | | break :ok is_in_range; |
| 12282 | | } |
| 12283 | | }; |
| 12284 | | try sema.addSafetyCheck(block, ok, .exact_division_remainder); |
| 12285 | | return result; |
| 12286 | | } |
| 12287 | 12434 | } |
| 12288 | 12435 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); |
| 12289 | 12436 | } |