| ... | @@ -875,10 +875,6 @@ fn analyzeBodyInner( | ... | @@ -875,10 +875,6 @@ fn analyzeBodyInner( |
| 875 | .add => try sema.zirArithmetic(block, inst, .add), | 875 | .add => try sema.zirArithmetic(block, inst, .add), |
| 876 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), | 876 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), |
| 877 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), | 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 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), | 878 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), |
| 883 | .mod => try sema.zirArithmetic(block, inst, .mod), | 879 | .mod => try sema.zirArithmetic(block, inst, .mod), |
| 884 | .rem => try sema.zirArithmetic(block, inst, .rem), | 880 | .rem => try sema.zirArithmetic(block, inst, .rem), |
| ... | @@ -889,6 +885,11 @@ fn analyzeBodyInner( | ... | @@ -889,6 +885,11 @@ fn analyzeBodyInner( |
| 889 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), | 885 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), |
| 890 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), | 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 | .maximum => try sema.zirMinMax(block, inst, .max), | 893 | .maximum => try sema.zirMinMax(block, inst, .max), |
| 893 | .minimum => try sema.zirMinMax(block, inst, .min), | 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,6 +11000,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 10999 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { | 11000 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11000 | return sema.failWithDivideByZero(block, rhs_src); | 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 | else => {}, | 11006 | else => {}, |
| ... | @@ -11041,112 +11043,506 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins | ... | @@ -11041,112 +11043,506 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 11041 | try sema.requireRuntimeBlock(block, src, runtime_src); | 11043 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11042 | | 11044 | |
| 11043 | if (block.wantSafety()) { | 11045 | if (block.wantSafety()) { |
| 11044 | int_overflow: { | 11046 | try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 11045 | if (!is_int) break :int_overflow; | 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. | 11050 | const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) { |
| 11048 | if (!lhs_scalar_ty.isSignedInt()) break :int_overflow; | 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. | 11057 | fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11051 | if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) { | 11058 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 11052 | break :int_overflow; | 11059 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; |
| 11053 | } | 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); | 11072 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 11056 | const neg_one = try Value.Tag.int_i64.create(sema.arena, -1); | 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, | 11077 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 11059 | // no overflow is possible. | 11078 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 11060 | if (maybe_lhs_val) |lhs_val| { | | |
| 11061 | if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) break :int_overflow; | | |
| 11062 | } | | |
| 11063 | | 11079 | |
| 11064 | // If the RHS is comptime-known to not be equal to -1, no overflow is possible. | 11080 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 11065 | if (maybe_rhs_val) |rhs_val| { | 11081 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11066 | if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) break :int_overflow; | | |
| 11067 | } | | |
| 11068 | | 11082 | |
| 11069 | var ok: Air.Inst.Ref = .none; | 11083 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 11070 | if (resolved_type.zigTypeTag() == .Vector) { | 11084 | |
| 11071 | const vector_ty_ref = try sema.addType(resolved_type); | 11085 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); |
| 11072 | if (maybe_lhs_val == null) { | 11086 | |
| 11073 | const min_int_ref = try sema.addConstant( | 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 | resolved_type, | 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); | 11135 | } else { |
| 11078 | } | 11136 | // TODO: emit compile error if there is a remainder |
| 11079 | if (maybe_rhs_val == null) { | 11137 | return sema.addConstant( |
| 11080 | const neg_one_ref = try sema.addConstant( | | |
| 11081 | resolved_type, | 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); | 11142 | } else break :rs rhs_src; |
| 11092 | ok = try block.addInst(.{ | 11143 | } else break :rs lhs_src; |
| 11093 | .tag = .reduce, | 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 | .data = .{ .reduce = .{ | 11168 | .data = .{ .reduce = .{ |
| 11095 | .operand = ok, | 11169 | .operand = eql, |
| 11096 | .operation = .And, | 11170 | .operation = .And, |
| 11097 | } }, | 11171 | } }, |
| 11098 | }); | 11172 | }); |
| 11099 | } else { | 11173 | } else { |
| 11100 | if (maybe_lhs_val == null) { | 11174 | const is_in_range = try block.addBinOp(switch (block.float_mode) { |
| 11101 | const min_int_ref = try sema.addConstant(resolved_type, min_int); | 11175 | .Strict => .cmp_eq, |
| 11102 | ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); | 11176 | .Optimized => .cmp_eq_optimized, |
| 11103 | } | 11177 | }, result, floored); |
| 11104 | if (maybe_rhs_val == null) { | 11178 | break :ok is_in_range; |
| 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); | | |
| 11114 | } | 11179 | } |
| 11115 | try sema.addSafetyCheck(block, ok, .integer_overflow); | 11180 | } else ok: { |
| 11116 | } | 11181 | const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); |
| 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; | | |
| 11121 | | 11182 | |
| 11122 | // If rhs was comptime-known to be zero a compile error would have been | 11183 | if (resolved_type.zigTypeTag() == .Vector) { |
| 11123 | // emitted above. | | |
| 11124 | if (maybe_rhs_val != null) break :div_by_zero; | | |
| 11125 | | | |
| 11126 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { | | |
| 11127 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); | 11184 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 11128 | const zero = try sema.addConstant(resolved_type, zero_val); | 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 | break :ok try block.addInst(.{ | 11187 | break :ok try block.addInst(.{ |
| 11131 | .tag = if (is_int) .reduce else .reduce_optimized, | 11188 | .tag = .reduce, |
| 11132 | .data = .{ .reduce = .{ | 11189 | .data = .{ .reduce = .{ |
| 11133 | .operand = ok, | 11190 | .operand = eql, |
| 11134 | .operation = .And, | 11191 | .operation = .And, |
| 11135 | } }, | 11192 | } }, |
| 11136 | }); | 11193 | }); |
| 11137 | } else ok: { | 11194 | } else { |
| 11138 | const zero = try sema.addConstant(resolved_type, Value.zero); | 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); | 11196 | const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); |
| 11140 | }; | 11197 | break :ok is_in_range; |
| 11141 | try sema.addSafetyCheck(block, ok, .divide_by_zero); | 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) { | 11315 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); |
| 11146 | .Optimized => Air.Inst.Tag.div_float_optimized, | 11316 | } |
| 11147 | .Strict => Air.Inst.Tag.div_float, | 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 | fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { | 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,13 +11819,8 @@ fn analyzeArithmetic( |
| 11423 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); | 11819 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11424 | | 11820 | |
| 11425 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | 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))) { | 11823 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, 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 | } | | |
| 11433 | | 11824 | |
| 11434 | const mod = sema.mod; | 11825 | const mod = sema.mod; |
| 11435 | const target = mod.getTarget(); | 11826 | const target = mod.getTarget(); |
| ... | @@ -11636,187 +12027,6 @@ fn analyzeArithmetic( | ... | @@ -11636,187 +12027,6 @@ fn analyzeArithmetic( |
| 11636 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; | 12027 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; |
| 11637 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; | 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 | .mul => { | 12030 | .mul => { |
| 11821 | // For integers: | 12031 | // For integers: |
| 11822 | // If either of the operands are zero, the result is zero. | 12032 | // If either of the operands are zero, the result is zero. |
| ... | @@ -12195,28 +12405,6 @@ fn analyzeArithmetic( | ... | @@ -12195,28 +12405,6 @@ fn analyzeArithmetic( |
| 12195 | } | 12405 | } |
| 12196 | } | 12406 | } |
| 12197 | switch (rs.air_tag) { | 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 | .rem, .mod, .rem_optimized, .mod_optimized => { | 12408 | .rem, .mod, .rem_optimized, .mod_optimized => { |
| 12221 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { | 12409 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 12222 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); | 12410 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| ... | @@ -12243,47 +12431,6 @@ fn analyzeArithmetic( | ... | @@ -12243,47 +12431,6 @@ fn analyzeArithmetic( |
| 12243 | }, | 12431 | }, |
| 12244 | else => {}, | 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 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | 12435 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); |
| 12289 | } | 12436 | } |