authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-08 21:15:24+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-08 21:15:24+02:00
logde7fdfecd81e74c115395256179c64cb480cb860
tree6bdf7898cf24cf909be6a96b06a3bb40936333d2
parentac7bf53d9486d6c9a079a818dc87891551eaeac1
parente42cb3b2369b3034c15be3a6f0bf519db4baac98

Merge pull request 'compiler: implement @divCeil builtin' (#36043) from pavelverigo/zig:div-ceil into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36043 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

55 files changed, 974 insertions(+), 100 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/compiler/Maker/WebServer.zig+1-1
......@@ -163,7 +163,7 @@ pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void {
163163 assert(idx == step_names_trailing.len);
164164 }
165165
166 const step_status_bits = try gpa.alloc(u8, std.math.divCeil(usize, all_steps.len, 4) catch unreachable);
166 const step_status_bits = try gpa.alloc(u8, @divCeil(all_steps.len, 4));
167167 errdefer gpa.free(step_status_bits);
168168 @memset(step_status_bits, 0);
169169
lib/compiler_rt/limb64.zig+1-2
......@@ -3,7 +3,6 @@ const testing = std.testing;
33const assert = std.debug.assert;
44const maxInt = std.math.maxInt;
55const minInt = std.math.minInt;
6const divCeil = std.math.divCeil;
76
87const builtin = @import("builtin");
98const compiler_rt = @import("../compiler_rt.zig");
......@@ -26,7 +25,7 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {
2625}
2726
2827fn usedLimbCount(bits: u16) u16 {
29 return divCeil(u16, bits, 64) catch unreachable;
28 return @divCeil(bits, 64);
3029}
3130
3231fn limbCount(bits: u16) u16 {
lib/compiler_rt/udivmodei4.zig+1-1
......@@ -8,7 +8,7 @@ const shl = std.math.shl;
88const compiler_rt = @import("../compiler_rt.zig");
99const symbol = @import("../compiler_rt.zig").symbol;
1010
11const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max supported type is u65535
11const max_limbs = @divCeil(65535, 32); // max supported type is u65535
1212
1313comptime {
1414 symbol(&__udivei4, "__udivei4");
lib/fuzzer.zig+1-1
......@@ -52,7 +52,7 @@ var fuzzer: Fuzzer = undefined;
5252var current_test_name: ?[]const u8 = null;
5353
5454fn bitsetUsizes(elems: usize) usize {
55 return math.divCeil(usize, elems, @bitSizeOf(usize)) catch unreachable;
55 return @divCeil(elems, @bitSizeOf(usize));
5656}
5757
5858const Executable = struct {
lib/std/Random.zig+1-1
......@@ -126,7 +126,7 @@ pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: ty
126126pub fn int(r: Random, comptime T: type) T {
127127 const bits = @typeInfo(T).int.bits;
128128 const UnsignedT = @Int(.unsigned, bits);
129 const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable;
129 const ceil_bytes = @divCeil(bits, 8);
130130 const ByteAlignedT = @Int(.unsigned, ceil_bytes * 8);
131131
132132 var rand_bytes: [ceil_bytes]u8 = undefined;
lib/std/Target.zig+1-1
......@@ -1232,7 +1232,7 @@ pub const Cpu = struct {
12321232 ints: [usize_count]usize,
12331233
12341234 pub const needed_bit_count = 347;
1235 pub const byte_count = (needed_bit_count + 7) / 8;
1235 pub const byte_count = @divCeil(needed_bit_count, 8);
12361236 pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);
12371237 pub const Index = std.math.Log2Int(@Int(.unsigned, usize_count * @bitSizeOf(usize)));
12381238 pub const ShiftInt = std.math.Log2Int(usize);
lib/std/crypto/aes_gcm.zig+2-2
......@@ -39,7 +39,7 @@ fn AesGcm(comptime Aes: anytype) type {
3939 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
4040 aes.encrypt(&t, &j);
4141
42 const block_count = (math.divCeil(usize, ad.len, Ghash.block_length) catch unreachable) + (math.divCeil(usize, c.len, Ghash.block_length) catch unreachable) + 1;
42 const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
4343 var mac = Ghash.initForBlockCount(&h, block_count);
4444 mac.update(ad);
4545 mac.pad();
......@@ -81,7 +81,7 @@ fn AesGcm(comptime Aes: anytype) type {
8181 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
8282 aes.encrypt(&t, &j);
8383
84 const block_count = (math.divCeil(usize, ad.len, Ghash.block_length) catch unreachable) + (math.divCeil(usize, c.len, Ghash.block_length) catch unreachable) + 1;
84 const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
8585 var mac = Ghash.initForBlockCount(&h, block_count);
8686 mac.update(ad);
8787 mac.pad();
lib/std/crypto/ascon.zig+1-1
......@@ -198,7 +198,7 @@ pub fn State(comptime endian: std.builtin.Endian) type {
198198 ///
199199 /// Note: Clears complete words that contain the specified byte range
200200 pub fn clear(self: *Self, from: usize, to: usize) void {
201 @memset(self.st[from / 8 .. (to + 7) / 8], 0);
201 @memset(self.st[from / 8 .. @divCeil(to, 8)], 0);
202202 }
203203
204204 /// Clear the entire state, disabling compiler optimizations.
lib/std/crypto/ff.zig+3-3
......@@ -61,14 +61,14 @@ pub fn Uint(comptime max_bits: comptime_int) type {
6161
6262 return struct {
6363 const Self = @This();
64 const max_limbs_count = math.divCeil(usize, max_bits, t_bits) catch unreachable;
64 const max_limbs_count = @divCeil(max_bits, t_bits);
6565
6666 limbs_buffer: [max_limbs_count]Limb,
6767 /// The number of active limbs.
6868 limbs_len: usize,
6969
7070 /// Number of bytes required to serialize an integer.
71 pub const encoded_bytes = math.divCeil(usize, max_bits, 8) catch unreachable;
71 pub const encoded_bytes = @divCeil(max_bits, 8);
7272
7373 /// Constant slice of active limbs.
7474 fn limbsConst(self: *const Self) []const Limb {
......@@ -847,7 +847,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
847847 }
848848 var e_normalized = Fe{ .v = e.v.normalize() };
849849 var buf_: [Fe.encoded_bytes]u8 = undefined;
850 var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_len * t_bits, 8) catch unreachable];
850 var buf = buf_[0..@divCeil(e_normalized.v.limbs_len * t_bits, 8)];
851851 e_normalized.toBytes(buf, .little) catch unreachable;
852852 const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]);
853853 buf = buf[0 .. buf.len - leading / 8];
lib/std/crypto/pbkdf2.zig+1-1
......@@ -74,7 +74,7 @@ pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, com
7474 // block
7575 //
7676
77 const blocks_count = @as(u32, @intCast(std.math.divCeil(usize, dk_len, h_len) catch unreachable));
77 const blocks_count: u32 = @intCast(@divCeil(dk_len, h_len));
7878 var r = dk_len % h_len;
7979 if (r == 0) {
8080 r = h_len;
lib/std/crypto/sha2.zig+1-1
......@@ -474,7 +474,7 @@ fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {
474474 return struct {
475475 const Self = @This();
476476 pub const block_length = 128;
477 pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable;
477 pub const digest_length = @divCeil(digest_bits, 8);
478478 pub const Options = struct {};
479479
480480 s: Iv64,
lib/std/crypto/sha3.zig+2-2
......@@ -58,7 +58,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim
5858 st: State,
5959
6060 /// The output length, in bytes.
61 pub const digest_length = std.math.divCeil(comptime_int, output_bits, 8) catch unreachable;
61 pub const digest_length: comptime_int = @divCeil(output_bits, 8);
6262 /// The block length, or rate, in bytes.
6363 pub const block_length = State.rate;
6464 /// The delimiter can be overwritten in the options.
......@@ -464,7 +464,7 @@ pub const NistLengthEncoding = enum {
464464 /// Encode a length according to NIST SP 800-185.
465465 pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length {
466466 const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3;
467 const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable;
467 const len_bytes = @divCeil(len_bits, 8);
468468
469469 var res = Length{ .len = len_bytes + 1 };
470470 if (encoding == .right) {
lib/std/debug.zig+1-1
......@@ -349,7 +349,7 @@ pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void {
349349 var chunks = mem.window(u8, bytes, 16, 16);
350350 while (chunks.next()) |window| {
351351 // 1. Print the address.
352 const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10;
352 const address = (@intFromPtr(bytes.ptr) + 0x10 * @divCeil(chunks.index orelse bytes.len, 16) - 0x10);
353353 try t.setColor(.dim);
354354 // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
355355 // Also, make sure all lines are aligned by padding the address.
lib/std/enums.zig+1-1
......@@ -1391,7 +1391,7 @@ test "EnumIndexer non-exhaustive" {
13911391 const max_index: comptime_int = std.math.maxInt(RangedType);
13921392 const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) {
13931393 .unsigned => 0,
1394 .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable,
1394 .signed => @divCeil(max_index, 2),
13951395 };
13961396
13971397 try testing.expectEqual(E, Indexer.Key);
lib/std/hash/auto_hash.zig+1-1
......@@ -99,7 +99,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
9999 } else {
100100 // Take only the part containing the key value, the remaining
101101 // bytes are undefined and must not be hashed!
102 const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Key), 8) catch unreachable;
102 const byte_size = @divCeil(@bitSizeOf(Key), 8);
103103 @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key)[0..byte_size] });
104104 }
105105 },
lib/std/math.zig+3-14
......@@ -922,21 +922,10 @@ fn testDivFloor() !void {
922922pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
923923 @setRuntimeSafety(false);
924924 if (denominator == 0) return error.DivisionByZero;
925 const info = @typeInfo(T);
926 switch (info) {
927 .comptime_float, .float => return @ceil(numerator / denominator),
928 .comptime_int, .int => {
929 if (numerator < 0 and denominator < 0) {
930 if (info == .int and numerator == minInt(T) and denominator == -1)
931 return error.Overflow;
932 return @divFloor(numerator + 1, denominator) + 1;
933 }
934 if (numerator > 0 and denominator > 0)
935 return @divFloor(numerator - 1, denominator) + 1;
936 return @divTrunc(numerator, denominator);
937 },
938 else => @compileError("divCeil unsupported on " ++ @typeName(T)),
925 if (@typeInfo(T) == .int and numerator == minInt(T) and denominator == -1) {
926 return error.Overflow;
939927 }
928 return @divCeil(numerator, denominator);
940929}
941930
942931test divCeil {
lib/std/math/big/int.zig+76-1
......@@ -122,7 +122,7 @@ pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize {
122122/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero
123123/// and this big integer implementation stores zero using one limb.
124124pub fn calcTwosCompLimbCount(bit_count: usize) usize {
125 return @max(std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable, 1);
125 return @max(@divCeil(bit_count, @bitSizeOf(Limb)), 1);
126126}
127127
128128/// a + b * c + *carry, sets carry to the overflow bits
......@@ -1221,6 +1221,62 @@ 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 {
1264 // Same sign.
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 //
1274 // This works for both positive and negative b because b keeps its sign.
1275 q.addScalar(q.toConst(), 1);
1276 r.sub(r.toConst(), y.toConst());
1277 }
1278 }
1279
12241280 /// q = a / b (rem r)
12251281 ///
12261282 /// a / b are truncated (rounded towards -inf).
......@@ -3314,6 +3370,25 @@ pub const Managed = struct {
33143370 r.setMetadata(mr.positive, mr.len);
33153371 }
33163372
3373 /// q = a / b (rem r)
3374 ///
3375 /// a / b are ceiled (rounded towards positive infinity).
3376 ///
3377 /// Returns an error if memory could not be allocated.
3378 pub fn divCeil(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void {
3379 const q_alias = limbsAliasDistinct(q, a) or limbsAliasDistinct(q, b);
3380 const r_alias = limbsAliasDistinct(r, a) or limbsAliasDistinct(r, b);
3381 try q.ensureAliasAwareCapacity(a.len(), q_alias);
3382 try r.ensureAliasAwareCapacity(b.len(), r_alias);
3383 var mq = q.toMutable();
3384 var mr = r.toMutable();
3385 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.len(), b.len()));
3386 defer q.allocator.free(limbs_buffer);
3387 mq.divCeil(&mr, a.toConst(), b.toConst(), limbs_buffer);
3388 q.setMetadata(mq.positive, mq.len);
3389 r.setMetadata(mr.positive, mr.len);
3390 }
3391
33173392 /// q = a / b (rem r)
33183393 ///
33193394 /// a / b are truncated (rounded towards -inf).
lib/std/math/big/int_test.zig+38
......@@ -2127,6 +2127,44 @@ test "div floor positive close to zero" {
21272127 try testing.expectEqual(10, try r.toInt(i32));
21282128}
21292129
2130fn testDivCeil(comptime T: type, u: T, v: T, eq: T, er: T) !void {
2131 var a = try Managed.initSet(testing.allocator, u);
2132 defer a.deinit();
2133 var b = try Managed.initSet(testing.allocator, v);
2134 defer b.deinit();
2135
2136 var q = try Managed.init(testing.allocator);
2137 defer q.deinit();
2138 var r = try Managed.init(testing.allocator);
2139 defer r.deinit();
2140
2141 try Managed.divCeil(&q, &r, &a, &b);
2142
2143 try testing.expectEqual(eq, try q.toInt(T));
2144 try testing.expectEqual(er, try r.toInt(T));
2145}
2146
2147test "div ceil small" {
2148 try testDivCeil(i32, 5, 3, 2, -1);
2149 try testDivCeil(i32, -5, 3, -1, -2);
2150 try testDivCeil(i32, 5, -3, -1, 2);
2151 try testDivCeil(i32, -5, -3, 2, 1);
2152 try testDivCeil(i32, -0x80000000, 1, -0x80000000, 0);
2153}
2154
2155test "div ceil multi-limb" {
2156 {
2157 const a = (@as(i128, 1) << 100) + 3;
2158 const b: i128 = 4;
2159 try testDivCeil(i128, a, b, (1 << 98) + 1, -1);
2160 }
2161 {
2162 const a = -((@as(i128, 1) << 100) + 3);
2163 const b: i128 = 4;
2164 try testDivCeil(i128, a, b, -(1 << 98), -3);
2165 }
2166}
2167
21302168test "div multi-multi with rem" {
21312169 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
21322170
lib/std/mem.zig+3-3
......@@ -1937,7 +1937,7 @@ fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T
19371937 const bit_count = @as(usize, @bitSizeOf(T));
19381938 const bit_shift = @as(u3, @intCast(bit_offset % 8));
19391939
1940 const load_size = (bit_count + 7) / 8;
1940 const load_size = @divCeil(bit_count, 8);
19411941 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
19421942 const LoadInt = @Int(.unsigned, load_size * 8);
19431943
......@@ -1964,9 +1964,9 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T {
19641964
19651965 const bit_count = @as(usize, @bitSizeOf(T));
19661966 const bit_shift = @as(u3, @intCast(bit_offset % 8));
1967 const byte_count = (@as(usize, bit_shift) + bit_count + 7) / 8;
1967 const byte_count = @divCeil(@as(usize, bit_shift) + bit_count, 8);
19681968
1969 const load_size = (bit_count + 7) / 8;
1969 const load_size = @divCeil(bit_count, 8);
19701970 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
19711971 const LoadInt = @Int(.unsigned, load_size * 8);
19721972
lib/std/zig/AstGen.zig+3-1
......@@ -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,
......@@ -4895,7 +4896,7 @@ fn structDeclInner(
48954896 const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len);
48964897 const field_comptime_bits = try scratch.addOptionalSlice(
48974898 scan_result.any_comptime_fields,
4898 std.math.divCeil(u32, scan_result.fields_len, 32) catch unreachable,
4899 @divCeil(scan_result.fields_len, 32),
48994900 );
49004901 if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0);
49014902
......@@ -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+8-1
......@@ -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,
......@@ -5272,7 +5279,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe
52725279 break :lens @ptrCast(lens);
52735280 } else null;
52745281 const field_comptime_bits: ?[]const u32 = if (small.any_comptime_fields) bits: {
5275 const bits_len = std.math.divCeil(u32, fields_len, 32) catch unreachable;
5282 const bits_len = @divCeil(fields_len, 32);
52765283 const bits = zir.extra[extra_index..][0..bits_len];
52775284 extra_index += bits_len;
52785285 break :bits bits;
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+14-3
......@@ -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.
......@@ -1510,7 +1517,7 @@ pub const ShuffleTwoMask = enum(u32) {
15101517/// Trailing:
15111518/// 0. `Inst.Ref` for every outputs_len
15121519/// 1. `Inst.Ref` for every inputs_len
1513/// 2. A number of u32 elements follow according to the equation `(source_len + 3) / 4`.
1520/// 2. A number of u32 elements follow according to the equation `@divCeil(source_len, 4)`.
15141521/// Memory starting at this position is reinterpreted as the source bytes.
15151522/// 3. for every outputs_len
15161523/// - constraint: memory at this position is reinterpreted as a null
......@@ -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,
......@@ -2214,7 +2225,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
22142225 }
22152226 const pl_op = inst.data.pl_op;
22162227 const extra = air.extraData(SwitchBr, pl_op.payload);
2217 const hint_bag_count = std.math.divCeil(usize, extra.data.cases_len + 1, 10) catch unreachable;
2228 const hint_bag_count = @divCeil(extra.data.cases_len + 1, 10);
22182229 return .{
22192230 .air = air,
22202231 .operand = pl_op.operand,
......@@ -2383,7 +2394,7 @@ pub const UnwrappedAsm = struct {
23832394 const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0);
23842395 // This equation accounts for the fact that even if we have exactly 4 bytes
23852396 // for the string, we still use the next u32 for the null terminator.
2386 const next_offset = std.math.divCeil(usize, constraint.len + 1 + name.len + 1, @sizeOf(u32)) catch unreachable;
2397 const next_offset = @divCeil(constraint.len + 1 + name.len + 1, @sizeOf(u32));
23872398 self.constraint_names = self.constraint_names[next_offset..];
23882399
23892400 return .{
src/Air/Legalize.zig+194-3
......@@ -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| {
......@@ -709,7 +746,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
709746 .switch_br, .loop_switch_br => {
710747 const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op;
711748 const extra = l.extraData(Air.SwitchBr, pl_op.payload);
712 const hint_bag_count = std.math.divCeil(usize, extra.data.cases_len + 1, 10) catch unreachable;
749 const hint_bag_count = @divCeil(extra.data.cases_len + 1, 10);
713750 var extra_index = extra.end + hint_bag_count;
714751 for (0..extra.data.cases_len) |_| {
715752 const case_extra = l.extraData(Air.SwitchBr.Case, extra_index);
......@@ -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/InternPool.zig+3-3
......@@ -3581,11 +3581,11 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
35813581 .start = extra_index,
35823582 .len = extra.data.fields_len,
35833583 } else .empty;
3584 extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
3584 extra_index += @divCeil(field_aligns.len, 4);
35853585 const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{
35863586 .tid = unwrapped_index.tid,
35873587 .start = extra_index,
3588 .len = std.math.divCeil(u32, extra.data.fields_len, 32) catch unreachable,
3588 .len = @divCeil(extra.data.fields_len, 32),
35893589 } else .empty;
35903590 extra_index += field_is_comptime_bits.len;
35913591 const field_runtime_order: LoadedStructType.RuntimeOrder.Slice = if (extra.data.flags.layout == .auto) .{
......@@ -3737,7 +3737,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
37373737 .start = extra_index,
37383738 .len = extra.data.fields_len,
37393739 } else .empty;
3740 extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
3740 extra_index += @divCeil(field_aligns.len, 4);
37413741
37423742 return .{
37433743 .zir_index = extra.data.zir_index,
src/Sema.zig+73-7
......@@ -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),
......@@ -10304,7 +10305,7 @@ fn finishSwitchBr(
1030410305 fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {
1030510306 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
1030610307 if (unused_hints >= additional_count) return;
10307 const bags_required = std.math.divCeil(u32, hints.count + additional_count, hints_per_bag) catch unreachable;
10308 const bags_required = @divCeil(hints.count + additional_count, hints_per_bag);
1030810309 return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);
1030910310 }
1031010311 fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void {
......@@ -10320,7 +10321,7 @@ fn finishSwitchBr(
1032010321 }
1032110322 };
1032210323 var branch_hints: BranchHints = hints: {
10323 const num_bags = std.math.divCeil(u32, estimated_cases_len, BranchHints.hints_per_bag) catch unreachable;
10324 const num_bags = @divCeil(estimated_cases_len, BranchHints.hints_per_bag);
1032410325 break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };
1032510326 };
1032610327 defer branch_hints.bags.deinit(gpa);
......@@ -12211,7 +12212,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion(
1221112212 {
1221212213 // All branch hints are `.none`, so just add zero elems.
1221312214 comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0);
12214 const need_elems = std.math.divCeil(usize, field_indices.len + 1, 10) catch unreachable;
12215 const need_elems = @divCeil(field_indices.len + 1, 10);
1221512216 try cases_extra.appendNTimes(gpa, 0, need_elems);
1221612217 }
1221712218
......@@ -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}
......@@ -18632,7 +18698,7 @@ fn finishStructInit(
1863218698 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
1863318699 },
1863418700 .@"packed" => {
18635 const buf = try sema.arena.alloc(u8, @intCast((struct_ty.bitSize(zcu) + 7) / 8));
18701 const buf = try sema.arena.alloc(u8, @intCast(@divCeil(struct_ty.bitSize(zcu), 8)));
1863618702 @memset(buf, 0);
1863718703 var bit_offset: u16 = 0;
1863818704 for (field_inits) |field_init| {
......@@ -29679,7 +29745,7 @@ pub fn bitCastVal(
2967929745 if (val.isUndef(zcu)) {
2968029746 return pt.undefValue(dest_ty);
2968129747 } else {
29682 const buf = try sema.arena.alloc(u8, @intCast((bit_size + 7) / 8));
29748 const buf = try sema.arena.alloc(u8, @intCast(@divCeil(bit_size, 8)));
2968329749 @memset(buf, 0);
2968429750 val.writeToPackedMemory(zcu, buf, 0);
2968529751 return .readFromPackedMemory(dest_ty, pt, buf, 0);
src/Sema/arith.zig+69-3
......@@ -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)) {
2197 16 => .{ .f16 = @divCeil(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2198 32 => .{ .f32 = @divCeil(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2199 64 => .{ .f64 = @divCeil(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2200 80 => .{ .f80 = @divCeil(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2201 128 => .{ .f128 = @divCeil(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();
......@@ -2238,7 +2304,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value {
22382304 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
22392305 const info = ty.intInfo(zcu);
22402306
2241 const buf = try sema.arena.alloc(u8, (info.bits + 7) / 8);
2307 const buf = try sema.arena.alloc(u8, @divCeil(info.bits, 8));
22422308 @memset(buf, 0xAA);
22432309
22442310 const limbs = try sema.arena.alloc(
src/Type.zig+3-3
......@@ -968,7 +968,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
968968 if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
969969 if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
970970 if (vector_type.len > 64) return .@"16";
971 const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
971 const bytes = @divCeil(vector_type.len, 8);
972972 return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
973973 }
974974 const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));
......@@ -1111,10 +1111,10 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
11111111 .vector_type => |vec| {
11121112 const elem_ty: Type = .fromInterned(vec.child);
11131113 const bytes = switch (zcu.comp.getZigBackend()) {
1114 else => std.math.divCeil(u64, vec.len * elem_ty.bitSize(zcu), 8) catch unreachable,
1114 else => @divCeil(vec.len * elem_ty.bitSize(zcu), 8),
11151115 .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu),
11161116 .stage2_x86_64 => switch (elem_ty.toIntern()) {
1117 .bool_type => std.math.divCeil(u64, vec.len, 8) catch unreachable,
1117 .bool_type => @divCeil(vec.len, 8),
11181118 else => vec.len * elem_ty.abiSize(zcu),
11191119 },
11201120 };
src/codegen/aarch64/Select.zig+12-10
......@@ -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,
......@@ -403,11 +405,11 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
403405 .live_registers = undefined,
404406 .repeat_list = undefined,
405407 });
406 try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
408 try isel.dom.appendNTimes(gpa, 0, @divCeil(isel.dom_len, @bitSizeOf(DomInt)));
407409 try isel.analyze(air_body_block.body);
408410 for (
409411 isel.dom.items[initial_dom_start..].ptr,
410 isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
412 isel.dom.items[isel.dom_start..][0..@divCeil(initial_dom_len, @bitSizeOf(DomInt))],
411413 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
412414 isel.dom_start = initial_dom_start;
413415 isel.dom_len = initial_dom_len;
......@@ -589,7 +591,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
589591 .live_registers = undefined,
590592 .repeat_list = undefined,
591593 });
592 try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
594 try isel.dom.appendNTimes(gpa, 0, @divCeil(isel.dom_len, @bitSizeOf(DomInt)));
593595
594596 var cases_it = switch_br.iterateCases();
595597 while (cases_it.next()) |case| try isel.analyze(case.body);
......@@ -597,7 +599,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
597599
598600 for (
599601 isel.dom.items[initial_dom_start..].ptr,
600 isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
602 isel.dom.items[isel.dom_start..][0..@divCeil(initial_dom_len, @bitSizeOf(DomInt))],
601603 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
602604 isel.dom_start = initial_dom_start;
603605 isel.dom_len = initial_dom_len;
......@@ -10194,7 +10196,7 @@ pub const Value = struct {
1019410196 0 => unreachable,
1019510197 1...64 => unreachable,
1019610198 65...256 => |bits| if (offset == 0 and size == ty_size) {
10197 const parts_len = std.math.divCeil(u16, bits, 64) catch unreachable;
10199 const parts_len = @divCeil(bits, 64);
1019810200 vi.setParts(isel, @intCast(parts_len));
1019910201 for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8);
1020010202 },
......@@ -10238,7 +10240,7 @@ pub const Value = struct {
1023810240 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
1023910241 const array_len = array_type.lenIncludingSentinel();
1024010242 if (array_len > Value.max_parts and
10241 (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10243 (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
1024210244 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
1024310245 const alignment = vi.alignment(isel);
1024410246 const Part = struct { offset: u64, size: u64 };
......@@ -10288,7 +10290,7 @@ pub const Value = struct {
1028810290 .anyframe_type => unreachable,
1028910291 .error_union_type => |error_union_type| {
1029010292 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10291 if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10293 if ((@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
1029210294 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
1029310295 const alignment = vi.alignment(isel);
1029410296 const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
......@@ -10395,7 +10397,7 @@ pub const Value = struct {
1039510397 }
1039610398 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
1039710399 if (loaded_struct.field_types.len > Value.max_parts and
10398 (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10400 (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
1039910401 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
1040010402 const alignment = vi.alignment(isel);
1040110403 const Part = struct { offset: u64, size: u64, signedness: ?std.lang.Signedness, is_vector: bool };
......@@ -10456,7 +10458,7 @@ pub const Value = struct {
1045610458 .tuple_type => |tuple_type| {
1045710459 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
1045810460 if (tuple_type.types.len > Value.max_parts and
10459 (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10461 (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
1046010462 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
1046110463 const alignment = vi.alignment(isel);
1046210464 const Part = struct { offset: u64, size: u64, is_vector: bool };
......@@ -10511,7 +10513,7 @@ pub const Value = struct {
1051110513 } },
1051210514 }
1051310515 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10514 if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10516 if ((@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
1051510517 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
1051610518 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
1051710519 const alignment = vi.alignment(isel);
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+76-2
......@@ -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;
......@@ -6874,7 +6948,7 @@ const ParamTypeIterator = struct {
68746948 switch (ip.indexToKey(ty.toIntern())) {
68756949 .struct_type => {
68766950 const size = ty.abiSize(zcu);
6877 assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);
6951 assert(@divCeil(size, 8) == types_index);
68786952 if (size % 8 > 0) {
68796953 it.types_buffer[types_index - 1] =
68806954 try it.object.builder.intType(@intCast(size % 8 * 8));
......@@ -7119,7 +7193,7 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E
71197193 switch (ip.indexToKey(ret_ty.toIntern())) {
71207194 .struct_type => {
71217195 const size = ret_ty.abiSize(zcu);
7122 assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);
7196 assert(@divCeil(size, 8) == types_index);
71237197 if (size % 8 > 0) {
71247198 types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8));
71257199 }
src/codegen/riscv64/CodeGen.zig+3-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,
......@@ -2215,8 +2217,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void {
22152217 };
22162218
22172219 const dst_mcv = if (dst_int_info.bits <= src_storage_bits and
2218 math.divCeil(u16, dst_int_info.bits, 64) catch unreachable ==
2219 math.divCeil(u32, src_storage_bits, 64) catch unreachable and
2220 @divCeil(dst_int_info.bits, 64) == @divCeil(src_storage_bits, 64) and
22202221 func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
22212222 const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true);
22222223 try func.genCopy(min_ty, dst_mcv, src_mcv);
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/spirv/Assembler.zig+1-1
......@@ -375,7 +375,7 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {
375375 },
376376 .string => |offset| {
377377 const text = std.mem.sliceTo(ass.inst.string_bytes.items[offset..], 0);
378 const size = std.math.divCeil(usize, text.len + 1, @sizeOf(Word)) catch unreachable;
378 const size = @divCeil(text.len + 1, @sizeOf(Word));
379379 try section.ensureUnusedCapacity(cg.gpa, size);
380380 section.writeOperand(spec.LiteralString, text);
381381 },
src/codegen/spirv/Section.zig+1-1
......@@ -232,7 +232,7 @@ fn operandSize(comptime Operand: type, operand: Operand) usize {
232232 return switch (Operand) {
233233 spec.LiteralSpecConstantOpInteger => unreachable,
234234 spec.Id, spec.LiteralInteger, spec.LiteralExtInstInteger => 1,
235 spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable,
235 spec.LiteralString => @divCeil(operand.len + 1, @sizeOf(Word)),
236236 spec.LiteralContextDependentNumber => switch (operand) {
237237 .int32, .uint32, .float32 => 1,
238238 .int64, .uint64, .float64 => 2,
src/codegen/wasm/CodeGen.zig+106-3
......@@ -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,
......@@ -3581,7 +3678,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35813678
35823679 const result = try cg.allocInt(ty);
35833680
3584 const used_len = (math.divCeil(u16, ty.bits, 64) catch unreachable) * 8;
3681 const used_len = @divCeil(ty.bits, 64) * 8;
35853682
35863683 if (ty.bits % 64 != 0) {
35873684 try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 });
......@@ -3647,7 +3744,7 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
36473744 } else {
36483745 const result = try cg.allocInt(int_ty);
36493746 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
3650 const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8;
3747 const used_len = @divCeil(int_ty.bits, 64) * 8;
36513748
36523749 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF });
36533750
......@@ -3681,7 +3778,7 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
36813778 } else {
36823779 const result = try cg.allocInt(int_ty);
36833780 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
3684 const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8;
3781 const used_len = @divCeil(int_ty.bits, 64) * 8;
36853782
36863783 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 });
36873784 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - (used_len - 8) * 8 - 1) }, Type.u64, used_len - 8);
......@@ -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+11-6
......@@ -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
......@@ -174781,7 +174786,7 @@ fn genShiftBinOpMir(
174781174786 try self.spillEflagsIfOccupied();
174782174787
174783174788 if (abi_size > 16) {
174784 const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable;
174789 const limbs_len = @divCeil(abi_size, 8);
174785174790 assert(shift_abi_size >= 1 and shift_abi_size <= 2);
174786174791
174787174792 const rcx_lock: ?RegisterLock = switch (rhs_mcv) {
......@@ -179593,7 +179598,7 @@ fn genSetReg(
179593179598 if (pack_alias != sign_alias) try cg.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, sign_alias);
179594179599 try cg.asmRegisterRegister(.{ .p_b, .ackssw }, pack_alias, pack_alias);
179595179600 }
179596 mask_size = std.math.divCeil(u32, mask_size, 2) catch unreachable;
179601 mask_size = @divCeil(mask_size, 2);
179597179602 break :pack_reg pack_reg;
179598179603 },
179599179604 };
......@@ -180157,7 +180162,7 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
180157180162 const bit_size = dst_ty.bitSize(zcu);
180158180163 if (abi_size * 8 <= bit_size) break :result dst_mcv;
180159180164
180160 const dst_limbs_len = std.math.divCeil(u31, @intCast(bit_size), 64) catch unreachable;
180165 const dst_limbs_len: u31 = @intCast(@divCeil(bit_size, 64));
180161180166 const high_mcv: MCValue = switch (dst_mcv) {
180162180167 .register => |dst_reg| .{ .register = dst_reg },
180163180168 .register_pair => |dst_regs| .{ .register = dst_regs[1] },
......@@ -183550,7 +183555,7 @@ const Temp = struct {
183550183555 const part_ty: Type = if (src_regs.len == 1)
183551183556 src_ty
183552183557 else if (cg.intInfo(src_ty)) |int_info| part_ty: {
183553 assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable);
183558 assert(src_regs.len == @divCeil(int_info.bits, 64));
183554183559 break :part_ty .u64;
183555183560 } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {
183556183561 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
......@@ -183560,7 +183565,7 @@ const Temp = struct {
183560183565 break :part_ty .usize;
183561183566 },
183562183567 .array_type => {
183563 assert(src_regs.len - part_index == std.math.divCeil(u32, src_size, 8) catch unreachable);
183568 assert(src_regs.len - part_index == @divCeil(src_size, 8));
183564183569 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_size, 8));
183565183570 },
183566183571 .vector_type => |vector_type| switch (@divExact(vector_type.len, src_regs.len)) {
......@@ -183580,7 +183585,7 @@ const Temp = struct {
183580183585 },
183581183586 },
183582183587 .struct_type, .union_type => {
183583 assert(src_regs.len - part_index == std.math.divCeil(u32, src_size, 8) catch unreachable);
183588 assert(src_regs.len - part_index == @divCeil(src_size, 8));
183584183589 break :part_ty switch (src_size) {
183585183590 0, 3, 5...7 => unreachable,
183586183591 1 => .u8,
src/codegen/x86_64/Emit.zig+1-1
......@@ -772,7 +772,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
772772 const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
773773 .rip_inst => 4,
774774 else => unreachable,
775 } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);
775 } else @intCast(@divCeil(op.immBitSize(), 8));
776776 reloc_offset -= enc_length;
777777 if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
778778 assert(!is_mem);
src/link/Dwarf.zig+3-3
......@@ -2137,7 +2137,7 @@ pub const WipNav = struct {
21372137 .signed => DW.FORM.sdata,
21382138 .unsigned => DW.FORM.udata,
21392139 }));
2140 try wip_nav.debug_info.ensureUnusedCapacity(std.math.divCeil(usize, bits, 7) catch unreachable);
2140 try wip_nav.debug_info.ensureUnusedCapacity(@divCeil(bits, 7));
21412141 var bit: usize = 0;
21422142 var carry: u1 = 1;
21432143 while (bit < bits) {
......@@ -2158,7 +2158,7 @@ pub const WipNav = struct {
21582158 }
21592159 } else {
21602160 try diw.writeUleb128(DW.FORM.block);
2161 const bytes = @max(ty.abiSize(zcu), std.math.divCeil(usize, bits, 8) catch unreachable);
2161 const bytes = @max(ty.abiSize(zcu), @divCeil(bits, 8));
21622162 try diw.writeUleb128(bytes);
21632163 try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes));
21642164 big_int.writeTwosComplement(
......@@ -4275,7 +4275,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
42754275 try wip_nav.abbrevCode(.aggregate_undefined_comptime_value);
42764276 try wip_nav.refType(.fromInterned(error_union.ty));
42774277 var err_buf: [4]u8 = undefined;
4278 const err_bytes = err_buf[0 .. std.math.divCeil(u17, zcu.errorSetBits(), 8) catch unreachable];
4278 const err_bytes = err_buf[0..@divCeil(zcu.errorSetBits(), 8)];
42794279 dwarf.writeInt(err_bytes, switch (error_union.val) {
42804280 .err_name => |err_name| try pt.getErrorValue(err_name),
42814281 .payload => 0,
src/main.zig+1-2
......@@ -162,8 +162,7 @@ const use_safe_allocator = build_options.debug_gpa or
162162 .ReleaseFast, .ReleaseSmall => false,
163163 });
164164
165// TODO: The `align(@alignOf(std.heap.SafeAllocator))` can be removed the next time zig1.wasm is updated
166var safe_allocator: std.heap.SafeAllocator align(@alignOf(std.heap.SafeAllocator)) = .init(std.heap.page_allocator, .{
165var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{
167166 .stack_trace_frames = build_options.mem_leak_frames,
168167});
169168
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,
stage1/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)); \
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
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