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>
13701370 <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li>
13711371 <li>Signed integer operands must be comptime-known and positive. In other cases, use
13721372 {#link|@divTrunc#},
1373 {#link|@divFloor#}, or
1373 {#link|@divFloor#},
1374 {#link|@divCeil#}, or
13741375 {#link|@divExact#} instead.
13751376 </li>
13761377 <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
47354736 <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>
47364737 </ul>
47374738 <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#}
47394740 {#header_close#}
47404741 {#header_open|@divFloor#}
47414742 <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
47494750 <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>
47504751 </ul>
47514752 <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#}
47534754 {#header_close#}
47544755 {#header_open|@divTrunc#}
47554756 <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
47634764 <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>
47644765 </ul>
47654766 <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#}
47674781 {#header_close#}
47684782
47694783 {#header_open|@embedFile#}
......@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
60956109 <li>{#syntax#}/{#endsyntax#} (division)</li>
60966110 <li>{#link|@divTrunc#} (division)</li>
60976111 <li>{#link|@divFloor#} (division)</li>
6112 <li>{#link|@divCeil#} (division)</li>
60986113 <li>{#link|@divExact#} (division)</li>
60996114 </ul>
61006115 <p>Example with addition at compile-time:</p>
......@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
61126127 <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>
61136128 <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>
61146129 <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>
6130 <li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li>
61156131 <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>
61166132 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
61176133 </ul>
lib/std/math/big/int.zig+66
......@@ -1221,6 +1221,72 @@ pub const Mutable = struct {
12211221 }
12221222 }
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
12241290 /// q = a / b (rem r)
12251291 ///
12261292 /// 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
28452845 .bit_reverse,
28462846 .div_exact,
28472847 .div_floor,
2848 .div_ceil,
28482849 .div_trunc,
28492850 .mod,
28502851 .rem,
......@@ -9392,6 +9393,7 @@ fn builtinCall(
93929393
93939394 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
93949395 .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),
93959397 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
93969398 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
93979399 .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.
936936 },
937937 .div_exact,
938938 .div_floor,
939 .div_ceil,
939940 .div_trunc,
940941 .mod,
941942 .rem,
lib/std/zig/BuiltinFn.zig+8
......@@ -31,6 +31,7 @@ pub const Tag = enum {
3131 c_va_copy,
3232 c_va_end,
3333 c_va_start,
34 div_ceil,
3435 div_exact,
3536 div_floor,
3637 div_trunc,
......@@ -398,6 +399,13 @@ pub const list = list: {
398399 .param_count = 2,
399400 },
400401 },
402 .{
403 "@divCeil",
404 .{
405 .tag = .div_ceil,
406 .param_count = 2,
407 },
408 },
401409 .{
402410 "@divTrunc",
403411 .{
lib/std/zig/Zir.zig+7
......@@ -200,6 +200,9 @@ pub const Inst = struct {
200200 /// Implements the `@divFloor` builtin.
201201 /// Uses the `pl_node` union field with payload `Bin`.
202202 div_floor,
203 /// Implements the `@divCeil` builtin.
204 /// Uses the `pl_node` union field with payload `Bin`.
205 div_ceil,
203206 /// Implements the `@divTrunc` builtin.
204207 /// Uses the `pl_node` union field with payload `Bin`.
205208 div_trunc,
......@@ -1267,6 +1270,7 @@ pub const Inst = struct {
12671270 .bit_reverse,
12681271 .div_exact,
12691272 .div_floor,
1273 .div_ceil,
12701274 .div_trunc,
12711275 .mod,
12721276 .rem,
......@@ -1547,6 +1551,7 @@ pub const Inst = struct {
15471551 .bit_reverse,
15481552 .div_exact,
15491553 .div_floor,
1554 .div_ceil,
15501555 .div_trunc,
15511556 .mod,
15521557 .rem,
......@@ -1815,6 +1820,7 @@ pub const Inst = struct {
18151820
18161821 .div_exact = .pl_node,
18171822 .div_floor = .pl_node,
1823 .div_ceil = .pl_node,
18181824 .div_trunc = .pl_node,
18191825 .mod = .pl_node,
18201826 .rem = .pl_node,
......@@ -4115,6 +4121,7 @@ fn findTrackableInner(
41154121 .mul_sat,
41164122 .div_exact,
41174123 .div_floor,
4124 .div_ceil,
41184125 .div_trunc,
41194126 .mod,
41204127 .rem,
lib/zig.h+32
......@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
813813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
814814 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)); \
815815 } \
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 } \
816825\
817826 zig_basic_operator(uint##w##_t, mod_u##w, %) \
818827\
......@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
20582067 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
20592068}
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
20612085#define zig_mod_u128 zig_rem_u128
20622086
20632087static 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
32513275 zig_trap();
32523276}
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
32543282zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
32553283static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
32563284 if (!is_signed) {
......@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
40104038 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
40114039 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
40124040 } \
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 } \
40134045\
40144046 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
40154047 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 {
143143 div_floor,
144144 /// Same as `div_floor` with optimized float mode.
145145 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,
146153 /// Integer or float division.
147154 /// If a remainder would be produced, illegal behavior occurs.
148155 /// For integers, overflow is illegal behavior.
......@@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
16051612 .div_float,
16061613 .div_trunc,
16071614 .div_floor,
1615 .div_ceil,
16081616 .div_exact,
16091617 .rem,
16101618 .mod,
......@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
16241632 .div_float_optimized,
16251633 .div_trunc_optimized,
16261634 .div_floor_optimized,
1635 .div_ceil_optimized,
16271636 .div_exact_optimized,
16281637 .rem_optimized,
16291638 .mod_optimized,
......@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
19851994 .div_trunc_optimized,
19861995 .div_floor,
19871996 .div_floor_optimized,
1997 .div_ceil,
1998 .div_ceil_optimized,
19881999 .div_exact,
19892000 .div_exact_optimized,
19902001 .rem,
src/Air/Legalize.zig+193-2
......@@ -54,6 +54,8 @@ pub const Feature = enum {
5454 scalarize_div_trunc_optimized,
5555 scalarize_div_floor,
5656 scalarize_div_floor_optimized,
57 scalarize_div_ceil,
58 scalarize_div_ceil_optimized,
5759 scalarize_div_exact,
5860 scalarize_div_exact_optimized,
5961 scalarize_rem,
......@@ -173,6 +175,15 @@ pub const Feature = enum {
173175 /// Not compatible with `scalarize_mul_safe`.
174176 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
176187 /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.
177188 /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the
178189 /// first byte of memory until bit pointers know their backing type.
......@@ -231,6 +242,8 @@ pub const Feature = enum {
231242 .div_trunc_optimized => .scalarize_div_trunc_optimized,
232243 .div_floor => .scalarize_div_floor,
233244 .div_floor_optimized => .scalarize_div_floor_optimized,
245 .div_ceil => .scalarize_div_ceil,
246 .div_ceil_optimized => .scalarize_div_ceil_optimized,
234247 .div_exact => .scalarize_div_exact,
235248 .div_exact_optimized => .scalarize_div_exact_optimized,
236249 .rem => .scalarize_rem,
......@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
382395 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
383396 .none => {},
384397 .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(
386399 inst,
387400 bin_op.lhs,
388401 bin_op.rhs,
......@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
596609 continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));
597610 }
598611 },
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 },
599636 inline .int_from_float_safe,
600637 .int_from_float_optimized_safe,
601638 => |air_tag| {
......@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_
24192456 } };
24202457}
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
24222612fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
24232613 const pt = l.pt;
24242614 const zcu = pt.zcu;
......@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(
34263616 } };
34273617}
34283618
3429fn softFloatDivTruncFloorBlockPayload(
3619fn softFloatDivTruncFloorCeilBlockPayload(
34303620 l: *Legalize,
34313621 orig_inst: Air.Inst.Index,
34323622 lhs: Air.Inst.Ref,
......@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(
34413631 const floor_tag: Air.Inst.Tag = switch (air_tag) {
34423632 .div_trunc, .div_trunc_optimized => .trunc_float,
34433633 .div_floor, .div_floor_optimized => .floor,
3634 .div_ceil, .div_ceil_optimized => .ceil,
34443635 else => unreachable,
34453636 };
34463637
src/Air/Liveness.zig+2
......@@ -417,6 +417,8 @@ fn analyzeInst(
417417 .div_floor_optimized,
418418 .div_exact,
419419 .div_exact_optimized,
420 .div_ceil,
421 .div_ceil_optimized,
420422 .rem,
421423 .rem_optimized,
422424 .mod,
src/Air/Liveness/Verify.zig+2
......@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
235235 .div_trunc_optimized,
236236 .div_floor,
237237 .div_floor_optimized,
238 .div_ceil,
239 .div_ceil_optimized,
238240 .div_exact,
239241 .div_exact_optimized,
240242 .rem,
src/Air/Verify.zig+2
......@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
253253 .div_trunc_optimized,
254254 .div_floor,
255255 .div_floor_optimized,
256 .div_ceil,
257 .div_ceil_optimized,
256258 .div_exact,
257259 .div_exact_optimized,
258260 .rem,
src/Air/print.zig+2
......@@ -132,6 +132,7 @@ const Writer = struct {
132132 .div_float,
133133 .div_trunc,
134134 .div_floor,
135 .div_ceil,
135136 .div_exact,
136137 .rem,
137138 .mod,
......@@ -160,6 +161,7 @@ const Writer = struct {
160161 .div_float_optimized,
161162 .div_trunc_optimized,
162163 .div_floor_optimized,
164 .div_ceil_optimized,
163165 .div_exact_optimized,
164166 .rem_optimized,
165167 .mod_optimized,
src/Sema.zig+68-2
......@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(
13351335 .div => try sema.zirDiv(block, inst),
13361336 .div_exact => try sema.zirDivExact(block, inst),
13371337 .div_floor => try sema.zirDivFloor(block, inst),
1338 .div_ceil => try sema.zirDivCeil(block, inst),
13381339 .div_trunc => try sema.zirDivTrunc(block, inst),
13391340
13401341 .mod_rem => try sema.zirModRem(block, inst),
......@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1385013851 return sema.fail(
1385113852 block,
1385213853 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",
1385413855 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
1385513856 );
1385613857 }
......@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1402314024 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
1402414025}
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
1402614092fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1402714093 const pt = sema.pt;
1402814094 const zcu = pt.zcu;
......@@ -18051,7 +18117,7 @@ fn analyzeRet(
1805118117fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
1805218118 // extend this swich as additional operators are implemented
1805318119 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,
1805518121 else => false,
1805618122 };
1805718123}
src/Sema/arith.zig+68-2
......@@ -768,7 +768,7 @@ fn mulSatScalar(
768768 }
769769}
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
773773/// Applies the `/` operator to comptime-known values.
774774/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
......@@ -843,6 +843,11 @@ fn divScalar(
843843 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
844844 return res.val;
845845 },
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 },
846851 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
847852 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
848853 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
......@@ -851,7 +856,7 @@ fn divScalar(
851856 }
852857 } else {
853858 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,
855860 .div_exact => false,
856861 };
857862 if (!allow_div_zero) {
......@@ -871,6 +876,7 @@ fn divScalar(
871876 .div => return floatDiv(sema, lhs_val, rhs_val, ty),
872877 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
873878 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
879 .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty),
874880 .div_exact => {
875881 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
876882 return sema.fail(block, src, "exact division produced remainder", .{});
......@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
17551761 }
17561762 return pt.intValue_big(ty, result_q.toConst());
17571763}
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}
17581807fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
17591808 const pt = sema.pt;
17601809 const zcu = pt.zcu;
......@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
21402189 .storage = storage,
21412190 } }));
21422191}
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}
21432209fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
21442210 const zcu = sema.pt.zcu;
21452211 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 {
175175 .div_trunc_optimized,
176176 .div_floor,
177177 .div_floor_optimized,
178 .div_ceil,
179 .div_ceil_optimized,
178180 .div_exact,
179181 .div_exact_optimized,
180182 .rem,
src/codegen/c.zig+2
......@@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
26752675 try airBinBuiltinCall(f, inst, "fmod", .none);
26762676 },
26772677 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
2678 .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none),
26782679 .mod => try airBinBuiltinCall(f, inst, "mod", .none),
26792680 .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 {
28562857 .div_float_optimized,
28572858 .div_trunc_optimized,
28582859 .div_floor_optimized,
2860 .div_ceil_optimized,
28592861 .div_exact_optimized,
28602862 .rem_optimized,
28612863 .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
383383 .div_float => try self.airDivFloat(inst, .normal),
384384 .div_trunc => try self.airDivTrunc(inst, .normal),
385385 .div_floor => try self.airDivFloor(inst, .normal),
386 .div_ceil => try self.airDivCeil(inst, .normal),
386387 .div_exact => try self.airDivExact(inst, .normal),
387388 .rem => try self.airRem(inst, .normal),
388389 .mod => try self.airMod(inst, .normal),
......@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
400401 .div_float_optimized => try self.airDivFloat(inst, .fast),
401402 .div_trunc_optimized => try self.airDivTrunc(inst, .fast),
402403 .div_floor_optimized => try self.airDivFloor(inst, .fast),
404 .div_ceil_optimized => try self.airDivCeil(inst, .fast),
403405 .div_exact_optimized => try self.airDivExact(inst, .fast),
404406 .rem_optimized => try self.airRem(inst, .fast),
405407 .mod_optimized => try self.airMod(inst, .fast),
......@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
35783580 return self.wip.bin(.udiv, lhs, rhs, "");
35793581}
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
35813655fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
35823656 const zcu = self.object.zcu;
35833657 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 {
14221422 .mod,
14231423 .div_float,
14241424 .div_floor,
1425 .div_ceil,
14251426 => return func.fail("TODO: {s}", .{@tagName(tag)}),
14261427
14271428 .sqrt,
......@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
16211622 .div_trunc_optimized,
16221623 .div_floor_optimized,
16231624 .div_exact_optimized,
1625 .div_ceil_optimized,
16241626 .rem_optimized,
16251627 .mod_optimized,
16261628 .neg_optimized,
src/codegen/sparc64/CodeGen.zig+2-1
......@@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
523523 .mul_with_overflow => try self.airMulWithOverflow(inst),
524524 .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
528528 .cmp_lt => try self.airCmp(inst, .lt),
529529 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
678678 .div_float_optimized,
679679 .div_trunc_optimized,
680680 .div_floor_optimized,
681 .div_ceil_optimized,
681682 .div_exact_optimized,
682683 .rem_optimized,
683684 .mod_optimized,
src/codegen/wasm/CodeGen.zig+103
......@@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
6262 .scalarize_div_trunc_optimized,
6363 .scalarize_div_floor,
6464 .scalarize_div_floor_optimized,
65 .scalarize_div_ceil,
66 .scalarize_div_ceil_optimized,
6567 .scalarize_div_exact,
6668 .scalarize_div_exact_optimized,
6769 .scalarize_rem,
......@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13401342 .div_exact,
13411343 .div_trunc,
13421344 .div_floor,
1345 .div_ceil,
13431346 => |tag| {
13441347 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
13451348 const lhs = try cg.resolveInst(bin_op.lhs);
......@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13661369 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),
13671370 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),
13681371 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),
1372 .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs),
13691373 else => unreachable,
13701374 };
13711375
......@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13841388 .div_exact => try cg.intDiv(int_ty, lhs, rhs),
13851389 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),
13861390 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),
1391 .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs),
13871392 else => unreachable,
13881393 };
13891394
......@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18811886 .div_float_optimized,
18821887 .div_trunc_optimized,
18831888 .div_floor_optimized,
1889 .div_ceil_optimized,
18841890 .div_exact_optimized,
18851891 .rem_optimized,
18861892 .mod_optimized,
......@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
27992805 }
28002806}
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
28022899fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
28032900 switch (ty.bits) {
28042901 0 => unreachable,
......@@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr
42654362 return cg.floatFloor(ty, div_result);
42664363}
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
42684371// mod(a, b) = fmod(fmod(a, b) + b, b)
42694372fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
42704373 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 {
7070 .expand_sub_safe,
7171 .expand_mul_safe,
7272
73 .expand_div_ceil,
74 .expand_div_ceil_optimized,
75
7376 .expand_packed_load,
7477 .expand_packed_store,
7578 .expand_packed_agg_field_val,
......@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173873173876 for (ops) |op| try op.die(cg);
173874173877 },
173875173878
173879 .div_ceil, .div_ceil_optimized => unreachable,
173880
173876173881 // No soft-float `Legalize` features are enabled, so this instruction never appears.
173877173882 .legalize_compiler_rt_call => unreachable,
173878173883
src/print_zir.zig+1
......@@ -392,6 +392,7 @@ const Writer = struct {
392392 .truncate,
393393 .div_exact,
394394 .div_floor,
395 .div_ceil,
395396 .div_trunc,
396397 .mod,
397398 .rem,
test/behavior/int128.zig+1
......@@ -59,6 +59,7 @@ test "int128" {
5959 const a: i128 = -170141183460469231731687303715884105728;
6060 const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;
6161 try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);
62 try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884);
6263 try expect(a == b);
6364}
6465
test/behavior/math.zig+89
......@@ -488,6 +488,36 @@ fn testIntDivision() !void {
488488 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);
489489 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
491521 try expect(divTrunc(i32, 5, 3) == 1);
492522 try expect(divTrunc(i32, -5, 3) == -1);
493523 try expect(divTrunc(i32, 9, -10) == 0);
......@@ -531,6 +561,24 @@ fn testIntDivision() !void {
531561 try expect(
532562 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
533563 );
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 );
534582 try expect(
535583 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
536584 );
......@@ -559,6 +607,13 @@ fn testFloatDivision() !void {
559607 try expect(divFloor(f16, -43.0, 12.0) == -4.0);
560608 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
562617 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
563618 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
564619 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
......@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {
607662
608663 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
609664 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);
610667 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
611668 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
612669 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
......@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {
622679fn divFloor(comptime T: type, a: T, b: T) T {
623680 return @divFloor(a, b);
624681}
682fn divCeil(comptime T: type, a: T, b: T) T {
683 return @divCeil(a, b);
684}
625685fn divTrunc(comptime T: type, a: T, b: T) T {
626686 return @divTrunc(a, b);
627687}
......@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {
18461906 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
18471907}
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
18491938fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
18501939 try expect(@divTrunc(numerator, denominator) == expected);
18511940}
test/behavior/vector.zig+35-1
......@@ -510,8 +510,37 @@ test "vector division operators" {
510510 inline for (@as([4]T, d2), 0..) |v, i| {
511511 try expect(@divFloor(x[i], y[i]) == v);
512512 }
513 const d3 = @divTrunc(x, y);
513 const d3 = @divCeil(x, y);
514514 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| {
515544 try expect(@divTrunc(x[i], y[i]) == v);
516545 }
517546 }
......@@ -566,6 +595,9 @@ test "vector division operators" {
566595 try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
567596 try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
568597 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 });
569601 }
570602 };
571603
......@@ -1318,11 +1350,13 @@ test "zero divisor" {
13181350 const v2 = @divExact(zeros, ones);
13191351 const v3 = @divTrunc(zeros, ones);
13201352 const v4 = @divFloor(zeros, ones);
1353 const v5 = @divCeil(zeros, ones);
13211354
13221355 _ = v1[0];
13231356 _ = v2[0];
13241357 _ = v3[0];
13251358 _ = v4[0];
1359 _ = v5[0];
13261360}
13271361
13281362test "zero multiplicand" {
test/behavior/x86_64/binary.zig+21
......@@ -5279,6 +5279,27 @@ test divFloorOptimized {
52795279 try test_div_floor_optimized.testFloatVectors();
52805280}
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
52825303inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {
52835304 return @rem(lhs, rhs);
52845305}
test/cases/compile_errors/signed_integer_division.zig+1-1
......@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {
44
55// error
66//
7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact
7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact