authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-04 18:31:41+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-06 20:13:46+02:00
logf810faa828de0ac1704e2c87f9fd467af16b564b
treef27211eff53a21116febf028e5faae0ccbaa7c81
parentc5af4c75da81616eadce8cf866aac86fb9e4ba9d

compiler: implement @divCeil builtin

Co-authored-by: riverbl <94326797+riverbl@users.noreply.github.com> Co-authored-by: Ratakor <ratakor@disroot.org>

28 files changed, 822 insertions(+), 13 deletions(-)

doc/langref.html.in+20-4
...@@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}</pre></td>...@@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}</pre></td>
1370 <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li>1370 <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li>
1371 <li>Signed integer operands must be comptime-known and positive. In other cases, use1371 <li>Signed integer operands must be comptime-known and positive. In other cases, use
1372 {#link|@divTrunc#},1372 {#link|@divTrunc#},
1373 {#link|@divFloor#}, or1373 {#link|@divFloor#},
1374 {#link|@divCeil#}, or
1374 {#link|@divExact#} instead.1375 {#link|@divExact#} instead.
1375 </li>1376 </li>
1376 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>1377 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
...@@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4735 <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>4736 <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>
4736 </ul>4737 </ul>
4737 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p>4738 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p>
4738 {#see_also|@divTrunc|@divFloor#}4739 {#see_also|@divTrunc|@divFloor|@divCeil#}
4739 {#header_close#}4740 {#header_close#}
4740 {#header_open|@divFloor#}4741 {#header_open|@divFloor#}
4741 <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre>4742 <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre>
...@@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4749 <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>4750 <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>
4750 </ul>4751 </ul>
4751 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p>4752 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p>
4752 {#see_also|@divTrunc|@divExact#}4753 {#see_also|@divTrunc|@divCeil|@divExact#}
4753 {#header_close#}4754 {#header_close#}
4754 {#header_open|@divTrunc#}4755 {#header_open|@divTrunc#}
4755 <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre>4756 <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre>
...@@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4763 <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>4764 <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>
4764 </ul>4765 </ul>
4765 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p>4766 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p>
4766 {#see_also|@divFloor|@divExact#}4767 {#see_also|@divFloor|@divCeil|@divExact#}
4768 {#header_close#}
4769 {#header_open|@divCeil#}
4770 <pre>{#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}</pre>
4771 <p>
4772 Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and
4773 {#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}.
4774 </p>
4775 <ul>
4776 <li>{#syntax#}@divCeil(5, 3) == 2{#endsyntax#}</li>
4777 <li>{#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}</li>
4778 </ul>
4779 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.</p>
4780 {#see_also|@divFloor|@divTrunc|@divExact#}
4767 {#header_close#}4781 {#header_close#}
47684782
4769 {#header_open|@embedFile#}4783 {#header_open|@embedFile#}
...@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
6095 <li>{#syntax#}/{#endsyntax#} (division)</li>6109 <li>{#syntax#}/{#endsyntax#} (division)</li>
6096 <li>{#link|@divTrunc#} (division)</li>6110 <li>{#link|@divTrunc#} (division)</li>
6097 <li>{#link|@divFloor#} (division)</li>6111 <li>{#link|@divFloor#} (division)</li>
6112 <li>{#link|@divCeil#} (division)</li>
6098 <li>{#link|@divExact#} (division)</li>6113 <li>{#link|@divExact#} (division)</li>
6099 </ul>6114 </ul>
6100 <p>Example with addition at compile-time:</p>6115 <p>Example with addition at compile-time:</p>
...@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
6112 <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>6127 <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>
6113 <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>6128 <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>
6114 <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>6129 <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>
6130 <li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li>
6115 <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>6131 <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>
6116 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>6132 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
6117 </ul>6133 </ul>
lib/std/math/big/int.zig+66
...@@ -1221,6 +1221,72 @@ pub const Mutable = struct {...@@ -1221,6 +1221,72 @@ pub const Mutable = struct {
1221 }1221 }
1222 }1222 }
12231223
1224 /// q = a / b (rem r)
1225 ///
1226 /// a / b are ceiled (rounded towards +inf).
1227 /// q may alias with a or b.
1228 ///
1229 /// Asserts there is enough memory to store q and r.
1230 /// The upper bound for r limb count is `b.limbs.len`.
1231 /// The upper bound for q limb count is given by `a.limbs`.
1232 ///
1233 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
1234 pub fn divCeil(
1235 q: *Mutable,
1236 r: *Mutable,
1237 a: Const,
1238 b: Const,
1239 limbs_buffer: []Limb,
1240 ) void {
1241 const sep = a.limbs.len + 2;
1242 var x = a.toMutable(limbs_buffer[0..sep]);
1243 var y = b.toMutable(limbs_buffer[sep..]);
1244
1245 // div performs truncating division (@divTrunc) which rounds towards negative
1246 // infinity if the result is positive and towards positive infinity if the result is
1247 // negative.
1248 div(q, r, &x, &y);
1249
1250 // @rem gives the remainder after @divTrunc, and is defined by:
1251 // x * @divTrunc(x, y) + @rem(x, y) = x
1252 // For all integers x, y with y != 0.
1253 // In the following comments, a, b will be integers with a >= 0, b > 0, and we will take
1254 // modCeil to be the remainder after @divCeil, defined by:
1255 // x * @divCeil(x, y) + modCeil(x, y) = x
1256 // For all integers x, y with y != 0.
1257
1258 if (a.positive != b.positive or r.eqlZero()) {
1259 // In this case either the result is negative or the remainder is 0.
1260 // If the result is negative then the default truncating division already rounds
1261 // towards positive infinity, so no adjustment is needed.
1262 // If the remainder is 0 then the division is exact and no adjustment is needed.
1263 } else if (a.positive) {
1264 // Both positive.
1265 // We have:
1266 // modCeil(a, b) != 0
1267 // => @divCeil(a, b) = @divTrunc(a, b) + 1
1268 // And:
1269 // b * @divTrunc(a, b) + @rem(a, b) = a
1270 // b * @divCeil(a, b) + modCeil(a, b) = a
1271 // => b * @divTrunc(a, b) + b + modCeil(a, b) = a
1272 // => modCeil(a, b) = @rem(a, b) - b
1273 q.addScalar(q.toConst(), 1);
1274 r.sub(r.toConst(), y.toConst());
1275 } else {
1276 // Both negative.
1277 // We have:
1278 // modCeil(-a, -b) != 0
1279 // => @divCeil(-a, -b) = @divTrunc(-a, -b) + 1
1280 // And:
1281 // -b * @divTrunc(-a, -b) + @rem(-a, -b) = -a
1282 // -b * @divCeil(-a, -b) + modCeil(-a, -b) = -a
1283 // => -b * @divTrunc(-a, -b) - b + modCeil(-a, -b) = -a
1284 // => modCeil(-a, -b) = @rem(-a, -b) + b
1285 q.addScalar(q.toConst(), 1);
1286 r.add(r.toConst(), y.toConst().abs());
1287 }
1288 }
1289
1224 /// q = a / b (rem r)1290 /// q = a / b (rem r)
1225 ///1291 ///
1226 /// a / b are truncated (rounded towards -inf).1292 /// a / b are truncated (rounded towards -inf).
lib/std/zig/AstGen.zig+2
...@@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2845 .bit_reverse,2845 .bit_reverse,
2846 .div_exact,2846 .div_exact,
2847 .div_floor,2847 .div_floor,
2848 .div_ceil,
2848 .div_trunc,2849 .div_trunc,
2849 .mod,2850 .mod,
2850 .rem,2851 .rem,
...@@ -9392,6 +9393,7 @@ fn builtinCall(...@@ -9392,6 +9393,7 @@ fn builtinCall(
93929393
9393 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),9394 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
9394 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),9395 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
9396 .div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil),
9395 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),9397 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
9396 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),9398 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
9397 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),9399 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
lib/std/zig/AstRlAnnotate.zig+1
...@@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
936 },936 },
937 .div_exact,937 .div_exact,
938 .div_floor,938 .div_floor,
939 .div_ceil,
939 .div_trunc,940 .div_trunc,
940 .mod,941 .mod,
941 .rem,942 .rem,
lib/std/zig/BuiltinFn.zig+8
...@@ -31,6 +31,7 @@ pub const Tag = enum {...@@ -31,6 +31,7 @@ pub const Tag = enum {
31 c_va_copy,31 c_va_copy,
32 c_va_end,32 c_va_end,
33 c_va_start,33 c_va_start,
34 div_ceil,
34 div_exact,35 div_exact,
35 div_floor,36 div_floor,
36 div_trunc,37 div_trunc,
...@@ -398,6 +399,13 @@ pub const list = list: {...@@ -398,6 +399,13 @@ pub const list = list: {
398 .param_count = 2,399 .param_count = 2,
399 },400 },
400 },401 },
402 .{
403 "@divCeil",
404 .{
405 .tag = .div_ceil,
406 .param_count = 2,
407 },
408 },
401 .{409 .{
402 "@divTrunc",410 "@divTrunc",
403 .{411 .{
lib/std/zig/Zir.zig+7
...@@ -200,6 +200,9 @@ pub const Inst = struct {...@@ -200,6 +200,9 @@ pub const Inst = struct {
200 /// Implements the `@divFloor` builtin.200 /// Implements the `@divFloor` builtin.
201 /// Uses the `pl_node` union field with payload `Bin`.201 /// Uses the `pl_node` union field with payload `Bin`.
202 div_floor,202 div_floor,
203 /// Implements the `@divCeil` builtin.
204 /// Uses the `pl_node` union field with payload `Bin`.
205 div_ceil,
203 /// Implements the `@divTrunc` builtin.206 /// Implements the `@divTrunc` builtin.
204 /// Uses the `pl_node` union field with payload `Bin`.207 /// Uses the `pl_node` union field with payload `Bin`.
205 div_trunc,208 div_trunc,
...@@ -1267,6 +1270,7 @@ pub const Inst = struct {...@@ -1267,6 +1270,7 @@ pub const Inst = struct {
1267 .bit_reverse,1270 .bit_reverse,
1268 .div_exact,1271 .div_exact,
1269 .div_floor,1272 .div_floor,
1273 .div_ceil,
1270 .div_trunc,1274 .div_trunc,
1271 .mod,1275 .mod,
1272 .rem,1276 .rem,
...@@ -1547,6 +1551,7 @@ pub const Inst = struct {...@@ -1547,6 +1551,7 @@ pub const Inst = struct {
1547 .bit_reverse,1551 .bit_reverse,
1548 .div_exact,1552 .div_exact,
1549 .div_floor,1553 .div_floor,
1554 .div_ceil,
1550 .div_trunc,1555 .div_trunc,
1551 .mod,1556 .mod,
1552 .rem,1557 .rem,
...@@ -1815,6 +1820,7 @@ pub const Inst = struct {...@@ -1815,6 +1820,7 @@ pub const Inst = struct {
18151820
1816 .div_exact = .pl_node,1821 .div_exact = .pl_node,
1817 .div_floor = .pl_node,1822 .div_floor = .pl_node,
1823 .div_ceil = .pl_node,
1818 .div_trunc = .pl_node,1824 .div_trunc = .pl_node,
1819 .mod = .pl_node,1825 .mod = .pl_node,
1820 .rem = .pl_node,1826 .rem = .pl_node,
...@@ -4115,6 +4121,7 @@ fn findTrackableInner(...@@ -4115,6 +4121,7 @@ fn findTrackableInner(
4115 .mul_sat,4121 .mul_sat,
4116 .div_exact,4122 .div_exact,
4117 .div_floor,4123 .div_floor,
4124 .div_ceil,
4118 .div_trunc,4125 .div_trunc,
4119 .mod,4126 .mod,
4120 .rem,4127 .rem,
lib/zig.h+32
...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \
815 } \815 } \
816\
817 static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
818 return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
819 } \
820\
821 static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
822 return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
823 ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
824 } \
816\825\
817 zig_basic_operator(uint##w##_t, mod_u##w, %) \826 zig_basic_operator(uint##w##_t, mod_u##w, %) \
818\827\
...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
2058 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));2067 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
2059}2068}
20602069
2070static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
2071 zig_u128 rem = zig_rem_u128(lhs, rhs);
2072 uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
2073 ? UINT64_C(1) : UINT64_C(0);
2074 return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
2075}
2076
2077static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
2078 zig_i128 rem = zig_rem_i128(lhs, rhs);
2079 int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
2080 ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
2081 : INT64_C(0);
2082 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
2083}
2084
2061#define zig_mod_u128 zig_rem_u1282085#define zig_mod_u128 zig_rem_u128
20622086
2063static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {2087static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs
3251 zig_trap();3275 zig_trap();
3252}3276}
32533277
3278static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3279 zig_trap();
3280}
3281
3254zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);3282zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
3255static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {3283static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3256 if (!is_signed) {3284 if (!is_signed) {
...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
4010 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \4038 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
4011 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \4039 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
4012 } \4040 } \
4041\
4042 static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
4043 return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
4044 } \
4013\4045\
4014 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \4046 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
4015 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \4047 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
src/Air.zig+11
...@@ -143,6 +143,13 @@ pub const Inst = struct {...@@ -143,6 +143,13 @@ pub const Inst = struct {
143 div_floor,143 div_floor,
144 /// Same as `div_floor` with optimized float mode.144 /// Same as `div_floor` with optimized float mode.
145 div_floor_optimized,145 div_floor_optimized,
146 /// Ceiling integer or float division. For integers, wrapping is illegal behavior.
147 /// Both operands are guaranteed to be the same type, and the result type
148 /// is the same as both operands.
149 /// Uses the `bin_op` field.
150 div_ceil,
151 /// Same as `div_ceil` with optimized float mode.
152 div_ceil_optimized,
146 /// Integer or float division.153 /// Integer or float division.
147 /// If a remainder would be produced, illegal behavior occurs.154 /// If a remainder would be produced, illegal behavior occurs.
148 /// For integers, overflow is illegal behavior.155 /// For integers, overflow is illegal behavior.
...@@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1605 .div_float,1612 .div_float,
1606 .div_trunc,1613 .div_trunc,
1607 .div_floor,1614 .div_floor,
1615 .div_ceil,
1608 .div_exact,1616 .div_exact,
1609 .rem,1617 .rem,
1610 .mod,1618 .mod,
...@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1624 .div_float_optimized,1632 .div_float_optimized,
1625 .div_trunc_optimized,1633 .div_trunc_optimized,
1626 .div_floor_optimized,1634 .div_floor_optimized,
1635 .div_ceil_optimized,
1627 .div_exact_optimized,1636 .div_exact_optimized,
1628 .rem_optimized,1637 .rem_optimized,
1629 .mod_optimized,1638 .mod_optimized,
...@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1985 .div_trunc_optimized,1994 .div_trunc_optimized,
1986 .div_floor,1995 .div_floor,
1987 .div_floor_optimized,1996 .div_floor_optimized,
1997 .div_ceil,
1998 .div_ceil_optimized,
1988 .div_exact,1999 .div_exact,
1989 .div_exact_optimized,2000 .div_exact_optimized,
1990 .rem,2001 .rem,
src/Air/Legalize.zig+193-2
...@@ -54,6 +54,8 @@ pub const Feature = enum {...@@ -54,6 +54,8 @@ pub const Feature = enum {
54 scalarize_div_trunc_optimized,54 scalarize_div_trunc_optimized,
55 scalarize_div_floor,55 scalarize_div_floor,
56 scalarize_div_floor_optimized,56 scalarize_div_floor_optimized,
57 scalarize_div_ceil,
58 scalarize_div_ceil_optimized,
57 scalarize_div_exact,59 scalarize_div_exact,
58 scalarize_div_exact_optimized,60 scalarize_div_exact_optimized,
59 scalarize_rem,61 scalarize_rem,
...@@ -173,6 +175,15 @@ pub const Feature = enum {...@@ -173,6 +175,15 @@ pub const Feature = enum {
173 /// Not compatible with `scalarize_mul_safe`.175 /// Not compatible with `scalarize_mul_safe`.
174 expand_mul_safe,176 expand_mul_safe,
175177
178 /// Replace `div_ceil` with truncating division followed by a remainder based adjustment for integers,
179 /// or division followed by ceil for floats.
180 /// Not compatible with `scalarize_div_ceil`.
181 expand_div_ceil,
182 /// Replace `div_ceil_optimized` with truncating division followed by a remainder based adjustment for integers,
183 /// or division followed by ceil for floats.
184 /// Not compatible with `scalarize_div_ceil_optimized`.
185 expand_div_ceil_optimized,
186
176 /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.187 /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.
177 /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the188 /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the
178 /// first byte of memory until bit pointers know their backing type.189 /// first byte of memory until bit pointers know their backing type.
...@@ -231,6 +242,8 @@ pub const Feature = enum {...@@ -231,6 +242,8 @@ pub const Feature = enum {
231 .div_trunc_optimized => .scalarize_div_trunc_optimized,242 .div_trunc_optimized => .scalarize_div_trunc_optimized,
232 .div_floor => .scalarize_div_floor,243 .div_floor => .scalarize_div_floor,
233 .div_floor_optimized => .scalarize_div_floor_optimized,244 .div_floor_optimized => .scalarize_div_floor_optimized,
245 .div_ceil => .scalarize_div_ceil,
246 .div_ceil_optimized => .scalarize_div_ceil_optimized,
234 .div_exact => .scalarize_div_exact,247 .div_exact => .scalarize_div_exact,
235 .div_exact_optimized => .scalarize_div_exact_optimized,248 .div_exact_optimized => .scalarize_div_exact_optimized,
236 .rem => .scalarize_rem,249 .rem => .scalarize_rem,
...@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
382 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {395 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
383 .none => {},396 .none => {},
384 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),397 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
385 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorBlockPayload(398 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
386 inst,399 inst,
387 bin_op.lhs,400 bin_op.lhs,
388 bin_op.rhs,401 bin_op.rhs,
...@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
596 continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));609 continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));
597 }610 }
598 },611 },
612 inline .div_ceil, .div_ceil_optimized => |air_tag| {
613 const expand_feature: Feature = switch (air_tag) {
614 .div_ceil => .expand_div_ceil,
615 .div_ceil_optimized => .expand_div_ceil_optimized,
616 else => unreachable,
617 };
618
619 if (l.features.has(expand_feature)) {
620 assert(!l.features.has(.scalarize(air_tag))); // it doesn't make sense to do both
621 continue :inst l.replaceInst(inst, .block, try l.divCeilBlockPayload(inst, air_tag));
622 } else {
623 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
624 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
625 .none => {},
626 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
627 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
628 inst,
629 bin_op.lhs,
630 bin_op.rhs,
631 air_tag,
632 )),
633 }
634 }
635 },
599 inline .int_from_float_safe,636 inline .int_from_float_safe,
600 .int_from_float_optimized_safe,637 .int_from_float_optimized_safe,
601 => |air_tag| {638 => |air_tag| {
...@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_...@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_
2419 } };2456 } };
2420}2457}
24212458
2459fn divCeilBlockPayload(
2460 l: *Legalize,
2461 orig_inst: Air.Inst.Index,
2462 air_tag: Air.Inst.Tag,
2463) Error!Air.Inst.Data {
2464 const pt = l.pt;
2465 const zcu = pt.zcu;
2466 const gpa = zcu.gpa;
2467
2468 const bin_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].bin_op;
2469 const operand_ty = l.typeOf(bin_op.lhs);
2470 assert(l.typeOf(bin_op.rhs).toIntern() == operand_ty.toIntern());
2471
2472 const scalar_ty = operand_ty.scalarType(zcu);
2473 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
2474
2475 switch (scalar_ty.zigTypeTag(zcu)) {
2476 .float => {
2477 // %result = ceil(lhs / rhs)
2478
2479 var inst_buf: [3]Air.Inst.Index = undefined;
2480 try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
2481
2482 var main_block: Block = .init(&inst_buf);
2483
2484 const div_tag: Air.Inst.Tag = switch (air_tag) {
2485 .div_ceil => .div_float,
2486 .div_ceil_optimized => .div_float_optimized,
2487 else => unreachable,
2488 };
2489
2490 const div_inst = main_block.add(l, .{
2491 .tag = div_tag,
2492 .data = .{ .bin_op = bin_op },
2493 });
2494
2495 const ceil_inst = main_block.add(l, .{
2496 .tag = .ceil,
2497 .data = .{ .un_op = div_inst.toRef() },
2498 });
2499
2500 main_block.addBr(l, orig_inst, ceil_inst.toRef());
2501
2502 _ = main_block.stealRemainingCapacity();
2503 return .{ .ty_pl = .{
2504 .ty = .fromType(operand_ty),
2505 .payload = try l.addBlockBody(main_block.body()),
2506 } };
2507 },
2508
2509 .int => {
2510 // Integer div_ceil:
2511 //
2512 // q = div_trunc(lhs, rhs)
2513 // r = rem(lhs, rhs)
2514 //
2515 // unsigned:
2516 // q + int(r != 0)
2517 //
2518 // signed:
2519 // q + int(r != 0 and same_sign(lhs, rhs))
2520 //
2521 // same_sign is `(lhs ^ rhs) >= 0`.
2522
2523 var inst_buf: [10]Air.Inst.Index = undefined;
2524 try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
2525
2526 var main_block: Block = .init(&inst_buf);
2527
2528 const q_inst = main_block.add(l, .{
2529 .tag = .div_trunc,
2530 .data = .{ .bin_op = bin_op },
2531 });
2532
2533 const r_inst = main_block.add(l, .{
2534 .tag = .rem,
2535 .data = .{ .bin_op = bin_op },
2536 });
2537
2538 const zero_ref: Air.Inst.Ref = if (is_vector) zero: {
2539 const zero_scalar = try pt.intValue(scalar_ty, 0);
2540 const zero_vec = try pt.aggregateSplatValue(operand_ty, zero_scalar);
2541 break :zero Air.internedToRef(zero_vec.toIntern());
2542 } else Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
2543
2544 const r_nonzero_inst = try main_block.addCmp(
2545 l,
2546 .neq,
2547 r_inst.toRef(),
2548 zero_ref,
2549 .{ .vector = is_vector },
2550 );
2551
2552 const int_info = scalar_ty.intInfo(zcu);
2553
2554 const need_adjust_inst: Air.Inst.Index = if (int_info.signedness == .unsigned) r_nonzero_inst else inst: {
2555 const sign_xor_inst = main_block.add(l, .{
2556 .tag = .xor,
2557 .data = .{ .bin_op = .{
2558 .lhs = bin_op.lhs,
2559 .rhs = bin_op.rhs,
2560 } },
2561 });
2562
2563 const signs_same_inst = try main_block.addCmp(
2564 l,
2565 .gte,
2566 sign_xor_inst.toRef(),
2567 zero_ref,
2568 .{ .vector = is_vector },
2569 );
2570
2571 break :inst main_block.add(l, .{
2572 .tag = .bit_and,
2573 .data = .{ .bin_op = .{
2574 .lhs = r_nonzero_inst.toRef(),
2575 .rhs = signs_same_inst.toRef(),
2576 } },
2577 });
2578 };
2579
2580 const adjust_u1_ty = if (is_vector)
2581 try pt.vectorType(.{
2582 .len = operand_ty.vectorLen(zcu),
2583 .child = Type.u1.toIntern(),
2584 })
2585 else
2586 Type.u1;
2587
2588 const adjust_u1_ref = main_block.addBitCast(l, adjust_u1_ty, need_adjust_inst.toRef());
2589 const adjust_inst = main_block.addTyOp(l, .int_cast, operand_ty, adjust_u1_ref);
2590
2591 const result_inst = main_block.add(l, .{
2592 .tag = .add,
2593 .data = .{ .bin_op = .{
2594 .lhs = q_inst.toRef(),
2595 .rhs = adjust_inst.toRef(),
2596 } },
2597 });
2598
2599 main_block.addBr(l, orig_inst, result_inst.toRef());
2600
2601 _ = main_block.stealRemainingCapacity();
2602 return .{ .ty_pl = .{
2603 .ty = .fromType(operand_ty),
2604 .payload = try l.addBlockBody(main_block.body()),
2605 } };
2606 },
2607
2608 else => unreachable,
2609 }
2610}
2611
2422fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {2612fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2423 const pt = l.pt;2613 const pt = l.pt;
2424 const zcu = pt.zcu;2614 const zcu = pt.zcu;
...@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(...@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(
3426 } };3616 } };
3427}3617}
34283618
3429fn softFloatDivTruncFloorBlockPayload(3619fn softFloatDivTruncFloorCeilBlockPayload(
3430 l: *Legalize,3620 l: *Legalize,
3431 orig_inst: Air.Inst.Index,3621 orig_inst: Air.Inst.Index,
3432 lhs: Air.Inst.Ref,3622 lhs: Air.Inst.Ref,
...@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(...@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(
3441 const floor_tag: Air.Inst.Tag = switch (air_tag) {3631 const floor_tag: Air.Inst.Tag = switch (air_tag) {
3442 .div_trunc, .div_trunc_optimized => .trunc_float,3632 .div_trunc, .div_trunc_optimized => .trunc_float,
3443 .div_floor, .div_floor_optimized => .floor,3633 .div_floor, .div_floor_optimized => .floor,
3634 .div_ceil, .div_ceil_optimized => .ceil,
3444 else => unreachable,3635 else => unreachable,
3445 };3636 };
34463637
src/Air/Liveness.zig+2
...@@ -417,6 +417,8 @@ fn analyzeInst(...@@ -417,6 +417,8 @@ fn analyzeInst(
417 .div_floor_optimized,417 .div_floor_optimized,
418 .div_exact,418 .div_exact,
419 .div_exact_optimized,419 .div_exact_optimized,
420 .div_ceil,
421 .div_ceil_optimized,
420 .rem,422 .rem,
421 .rem_optimized,423 .rem_optimized,
422 .mod,424 .mod,
src/Air/Liveness/Verify.zig+2
...@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
235 .div_trunc_optimized,235 .div_trunc_optimized,
236 .div_floor,236 .div_floor,
237 .div_floor_optimized,237 .div_floor_optimized,
238 .div_ceil,
239 .div_ceil_optimized,
238 .div_exact,240 .div_exact,
239 .div_exact_optimized,241 .div_exact_optimized,
240 .rem,242 .rem,
src/Air/Verify.zig+2
...@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {...@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
253 .div_trunc_optimized,253 .div_trunc_optimized,
254 .div_floor,254 .div_floor,
255 .div_floor_optimized,255 .div_floor_optimized,
256 .div_ceil,
257 .div_ceil_optimized,
256 .div_exact,258 .div_exact,
257 .div_exact_optimized,259 .div_exact_optimized,
258 .rem,260 .rem,
src/Air/print.zig+2
...@@ -132,6 +132,7 @@ const Writer = struct {...@@ -132,6 +132,7 @@ const Writer = struct {
132 .div_float,132 .div_float,
133 .div_trunc,133 .div_trunc,
134 .div_floor,134 .div_floor,
135 .div_ceil,
135 .div_exact,136 .div_exact,
136 .rem,137 .rem,
137 .mod,138 .mod,
...@@ -160,6 +161,7 @@ const Writer = struct {...@@ -160,6 +161,7 @@ const Writer = struct {
160 .div_float_optimized,161 .div_float_optimized,
161 .div_trunc_optimized,162 .div_trunc_optimized,
162 .div_floor_optimized,163 .div_floor_optimized,
164 .div_ceil_optimized,
163 .div_exact_optimized,165 .div_exact_optimized,
164 .rem_optimized,166 .rem_optimized,
165 .mod_optimized,167 .mod_optimized,
src/Sema.zig+68-2
...@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(...@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(
1335 .div => try sema.zirDiv(block, inst),1335 .div => try sema.zirDiv(block, inst),
1336 .div_exact => try sema.zirDivExact(block, inst),1336 .div_exact => try sema.zirDivExact(block, inst),
1337 .div_floor => try sema.zirDivFloor(block, inst),1337 .div_floor => try sema.zirDivFloor(block, inst),
1338 .div_ceil => try sema.zirDivCeil(block, inst),
1338 .div_trunc => try sema.zirDivTrunc(block, inst),1339 .div_trunc => try sema.zirDivTrunc(block, inst),
13391340
1340 .mod_rem => try sema.zirModRem(block, inst),1341 .mod_rem => try sema.zirModRem(block, inst),
...@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
13850 return sema.fail(13851 return sema.fail(
13851 block,13852 block,
13852 src,13853 src,
13853 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact",13854 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact",
13854 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },13855 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
13855 );13856 );
13856 }13857 }
...@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
14023 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);14024 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
14024}14025}
1402514026
14027fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14028 const pt = sema.pt;
14029 const zcu = pt.zcu;
14030 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
14031 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14032 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14033 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14034 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14035 const lhs = sema.resolveInst(extra.lhs);
14036 const rhs = sema.resolveInst(extra.rhs);
14037 const lhs_ty = sema.typeOf(lhs);
14038 const rhs_ty = sema.typeOf(rhs);
14039 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14040 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14041 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14042 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14043
14044 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14045 .override = &.{ lhs_src, rhs_src },
14046 });
14047
14048 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14049 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14050
14051 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14052 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14053
14054 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14055
14056 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil);
14057
14058 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14059 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14060
14061 const allow_div_zero = !is_int and
14062 resolved_type.toIntern() != .comptime_float_type and
14063 block.float_mode == .strict;
14064
14065 if (maybe_lhs_val) |lhs_val| {
14066 if (maybe_rhs_val) |rhs_val| {
14067 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil);
14068 return Air.internedToRef(result.toIntern());
14069 }
14070 if (allow_div_zero) {
14071 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14072 } else {
14073 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14074 }
14075 } else if (maybe_rhs_val) |rhs_val| {
14076 if (allow_div_zero) {
14077 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14078 } else {
14079 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14080 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14081 }
14082 }
14083
14084 if (block.wantSafety()) {
14085 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14086 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14087 }
14088
14089 return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs);
14090}
14091
14026fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {14092fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14027 const pt = sema.pt;14093 const pt = sema.pt;
14028 const zcu = pt.zcu;14094 const zcu = pt.zcu;
...@@ -18051,7 +18117,7 @@ fn analyzeRet(...@@ -18051,7 +18117,7 @@ fn analyzeRet(
18051fn floatOpAllowed(tag: Zir.Inst.Tag) bool {18117fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
18052 // extend this swich as additional operators are implemented18118 // extend this swich as additional operators are implemented
18053 return switch (tag) {18119 return switch (tag) {
18054 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,18120 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true,
18055 else => false,18121 else => false,
18056 };18122 };
18057}18123}
src/Sema/arith.zig+68-2
...@@ -768,7 +768,7 @@ fn mulSatScalar(...@@ -768,7 +768,7 @@ fn mulSatScalar(
768 }768 }
769}769}
770770
771pub const DivOp = enum { div, div_trunc, div_floor, div_exact };771pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact };
772772
773/// Applies the `/` operator to comptime-known values.773/// Applies the `/` operator to comptime-known values.
774/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.774/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
...@@ -843,6 +843,11 @@ fn divScalar(...@@ -843,6 +843,11 @@ fn divScalar(
843 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);843 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
844 return res.val;844 return res.val;
845 },845 },
846 .div_ceil => {
847 const res = try intDivCeil(sema, lhs_val, rhs_val, ty);
848 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
849 return res.val;
850 },
846 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {851 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
847 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),852 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
848 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),853 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
...@@ -851,7 +856,7 @@ fn divScalar(...@@ -851,7 +856,7 @@ fn divScalar(
851 }856 }
852 } else {857 } else {
853 const allow_div_zero = switch (op) {858 const allow_div_zero = switch (op) {
854 .div, .div_trunc, .div_floor => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,859 .div, .div_trunc, .div_floor, .div_ceil => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
855 .div_exact => false,860 .div_exact => false,
856 };861 };
857 if (!allow_div_zero) {862 if (!allow_div_zero) {
...@@ -871,6 +876,7 @@ fn divScalar(...@@ -871,6 +876,7 @@ fn divScalar(
871 .div => return floatDiv(sema, lhs_val, rhs_val, ty),876 .div => return floatDiv(sema, lhs_val, rhs_val, ty),
872 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),877 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
873 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),878 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
879 .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty),
874 .div_exact => {880 .div_exact => {
875 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {881 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
876 return sema.fail(block, src, "exact division produced remainder", .{});882 return sema.fail(block, src, "exact division produced remainder", .{});
...@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1755 }1761 }
1756 return pt.intValue_big(ty, result_q.toConst());1762 return pt.intValue_big(ty, result_q.toConst());
1757}1763}
1764fn intDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1765 const result = intDivCeilInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1766 error.Overflow => {
1767 const result = intDivCeilInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1768 error.Overflow => unreachable,
1769 else => |e| return e,
1770 };
1771 return .{ .overflow = true, .val = result };
1772 },
1773 else => |e| return e,
1774 };
1775 return .{ .overflow = false, .val = result };
1776}
1777fn intDivCeilInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1778 const pt = sema.pt;
1779 const zcu = pt.zcu;
1780 var lhs_space: Value.BigIntSpace = undefined;
1781 var rhs_space: Value.BigIntSpace = undefined;
1782 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1783 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1784 const limbs_q = try sema.arena.alloc(
1785 std.math.big.Limb,
1786 lhs_bigint.limbs.len,
1787 );
1788 const limbs_r = try sema.arena.alloc(
1789 std.math.big.Limb,
1790 rhs_bigint.limbs.len,
1791 );
1792 const limbs_buf = try sema.arena.alloc(
1793 std.math.big.Limb,
1794 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1795 );
1796 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1797 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1798 result_q.divCeil(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1799 if (ty.toIntern() != .comptime_int_type) {
1800 const info = ty.intInfo(zcu);
1801 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1802 return error.Overflow;
1803 }
1804 }
1805 return pt.intValue_big(ty, result_q.toConst());
1806}
1758fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {1807fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1759 const pt = sema.pt;1808 const pt = sema.pt;
1760 const zcu = pt.zcu;1809 const zcu = pt.zcu;
...@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2140 .storage = storage,2189 .storage = storage,
2141 } }));2190 } }));
2142}2191}
2192fn floatDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2193 const pt = sema.pt;
2194 const zcu = pt.zcu;
2195 const target = zcu.getTarget();
2196 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) { // TODO
2197 16 => .{ .f16 = @ceil(lhs.toFloat(f16, zcu) / rhs.toFloat(f16, zcu)) },
2198 32 => .{ .f32 = @ceil(lhs.toFloat(f32, zcu) / rhs.toFloat(f32, zcu)) },
2199 64 => .{ .f64 = @ceil(lhs.toFloat(f64, zcu) / rhs.toFloat(f64, zcu)) },
2200 80 => .{ .f80 = @ceil(lhs.toFloat(f80, zcu) / rhs.toFloat(f80, zcu)) },
2201 128 => .{ .f128 = @ceil(lhs.toFloat(f128, zcu) / rhs.toFloat(f128, zcu)) },
2202 else => unreachable,
2203 };
2204 return .fromInterned(try pt.intern(.{ .float = .{
2205 .ty = ty.toIntern(),
2206 .storage = storage,
2207 } }));
2208}
2143fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {2209fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
2144 const zcu = sema.pt.zcu;2210 const zcu = sema.pt.zcu;
2145 const target = zcu.getTarget();2211 const target = zcu.getTarget();
src/codegen/aarch64/Select.zig+2
...@@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
175 .div_trunc_optimized,175 .div_trunc_optimized,
176 .div_floor,176 .div_floor,
177 .div_floor_optimized,177 .div_floor_optimized,
178 .div_ceil,
179 .div_ceil_optimized,
178 .div_exact,180 .div_exact,
179 .div_exact_optimized,181 .div_exact_optimized,
180 .rem,182 .rem,
src/codegen/c.zig+2
...@@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2675 try airBinBuiltinCall(f, inst, "fmod", .none);2675 try airBinBuiltinCall(f, inst, "fmod", .none);
2676 },2676 },
2677 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),2677 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
2678 .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none),
2678 .mod => try airBinBuiltinCall(f, inst, "mod", .none),2679 .mod => try airBinBuiltinCall(f, inst, "mod", .none),
2679 .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),2680 .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),
26802681
...@@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2856 .div_float_optimized,2857 .div_float_optimized,
2857 .div_trunc_optimized,2858 .div_trunc_optimized,
2858 .div_floor_optimized,2859 .div_floor_optimized,
2860 .div_ceil_optimized,
2859 .div_exact_optimized,2861 .div_exact_optimized,
2860 .rem_optimized,2862 .rem_optimized,
2861 .mod_optimized,2863 .mod_optimized,
src/codegen/llvm/FuncGen.zig+74
...@@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov...@@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
383 .div_float => try self.airDivFloat(inst, .normal),383 .div_float => try self.airDivFloat(inst, .normal),
384 .div_trunc => try self.airDivTrunc(inst, .normal),384 .div_trunc => try self.airDivTrunc(inst, .normal),
385 .div_floor => try self.airDivFloor(inst, .normal),385 .div_floor => try self.airDivFloor(inst, .normal),
386 .div_ceil => try self.airDivCeil(inst, .normal),
386 .div_exact => try self.airDivExact(inst, .normal),387 .div_exact => try self.airDivExact(inst, .normal),
387 .rem => try self.airRem(inst, .normal),388 .rem => try self.airRem(inst, .normal),
388 .mod => try self.airMod(inst, .normal),389 .mod => try self.airMod(inst, .normal),
...@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov...@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
400 .div_float_optimized => try self.airDivFloat(inst, .fast),401 .div_float_optimized => try self.airDivFloat(inst, .fast),
401 .div_trunc_optimized => try self.airDivTrunc(inst, .fast),402 .div_trunc_optimized => try self.airDivTrunc(inst, .fast),
402 .div_floor_optimized => try self.airDivFloor(inst, .fast),403 .div_floor_optimized => try self.airDivFloor(inst, .fast),
404 .div_ceil_optimized => try self.airDivCeil(inst, .fast),
403 .div_exact_optimized => try self.airDivExact(inst, .fast),405 .div_exact_optimized => try self.airDivExact(inst, .fast),
404 .rem_optimized => try self.airRem(inst, .fast),406 .rem_optimized => try self.airRem(inst, .fast),
405 .mod_optimized => try self.airMod(inst, .fast),407 .mod_optimized => try self.airMod(inst, .fast),
...@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)...@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
3578 return self.wip.bin(.udiv, lhs, rhs, "");3580 return self.wip.bin(.udiv, lhs, rhs, "");
3579}3581}
35803582
3583fn airDivCeil(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
3584 const o = self.object;
3585 const zcu = o.zcu;
3586 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3587 const lhs = try self.resolveInst(bin_op.lhs);
3588 const rhs = try self.resolveInst(bin_op.rhs);
3589 const inst_ty = self.typeOfIndex(inst);
3590 const scalar_ty = inst_ty.scalarType(zcu);
3591
3592 if (scalar_ty.isRuntimeFloat()) {
3593 const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs });
3594 return self.buildFloatOp(.ceil, fast, inst_ty, 1, .{result});
3595 }
3596 if (scalar_ty.isSignedInt(zcu)) {
3597 const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
3598 const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
3599
3600 const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb;
3601 var bfa_buf: ExpectedContents = undefined;
3602 var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa);
3603 const allocator = bfa.allocator();
3604
3605 const scalar_bits = scalar_ty.intInfo(zcu).bits;
3606 var smin_big_int: std.math.big.int.Mutable = .{
3607 .limbs = try allocator.alloc(
3608 std.math.big.Limb,
3609 std.math.big.int.calcTwosCompLimbCount(scalar_bits),
3610 ),
3611 .len = undefined,
3612 .positive = undefined,
3613 };
3614 defer allocator.free(smin_big_int.limbs);
3615 smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits);
3616 const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst(
3617 scalar_llvm_ty,
3618 smin_big_int.toConst(),
3619 ));
3620
3621 const zero = try o.builder.splatValue(
3622 inst_llvm_ty,
3623 try o.builder.intConst(scalar_llvm_ty, 0),
3624 );
3625
3626 const div = try self.wip.bin(.sdiv, lhs, rhs, "divCeil.div");
3627 const rem = try self.wip.bin(.srem, lhs, rhs, "divCeil.rem");
3628
3629 const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "divCeil.rhs_sign");
3630 const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "divCeil.rem_xor_rhs_sign");
3631
3632 const need_correction = try self.wip.icmp(.sgt, rem_xor_rhs_sign, zero, "divCeil.need_correction");
3633
3634 const correction = try self.wip.cast(.zext, need_correction, inst_llvm_ty, "divCeil.correction");
3635 return self.wip.bin(.@"add nsw", div, correction, "divCeil");
3636 } else {
3637 const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
3638 const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
3639
3640 const zero = try o.builder.splatValue(
3641 inst_llvm_ty,
3642 try o.builder.intConst(scalar_llvm_ty, 0),
3643 );
3644
3645 const div = try self.wip.bin(.udiv, lhs, rhs, "divCeil.div");
3646 const rem = try self.wip.bin(.urem, lhs, rhs, "divCeil.rem");
3647
3648 const rem_nonzero = try self.wip.icmp(.ne, rem, zero, "divCeil.rem_nonzero");
3649 const correction = try self.wip.cast(.zext, rem_nonzero, inst_llvm_ty, "divCeil.correction");
3650
3651 return self.wip.bin(.@"add nuw", div, correction, "divCeil");
3652 }
3653}
3654
3581fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {3655fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
3582 const zcu = self.object.zcu;3656 const zcu = self.object.zcu;
3583 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3657 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
src/codegen/riscv64/CodeGen.zig+2
...@@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1422 .mod,1422 .mod,
1423 .div_float,1423 .div_float,
1424 .div_floor,1424 .div_floor,
1425 .div_ceil,
1425 => return func.fail("TODO: {s}", .{@tagName(tag)}),1426 => return func.fail("TODO: {s}", .{@tagName(tag)}),
14261427
1427 .sqrt,1428 .sqrt,
...@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1621 .div_trunc_optimized,1622 .div_trunc_optimized,
1622 .div_floor_optimized,1623 .div_floor_optimized,
1623 .div_exact_optimized,1624 .div_exact_optimized,
1625 .div_ceil_optimized,
1624 .rem_optimized,1626 .rem_optimized,
1625 .mod_optimized,1627 .mod_optimized,
1626 .neg_optimized,1628 .neg_optimized,
src/codegen/sparc64/CodeGen.zig+2-1
...@@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
523 .mul_with_overflow => try self.airMulWithOverflow(inst),523 .mul_with_overflow => try self.airMulWithOverflow(inst),
524 .shl_with_overflow => try self.airShlWithOverflow(inst),524 .shl_with_overflow => try self.airShlWithOverflow(inst),
525525
526 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),526 .div_float, .div_trunc, .div_floor, .div_ceil, .div_exact => try self.airDiv(inst),
527527
528 .cmp_lt => try self.airCmp(inst, .lt),528 .cmp_lt => try self.airCmp(inst, .lt),
529 .cmp_lte => try self.airCmp(inst, .lte),529 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
678 .div_float_optimized,678 .div_float_optimized,
679 .div_trunc_optimized,679 .div_trunc_optimized,
680 .div_floor_optimized,680 .div_floor_optimized,
681 .div_ceil_optimized,
681 .div_exact_optimized,682 .div_exact_optimized,
682 .rem_optimized,683 .rem_optimized,
683 .mod_optimized,684 .mod_optimized,
src/codegen/wasm/CodeGen.zig+103
...@@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {...@@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
62 .scalarize_div_trunc_optimized,62 .scalarize_div_trunc_optimized,
63 .scalarize_div_floor,63 .scalarize_div_floor,
64 .scalarize_div_floor_optimized,64 .scalarize_div_floor_optimized,
65 .scalarize_div_ceil,
66 .scalarize_div_ceil_optimized,
65 .scalarize_div_exact,67 .scalarize_div_exact,
66 .scalarize_div_exact_optimized,68 .scalarize_div_exact_optimized,
67 .scalarize_rem,69 .scalarize_rem,
...@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1340 .div_exact,1342 .div_exact,
1341 .div_trunc,1343 .div_trunc,
1342 .div_floor,1344 .div_floor,
1345 .div_ceil,
1343 => |tag| {1346 => |tag| {
1344 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1347 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1345 const lhs = try cg.resolveInst(bin_op.lhs);1348 const lhs = try cg.resolveInst(bin_op.lhs);
...@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1366 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),1369 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),
1367 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),1370 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),
1368 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),1371 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),
1372 .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs),
1369 else => unreachable,1373 else => unreachable,
1370 };1374 };
13711375
...@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1384 .div_exact => try cg.intDiv(int_ty, lhs, rhs),1388 .div_exact => try cg.intDiv(int_ty, lhs, rhs),
1385 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),1389 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),
1386 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),1390 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),
1391 .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs),
1387 else => unreachable,1392 else => unreachable,
1388 };1393 };
13891394
...@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1881 .div_float_optimized,1886 .div_float_optimized,
1882 .div_trunc_optimized,1887 .div_trunc_optimized,
1883 .div_floor_optimized,1888 .div_floor_optimized,
1889 .div_ceil_optimized,
1884 .div_exact_optimized,1890 .div_exact_optimized,
1885 .rem_optimized,1891 .rem_optimized,
1886 .mod_optimized,1892 .mod_optimized,
...@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W...@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
2799 }2805 }
2800}2806}
28012807
2808fn intDivCeil(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
2809 switch (ty.bits) {
2810 0 => unreachable,
2811 1...32 => {
2812 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i32);
2813 defer q.free(cg);
2814
2815 const zero: WValue = .{ .imm32 = 0 };
2816
2817 const r = try cg.intRem(ty, lhs, rhs);
2818 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
2819 defer r_nonzero.free(cg);
2820
2821 if (!ty.is_signed) {
2822 try cg.emitWValue(q);
2823 try cg.emitWValue(r_nonzero);
2824 try cg.addTag(.i32_add);
2825 return .stack;
2826 }
2827
2828 const sign_xor = try cg.intXor(ty, lhs, rhs);
2829 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
2830 defer same_sign.free(cg);
2831
2832 try cg.emitWValue(q);
2833 const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
2834 try cg.emitWValue(need_adjust);
2835 try cg.addTag(.i32_add);
2836 return .stack;
2837 },
2838 33...64 => {
2839 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i64);
2840 defer q.free(cg);
2841
2842 const zero: WValue = .{ .imm64 = 0 };
2843
2844 const r = try cg.intRem(ty, lhs, rhs);
2845 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
2846 defer r_nonzero.free(cg);
2847
2848 if (!ty.is_signed) {
2849 try cg.emitWValue(q);
2850 try cg.emitWValue(r_nonzero);
2851 try cg.addTag(.i64_extend_i32_u);
2852 try cg.addTag(.i64_add);
2853 return .stack;
2854 }
2855
2856 const sign_xor = try cg.intXor(ty, lhs, rhs);
2857 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
2858 defer same_sign.free(cg);
2859
2860 try cg.emitWValue(q);
2861 const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
2862 try cg.emitWValue(need_adjust);
2863 try cg.addTag(.i64_extend_i32_u);
2864 try cg.addTag(.i64_add);
2865 return .stack;
2866 },
2867 else => {
2868 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.usize);
2869 defer q.free(cg);
2870
2871 const zero = try cg.intZeroValue(ty);
2872
2873 const r = try cg.intRem(ty, lhs, rhs);
2874 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.u32);
2875 defer r_nonzero.free(cg);
2876
2877 if (!ty.is_signed) {
2878 var adjust_bigint = try (try cg.intCast(ty, .u32, r_nonzero)).toLocal(cg, Type.usize);
2879 defer adjust_bigint.free(cg);
2880
2881 return try cg.intAdd(ty, q, adjust_bigint);
2882 }
2883
2884 const sign_xor = try cg.intXor(ty, lhs, rhs);
2885 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.u32);
2886 defer same_sign.free(cg);
2887
2888 var adjust = try (try cg.intAnd(.u32, r_nonzero, same_sign)).toLocal(cg, Type.u32);
2889 defer adjust.free(cg);
2890
2891 var adjust_bigint = try (try cg.intCast(ty, .u32, adjust)).toLocal(cg, Type.usize);
2892 defer adjust_bigint.free(cg);
2893
2894 return try cg.intAdd(ty, q, adjust_bigint);
2895 },
2896 }
2897}
2898
2802fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {2899fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
2803 switch (ty.bits) {2900 switch (ty.bits) {
2804 0 => unreachable,2901 0 => unreachable,
...@@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr...@@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr
4265 return cg.floatFloor(ty, div_result);4362 return cg.floatFloor(ty, div_result);
4266}4363}
42674364
4365// div_ceil(a, b) = ceil(a / b)
4366fn floatDivCeil(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
4367 const div_result = try cg.floatDiv(ty, lhs, rhs);
4368 return cg.floatCeil(ty, div_result);
4369}
4370
4268// mod(a, b) = fmod(fmod(a, b) + b, b)4371// mod(a, b) = fmod(fmod(a, b) + b, b)
4269fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {4372fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
4270 const r = try cg.floatRem(ty, lhs, rhs);4373 const r = try cg.floatRem(ty, lhs, rhs);
src/codegen/x86_64/CodeGen.zig+5
...@@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {...@@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
70 .expand_sub_safe,70 .expand_sub_safe,
71 .expand_mul_safe,71 .expand_mul_safe,
7272
73 .expand_div_ceil,
74 .expand_div_ceil_optimized,
75
73 .expand_packed_load,76 .expand_packed_load,
74 .expand_packed_store,77 .expand_packed_store,
75 .expand_packed_agg_field_val,78 .expand_packed_agg_field_val,
...@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173873 for (ops) |op| try op.die(cg);173876 for (ops) |op| try op.die(cg);
173874 },173877 },
173875173878
173879 .div_ceil, .div_ceil_optimized => unreachable,
173880
173876 // No soft-float `Legalize` features are enabled, so this instruction never appears.173881 // No soft-float `Legalize` features are enabled, so this instruction never appears.
173877 .legalize_compiler_rt_call => unreachable,173882 .legalize_compiler_rt_call => unreachable,
173878173883
src/print_zir.zig+1
...@@ -392,6 +392,7 @@ const Writer = struct {...@@ -392,6 +392,7 @@ const Writer = struct {
392 .truncate,392 .truncate,
393 .div_exact,393 .div_exact,
394 .div_floor,394 .div_floor,
395 .div_ceil,
395 .div_trunc,396 .div_trunc,
396 .mod,397 .mod,
397 .rem,398 .rem,
test/behavior/int128.zig+1
...@@ -59,6 +59,7 @@ test "int128" {...@@ -59,6 +59,7 @@ test "int128" {
59 const a: i128 = -170141183460469231731687303715884105728;59 const a: i128 = -170141183460469231731687303715884105728;
60 const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;60 const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;
61 try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);61 try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);
62 try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884);
62 try expect(a == b);63 try expect(a == b);
63}64}
6465
test/behavior/math.zig+89
...@@ -488,6 +488,36 @@ fn testIntDivision() !void {...@@ -488,6 +488,36 @@ fn testIntDivision() !void {
488 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);488 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);
489 try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);489 try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);
490490
491 try expect(divCeil(i32, 5, 3) == 2);
492 try expect(divCeil(i32, -5, 3) == -1);
493 try expect(divCeil(i32, -0x80000000, -2) == 0x40000000);
494 try expect(divCeil(i32, 0, -0x80000000) == 0);
495 try expect(divCeil(i32, -0x40000001, 0x40000000) == -1);
496 try expect(divCeil(i32, -0x80000000, 1) == -0x80000000);
497 try expect(divCeil(i32, 10, 12) == 1);
498 try expect(divCeil(i32, -14, 12) == -1);
499 try expect(divCeil(i32, -2, 12) == 0);
500
501 try expect(divCeil(u32, 5, 3) == 2);
502 try expect(divCeil(u32, 16, 4) == 4);
503 try expect(divCeil(u32, 0, 100) == 0);
504 try expect(divCeil(u32, maxInt(u32) - 1, 100) == 42949673);
505
506 try expect(divCeil(i64, 5, 3) == 2);
507 try expect(divCeil(i64, -5, 3) == -1);
508 try expect(divCeil(i64, -0x80000000, -2) == 0x40000000);
509 try expect(divCeil(i64, 0, -0x80000000) == 0);
510 try expect(divCeil(i64, -0x40000001, 0x40000000) == -1);
511 try expect(divCeil(i64, -0x80000000, 1) == -0x80000000);
512 try expect(divCeil(i64, 10, 12) == 1);
513 try expect(divCeil(i64, -14, 12) == -1);
514 try expect(divCeil(i64, -2, 12) == 0);
515
516 try expect(divCeil(u64, 5, 3) == 2);
517 try expect(divCeil(u64, 16, 4) == 4);
518 try expect(divCeil(u64, 0, 100) == 0);
519 try expect(divCeil(u64, maxInt(u64) - 1, 10000) == 1844674407370956);
520
491 try expect(divTrunc(i32, 5, 3) == 1);521 try expect(divTrunc(i32, 5, 3) == 1);
492 try expect(divTrunc(i32, -5, 3) == -1);522 try expect(divTrunc(i32, -5, 3) == -1);
493 try expect(divTrunc(i32, 9, -10) == 0);523 try expect(divTrunc(i32, 9, -10) == 0);
...@@ -531,6 +561,24 @@ fn testIntDivision() !void {...@@ -531,6 +561,24 @@ fn testIntDivision() !void {
531 try expect(561 try expect(
532 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,562 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
533 );563 );
564 try expect(
565 @divFloor(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -3,
566 );
567 try expect(
568 @divFloor(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -3,
569 );
570 try expect(
571 @divFloor(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
572 );
573 try expect(
574 @divCeil(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
575 );
576 try expect(
577 @divCeil(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
578 );
579 try expect(
580 @divCeil(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 3,
581 );
534 try expect(582 try expect(
535 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,583 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
536 );584 );
...@@ -559,6 +607,13 @@ fn testFloatDivision() !void {...@@ -559,6 +607,13 @@ fn testFloatDivision() !void {
559 try expect(divFloor(f16, -43.0, 12.0) == -4.0);607 try expect(divFloor(f16, -43.0, 12.0) == -4.0);
560 try expect(divFloor(f64, -90.0, -9.0) == 10.0);608 try expect(divFloor(f64, -90.0, -9.0) == 10.0);
561609
610 try expect(divCeil(f32, 5.0, 3.0) == 2.0);
611 try expect(divCeil(f32, -5.0, 3.0) == -1.0);
612 try expect(divCeil(f32, 56.0, 9.0) == 7.0);
613 try expect(divCeil(f32, 1053.0, -41.0) == -25.0);
614 try expect(divCeil(f16, -43.0, 12.0) == -3.0);
615 try expect(divCeil(f64, -90.0, -9.0) == 10.0);
616
562 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);617 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
563 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);618 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
564 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);619 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
...@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {...@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {
607662
608 try expect(divFloor(f16, 5.0, 3.0) == 1.0);663 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
609 try expect(divFloor(f16, -5.0, 3.0) == -2.0);664 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
665 try expect(divCeil(f16, 5.0, 3.0) == 2.0);
666 try expect(divCeil(f16, -5.0, 3.0) == -1.0);
610 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);667 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
611 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);668 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
612 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);669 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
...@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {...@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {
622fn divFloor(comptime T: type, a: T, b: T) T {679fn divFloor(comptime T: type, a: T, b: T) T {
623 return @divFloor(a, b);680 return @divFloor(a, b);
624}681}
682fn divCeil(comptime T: type, a: T, b: T) T {
683 return @divCeil(a, b);
684}
625fn divTrunc(comptime T: type, a: T, b: T) T {685fn divTrunc(comptime T: type, a: T, b: T) T {
626 return @divTrunc(a, b);686 return @divTrunc(a, b);
627}687}
...@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {...@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {
1846 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);1906 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
1847}1907}
18481908
1909fn testDivCeil(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1910 try expect(@divCeil(numerator, denominator) == expected);
1911}
1912
1913test "@divCeil > 128 bits" {
1914 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1915 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1916 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1917
1918 try testDivCeil(u140, 0, maxInt(u140), 0);
1919 try testDivCeil(u140, maxInt(u140), maxInt(u140), 1);
1920 try testDivCeil(u140, maxInt(u140), 2, maxInt(u140) / 2 + 1);
1921 try testDivCeil(u140, (1 << 139) + 5, 1 << 70, (1 << 69) + 1);
1922 try testDivCeil(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 2);
1923 try testDivCeil(u200, 123, 1 << 100, 1);
1924 try testDivCeil(u200, 1 << 120, 1 << 60, 1 << 60);
1925 try testDivCeil(u200, maxInt(u200), 1 << 100, 1 << 100);
1926
1927 try testDivCeil(i140, 0, maxInt(i140), 0);
1928 try testDivCeil(i140, maxInt(i140), maxInt(i140), 1);
1929 try testDivCeil(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50));
1930 try testDivCeil(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50));
1931 try testDivCeil(i140, -((1 << 100) + 1), -(1 << 50), (1 << 50) + 1);
1932 try testDivCeil(i200, -3, 2, -1);
1933 try testDivCeil(i200, minInt(i200), 1, minInt(i200));
1934 try testDivCeil(i200, minInt(i200), -2, 1 << 198);
1935 try testDivCeil(i200, maxInt(i200), 2, 1 << 198);
1936}
1937
1849fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {1938fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1850 try expect(@divTrunc(numerator, denominator) == expected);1939 try expect(@divTrunc(numerator, denominator) == expected);
1851}1940}
test/behavior/vector.zig+35-1
...@@ -510,8 +510,37 @@ test "vector division operators" {...@@ -510,8 +510,37 @@ test "vector division operators" {
510 inline for (@as([4]T, d2), 0..) |v, i| {510 inline for (@as([4]T, d2), 0..) |v, i| {
511 try expect(@divFloor(x[i], y[i]) == v);511 try expect(@divFloor(x[i], y[i]) == v);
512 }512 }
513 const d3 = @divTrunc(x, y);513 const d3 = @divCeil(x, y);
514 inline for (@as([4]T, d3), 0..) |v, i| {514 inline for (@as([4]T, d3), 0..) |v, i| {
515 try expect(@divCeil(x[i], y[i]) == v);
516 }
517 const d4 = @divTrunc(x, y);
518 inline for (@as([4]T, d4), 0..) |v, i| {
519 try expect(@divTrunc(x[i], y[i]) == v);
520 }
521 }
522
523 fn doTheTestDivNoExact(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {
524 const is_signed_int = switch (@typeInfo(T)) {
525 .int => |info| info.signedness == .signed,
526 else => false,
527 };
528 if (!is_signed_int) {
529 const d0 = x / y;
530 inline for (@as([4]T, d0), 0..) |v, i| {
531 try expect(x[i] / y[i] == v);
532 }
533 }
534 const d2 = @divFloor(x, y);
535 inline for (@as([4]T, d2), 0..) |v, i| {
536 try expect(@divFloor(x[i], y[i]) == v);
537 }
538 const d3 = @divCeil(x, y);
539 inline for (@as([4]T, d3), 0..) |v, i| {
540 try expect(@divCeil(x[i], y[i]) == v);
541 }
542 const d4 = @divTrunc(x, y);
543 inline for (@as([4]T, d4), 0..) |v, i| {
515 try expect(@divTrunc(x[i], y[i]) == v);544 try expect(@divTrunc(x[i], y[i]) == v);
516 }545 }
517 }546 }
...@@ -566,6 +595,9 @@ test "vector division operators" {...@@ -566,6 +595,9 @@ test "vector division operators" {
566 try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });595 try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
567 try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });596 try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
568 try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });597 try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
598
599 try doTheTestDivNoExact(u64, [4]u64{ 4, 5, 6, 7 }, [4]u64{ 4, 4, 4, 4 });
600 try doTheTestDivNoExact(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 3, 3, -3, -3 });
569 }601 }
570 };602 };
571603
...@@ -1318,11 +1350,13 @@ test "zero divisor" {...@@ -1318,11 +1350,13 @@ test "zero divisor" {
1318 const v2 = @divExact(zeros, ones);1350 const v2 = @divExact(zeros, ones);
1319 const v3 = @divTrunc(zeros, ones);1351 const v3 = @divTrunc(zeros, ones);
1320 const v4 = @divFloor(zeros, ones);1352 const v4 = @divFloor(zeros, ones);
1353 const v5 = @divCeil(zeros, ones);
13211354
1322 _ = v1[0];1355 _ = v1[0];
1323 _ = v2[0];1356 _ = v2[0];
1324 _ = v3[0];1357 _ = v3[0];
1325 _ = v4[0];1358 _ = v4[0];
1359 _ = v5[0];
1326}1360}
13271361
1328test "zero multiplicand" {1362test "zero multiplicand" {
test/behavior/x86_64/binary.zig+21
...@@ -5279,6 +5279,27 @@ test divFloorOptimized {...@@ -5279,6 +5279,27 @@ test divFloorOptimized {
5279 try test_div_floor_optimized.testFloatVectors();5279 try test_div_floor_optimized.testFloatVectors();
5280}5280}
52815281
5282inline fn divCeilUnoptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
5283 return @divCeil(lhs, rhs);
5284}
5285test divCeilUnoptimized {
5286 const test_div_ceil_unoptimized = binary(divCeilUnoptimized, .{ .compare = .approx_int });
5287 try test_div_ceil_unoptimized.testInts();
5288 try test_div_ceil_unoptimized.testIntVectors();
5289 try test_div_ceil_unoptimized.testFloats();
5290 try test_div_ceil_unoptimized.testFloatVectors();
5291}
5292
5293inline fn divCeilOptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
5294 @setFloatMode(.optimized);
5295 return @divCeil(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0)));
5296}
5297test divCeilOptimized {
5298 const test_div_ceil_optimized = binary(divCeilOptimized, .{ .compare = .approx_int });
5299 try test_div_ceil_optimized.testFloats();
5300 try test_div_ceil_optimized.testFloatVectors();
5301}
5302
5282inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {5303inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {
5283 return @rem(lhs, rhs);5304 return @rem(lhs, rhs);
5284}5305}
test/cases/compile_errors/signed_integer_division.zig+1-1
...@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {...@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {
44
5// error5// error
6//6//
7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact