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>...@@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}</pre></td>
1370 <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li>1370 <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li>
1371 <li>Signed integer operands must be comptime-known and positive. In other cases, use1371 <li>Signed integer operands must be comptime-known and positive. In other cases, use
1372 {#link|@divTrunc#},1372 {#link|@divTrunc#},
1373 {#link|@divFloor#}, or1373 {#link|@divFloor#},
1374 {#link|@divCeil#}, or
1374 {#link|@divExact#} instead.1375 {#link|@divExact#} instead.
1375 </li>1376 </li>
1376 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>1377 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
...@@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4735 <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>4736 <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>
4736 </ul>4737 </ul>
4737 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p>4738 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p>
4738 {#see_also|@divTrunc|@divFloor#}4739 {#see_also|@divTrunc|@divFloor|@divCeil#}
4739 {#header_close#}4740 {#header_close#}
4740 {#header_open|@divFloor#}4741 {#header_open|@divFloor#}
4741 <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre>4742 <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre>
...@@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4749 <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>4750 <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>
4750 </ul>4751 </ul>
4751 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p>4752 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p>
4752 {#see_also|@divTrunc|@divExact#}4753 {#see_also|@divTrunc|@divCeil|@divExact#}
4753 {#header_close#}4754 {#header_close#}
4754 {#header_open|@divTrunc#}4755 {#header_open|@divTrunc#}
4755 <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre>4756 <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre>
...@@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4763 <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>4764 <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>
4764 </ul>4765 </ul>
4765 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p>4766 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p>
4766 {#see_also|@divFloor|@divExact#}4767 {#see_also|@divFloor|@divCeil|@divExact#}
4768 {#header_close#}
4769 {#header_open|@divCeil#}
4770 <pre>{#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}</pre>
4771 <p>
4772 Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and
4773 {#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}.
4774 </p>
4775 <ul>
4776 <li>{#syntax#}@divCeil(5, 3) == 2{#endsyntax#}</li>
4777 <li>{#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}</li>
4778 </ul>
4779 <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.</p>
4780 {#see_also|@divFloor|@divTrunc|@divExact#}
4767 {#header_close#}4781 {#header_close#}
47684782
4769 {#header_open|@embedFile#}4783 {#header_open|@embedFile#}
...@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
6095 <li>{#syntax#}/{#endsyntax#} (division)</li>6109 <li>{#syntax#}/{#endsyntax#} (division)</li>
6096 <li>{#link|@divTrunc#} (division)</li>6110 <li>{#link|@divTrunc#} (division)</li>
6097 <li>{#link|@divFloor#} (division)</li>6111 <li>{#link|@divFloor#} (division)</li>
6112 <li>{#link|@divCeil#} (division)</li>
6098 <li>{#link|@divExact#} (division)</li>6113 <li>{#link|@divExact#} (division)</li>
6099 </ul>6114 </ul>
6100 <p>Example with addition at compile-time:</p>6115 <p>Example with addition at compile-time:</p>
...@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
6112 <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>6127 <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>
6113 <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>6128 <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>
6114 <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>6129 <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>
6130 <li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li>
6115 <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>6131 <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>
6116 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>6132 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
6117 </ul>6133 </ul>
lib/compiler/Maker/WebServer.zig+1-1
...@@ -163,7 +163,7 @@ pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void {...@@ -163,7 +163,7 @@ pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void {
163 assert(idx == step_names_trailing.len);163 assert(idx == step_names_trailing.len);
164 }164 }
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));
167 errdefer gpa.free(step_status_bits);167 errdefer gpa.free(step_status_bits);
168 @memset(step_status_bits, 0);168 @memset(step_status_bits, 0);
169169
lib/compiler_rt/limb64.zig+1-2
...@@ -3,7 +3,6 @@ const testing = std.testing;...@@ -3,7 +3,6 @@ const testing = std.testing;
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const minInt = std.math.minInt;5const minInt = std.math.minInt;
6const divCeil = std.math.divCeil;
76
8const builtin = @import("builtin");7const builtin = @import("builtin");
9const compiler_rt = @import("../compiler_rt.zig");8const compiler_rt = @import("../compiler_rt.zig");
...@@ -26,7 +25,7 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {...@@ -26,7 +25,7 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {
26}25}
2726
28fn usedLimbCount(bits: u16) u16 {27fn usedLimbCount(bits: u16) u16 {
29 return divCeil(u16, bits, 64) catch unreachable;28 return @divCeil(bits, 64);
30}29}
3130
32fn limbCount(bits: u16) u16 {31fn limbCount(bits: u16) u16 {
lib/compiler_rt/udivmodei4.zig+1-1
...@@ -8,7 +8,7 @@ const shl = std.math.shl;...@@ -8,7 +8,7 @@ const shl = std.math.shl;
8const compiler_rt = @import("../compiler_rt.zig");8const compiler_rt = @import("../compiler_rt.zig");
9const symbol = @import("../compiler_rt.zig").symbol;9const symbol = @import("../compiler_rt.zig").symbol;
1010
11const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max supported type is u6553511const max_limbs = @divCeil(65535, 32); // max supported type is u65535
1212
13comptime {13comptime {
14 symbol(&__udivei4, "__udivei4");14 symbol(&__udivei4, "__udivei4");
lib/fuzzer.zig+1-1
...@@ -52,7 +52,7 @@ var fuzzer: Fuzzer = undefined;...@@ -52,7 +52,7 @@ var fuzzer: Fuzzer = undefined;
52var current_test_name: ?[]const u8 = null;52var current_test_name: ?[]const u8 = null;
5353
54fn bitsetUsizes(elems: usize) usize {54fn bitsetUsizes(elems: usize) usize {
55 return math.divCeil(usize, elems, @bitSizeOf(usize)) catch unreachable;55 return @divCeil(elems, @bitSizeOf(usize));
56}56}
5757
58const Executable = struct {58const Executable = struct {
lib/std/Random.zig+1-1
...@@ -126,7 +126,7 @@ pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: ty...@@ -126,7 +126,7 @@ pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: ty
126pub fn int(r: Random, comptime T: type) T {126pub fn int(r: Random, comptime T: type) T {
127 const bits = @typeInfo(T).int.bits;127 const bits = @typeInfo(T).int.bits;
128 const UnsignedT = @Int(.unsigned, bits);128 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);
130 const ByteAlignedT = @Int(.unsigned, ceil_bytes * 8);130 const ByteAlignedT = @Int(.unsigned, ceil_bytes * 8);
131131
132 var rand_bytes: [ceil_bytes]u8 = undefined;132 var rand_bytes: [ceil_bytes]u8 = undefined;
lib/std/Target.zig+1-1
...@@ -1232,7 +1232,7 @@ pub const Cpu = struct {...@@ -1232,7 +1232,7 @@ pub const Cpu = struct {
1232 ints: [usize_count]usize,1232 ints: [usize_count]usize,
12331233
1234 pub const needed_bit_count = 347;1234 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);
1236 pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);1236 pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);
1237 pub const Index = std.math.Log2Int(@Int(.unsigned, usize_count * @bitSizeOf(usize)));1237 pub const Index = std.math.Log2Int(@Int(.unsigned, usize_count * @bitSizeOf(usize)));
1238 pub const ShiftInt = std.math.Log2Int(usize);1238 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 {...@@ -39,7 +39,7 @@ fn AesGcm(comptime Aes: anytype) type {
39 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);39 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
40 aes.encrypt(&t, &j);40 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;
43 var mac = Ghash.initForBlockCount(&h, block_count);43 var mac = Ghash.initForBlockCount(&h, block_count);
44 mac.update(ad);44 mac.update(ad);
45 mac.pad();45 mac.pad();
...@@ -81,7 +81,7 @@ fn AesGcm(comptime Aes: anytype) type {...@@ -81,7 +81,7 @@ fn AesGcm(comptime Aes: anytype) type {
81 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);81 mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
82 aes.encrypt(&t, &j);82 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;
85 var mac = Ghash.initForBlockCount(&h, block_count);85 var mac = Ghash.initForBlockCount(&h, block_count);
86 mac.update(ad);86 mac.update(ad);
87 mac.pad();87 mac.pad();
lib/std/crypto/ascon.zig+1-1
...@@ -198,7 +198,7 @@ pub fn State(comptime endian: std.builtin.Endian) type {...@@ -198,7 +198,7 @@ pub fn State(comptime endian: std.builtin.Endian) type {
198 ///198 ///
199 /// Note: Clears complete words that contain the specified byte range199 /// Note: Clears complete words that contain the specified byte range
200 pub fn clear(self: *Self, from: usize, to: usize) void {200 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);
202 }202 }
203203
204 /// Clear the entire state, disabling compiler optimizations.204 /// 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 {...@@ -61,14 +61,14 @@ pub fn Uint(comptime max_bits: comptime_int) type {
6161
62 return struct {62 return struct {
63 const Self = @This();63 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
66 limbs_buffer: [max_limbs_count]Limb,66 limbs_buffer: [max_limbs_count]Limb,
67 /// The number of active limbs.67 /// The number of active limbs.
68 limbs_len: usize,68 limbs_len: usize,
6969
70 /// Number of bytes required to serialize an integer.70 /// 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
73 /// Constant slice of active limbs.73 /// Constant slice of active limbs.
74 fn limbsConst(self: *const Self) []const Limb {74 fn limbsConst(self: *const Self) []const Limb {
...@@ -847,7 +847,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type {...@@ -847,7 +847,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
847 }847 }
848 var e_normalized = Fe{ .v = e.v.normalize() };848 var e_normalized = Fe{ .v = e.v.normalize() };
849 var buf_: [Fe.encoded_bytes]u8 = undefined;849 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)];
851 e_normalized.toBytes(buf, .little) catch unreachable;851 e_normalized.toBytes(buf, .little) catch unreachable;
852 const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]);852 const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]);
853 buf = buf[0 .. buf.len - leading / 8];853 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...@@ -74,7 +74,7 @@ pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, com
74 // block74 // block
75 //75 //
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));
78 var r = dk_len % h_len;78 var r = dk_len % h_len;
79 if (r == 0) {79 if (r == 0) {
80 r = h_len;80 r = h_len;
lib/std/crypto/sha2.zig+1-1
...@@ -474,7 +474,7 @@ fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {...@@ -474,7 +474,7 @@ fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {
474 return struct {474 return struct {
475 const Self = @This();475 const Self = @This();
476 pub const block_length = 128;476 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);
478 pub const Options = struct {};478 pub const Options = struct {};
479479
480 s: Iv64,480 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...@@ -58,7 +58,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim
58 st: State,58 st: State,
5959
60 /// The output length, in bytes.60 /// 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);
62 /// The block length, or rate, in bytes.62 /// The block length, or rate, in bytes.
63 pub const block_length = State.rate;63 pub const block_length = State.rate;
64 /// The delimiter can be overwritten in the options.64 /// The delimiter can be overwritten in the options.
...@@ -464,7 +464,7 @@ pub const NistLengthEncoding = enum {...@@ -464,7 +464,7 @@ pub const NistLengthEncoding = enum {
464 /// Encode a length according to NIST SP 800-185.464 /// Encode a length according to NIST SP 800-185.
465 pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length {465 pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length {
466 const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3;466 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
469 var res = Length{ .len = len_bytes + 1 };469 var res = Length{ .len = len_bytes + 1 };
470 if (encoding == .right) {470 if (encoding == .right) {
lib/std/debug.zig+1-1
...@@ -349,7 +349,7 @@ pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void {...@@ -349,7 +349,7 @@ pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void {
349 var chunks = mem.window(u8, bytes, 16, 16);349 var chunks = mem.window(u8, bytes, 16, 16);
350 while (chunks.next()) |window| {350 while (chunks.next()) |window| {
351 // 1. Print the address.351 // 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);
353 try t.setColor(.dim);353 try t.setColor(.dim);
354 // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.354 // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
355 // Also, make sure all lines are aligned by padding the address.355 // 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" {...@@ -1391,7 +1391,7 @@ test "EnumIndexer non-exhaustive" {
1391 const max_index: comptime_int = std.math.maxInt(RangedType);1391 const max_index: comptime_int = std.math.maxInt(RangedType);
1392 const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) {1392 const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) {
1393 .unsigned => 0,1393 .unsigned => 0,
1394 .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable,1394 .signed => @divCeil(max_index, 2),
1395 };1395 };
13961396
1397 try testing.expectEqual(E, Indexer.Key);1397 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 {...@@ -99,7 +99,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
99 } else {99 } else {
100 // Take only the part containing the key value, the remaining100 // Take only the part containing the key value, the remaining
101 // bytes are undefined and must not be hashed!101 // 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);
103 @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key)[0..byte_size] });103 @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key)[0..byte_size] });
104 }104 }
105 },105 },
lib/std/math.zig+3-14
...@@ -922,21 +922,10 @@ fn testDivFloor() !void {...@@ -922,21 +922,10 @@ fn testDivFloor() !void {
922pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {922pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
923 @setRuntimeSafety(false);923 @setRuntimeSafety(false);
924 if (denominator == 0) return error.DivisionByZero;924 if (denominator == 0) return error.DivisionByZero;
925 const info = @typeInfo(T);925 if (@typeInfo(T) == .int and numerator == minInt(T) and denominator == -1) {
926 switch (info) {926 return error.Overflow;
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)),
939 }927 }
928 return @divCeil(numerator, denominator);
940}929}
941930
942test divCeil {931test divCeil {
lib/std/math/big/int.zig+76-1
...@@ -122,7 +122,7 @@ pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize {...@@ -122,7 +122,7 @@ pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize {
122/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero122/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero
123/// and this big integer implementation stores zero using one limb.123/// and this big integer implementation stores zero using one limb.
124pub fn calcTwosCompLimbCount(bit_count: usize) usize {124pub 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);
126}126}
127127
128/// a + b * c + *carry, sets carry to the overflow bits128/// a + b * c + *carry, sets carry to the overflow bits
...@@ -1221,6 +1221,62 @@ pub const Mutable = struct {...@@ -1221,6 +1221,62 @@ pub const Mutable = struct {
1221 }1221 }
1222 }1222 }
12231223
1224 /// q = a / b (rem r)
1225 ///
1226 /// a / b are ceiled (rounded towards +inf).
1227 /// q may alias with a or b.
1228 ///
1229 /// Asserts there is enough memory to store q and r.
1230 /// The upper bound for r limb count is `b.limbs.len`.
1231 /// The upper bound for q limb count is given by `a.limbs`.
1232 ///
1233 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
1234 pub fn divCeil(
1235 q: *Mutable,
1236 r: *Mutable,
1237 a: Const,
1238 b: Const,
1239 limbs_buffer: []Limb,
1240 ) void {
1241 const sep = a.limbs.len + 2;
1242 var x = a.toMutable(limbs_buffer[0..sep]);
1243 var y = b.toMutable(limbs_buffer[sep..]);
1244
1245 // div performs truncating division (@divTrunc) which rounds towards negative
1246 // infinity if the result is positive and towards positive infinity if the result is
1247 // negative.
1248 div(q, r, &x, &y);
1249
1250 // @rem gives the remainder after @divTrunc, and is defined by:
1251 // x * @divTrunc(x, y) + @rem(x, y) = x
1252 // For all integers x, y with y != 0.
1253 // In the following comments, a, b will be integers with a >= 0, b > 0, and we will take
1254 // modCeil to be the remainder after @divCeil, defined by:
1255 // x * @divCeil(x, y) + modCeil(x, y) = x
1256 // For all integers x, y with y != 0.
1257
1258 if (a.positive != b.positive or r.eqlZero()) {
1259 // In this case either the result is negative or the remainder is 0.
1260 // If the result is negative then the default truncating division already rounds
1261 // towards positive infinity, so no adjustment is needed.
1262 // If the remainder is 0 then the division is exact and no adjustment is needed.
1263 } else {
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
1224 /// q = a / b (rem r)1280 /// q = a / b (rem r)
1225 ///1281 ///
1226 /// a / b are truncated (rounded towards -inf).1282 /// a / b are truncated (rounded towards -inf).
...@@ -3314,6 +3370,25 @@ pub const Managed = struct {...@@ -3314,6 +3370,25 @@ pub const Managed = struct {
3314 r.setMetadata(mr.positive, mr.len);3370 r.setMetadata(mr.positive, mr.len);
3315 }3371 }
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
3317 /// q = a / b (rem r)3392 /// q = a / b (rem r)
3318 ///3393 ///
3319 /// a / b are truncated (rounded towards -inf).3394 /// 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" {...@@ -2127,6 +2127,44 @@ test "div floor positive close to zero" {
2127 try testing.expectEqual(10, try r.toInt(i32));2127 try testing.expectEqual(10, try r.toInt(i32));
2128}2128}
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
2130test "div multi-multi with rem" {2168test "div multi-multi with rem" {
2131 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;2169 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...@@ -1937,7 +1937,7 @@ fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T
1937 const bit_count = @as(usize, @bitSizeOf(T));1937 const bit_count = @as(usize, @bitSizeOf(T));
1938 const bit_shift = @as(u3, @intCast(bit_offset % 8));1938 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);
1941 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));1941 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
1942 const LoadInt = @Int(.unsigned, load_size * 8);1942 const LoadInt = @Int(.unsigned, load_size * 8);
19431943
...@@ -1964,9 +1964,9 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T {...@@ -1964,9 +1964,9 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T {
19641964
1965 const bit_count = @as(usize, @bitSizeOf(T));1965 const bit_count = @as(usize, @bitSizeOf(T));
1966 const bit_shift = @as(u3, @intCast(bit_offset % 8));1966 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);
1970 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));1970 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
1971 const LoadInt = @Int(.unsigned, load_size * 8);1971 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...@@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2845 .bit_reverse,2845 .bit_reverse,
2846 .div_exact,2846 .div_exact,
2847 .div_floor,2847 .div_floor,
2848 .div_ceil,
2848 .div_trunc,2849 .div_trunc,
2849 .mod,2850 .mod,
2850 .rem,2851 .rem,
...@@ -4895,7 +4896,7 @@ fn structDeclInner(...@@ -4895,7 +4896,7 @@ fn structDeclInner(
4895 const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len);4896 const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len);
4896 const field_comptime_bits = try scratch.addOptionalSlice(4897 const field_comptime_bits = try scratch.addOptionalSlice(
4897 scan_result.any_comptime_fields,4898 scan_result.any_comptime_fields,
4898 std.math.divCeil(u32, scan_result.fields_len, 32) catch unreachable,4899 @divCeil(scan_result.fields_len, 32),
4899 );4900 );
4900 if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0);4901 if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0);
49014902
...@@ -9392,6 +9393,7 @@ fn builtinCall(...@@ -9392,6 +9393,7 @@ fn builtinCall(
93929393
9393 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),9394 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
9394 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),9395 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
9396 .div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil),
9395 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),9397 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
9396 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),9398 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
9397 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),9399 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
lib/std/zig/AstRlAnnotate.zig+1
...@@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
936 },936 },
937 .div_exact,937 .div_exact,
938 .div_floor,938 .div_floor,
939 .div_ceil,
939 .div_trunc,940 .div_trunc,
940 .mod,941 .mod,
941 .rem,942 .rem,
lib/std/zig/BuiltinFn.zig+8
...@@ -31,6 +31,7 @@ pub const Tag = enum {...@@ -31,6 +31,7 @@ pub const Tag = enum {
31 c_va_copy,31 c_va_copy,
32 c_va_end,32 c_va_end,
33 c_va_start,33 c_va_start,
34 div_ceil,
34 div_exact,35 div_exact,
35 div_floor,36 div_floor,
36 div_trunc,37 div_trunc,
...@@ -398,6 +399,13 @@ pub const list = list: {...@@ -398,6 +399,13 @@ pub const list = list: {
398 .param_count = 2,399 .param_count = 2,
399 },400 },
400 },401 },
402 .{
403 "@divCeil",
404 .{
405 .tag = .div_ceil,
406 .param_count = 2,
407 },
408 },
401 .{409 .{
402 "@divTrunc",410 "@divTrunc",
403 .{411 .{
lib/std/zig/Zir.zig+8-1
...@@ -200,6 +200,9 @@ pub const Inst = struct {...@@ -200,6 +200,9 @@ pub const Inst = struct {
200 /// Implements the `@divFloor` builtin.200 /// Implements the `@divFloor` builtin.
201 /// Uses the `pl_node` union field with payload `Bin`.201 /// Uses the `pl_node` union field with payload `Bin`.
202 div_floor,202 div_floor,
203 /// Implements the `@divCeil` builtin.
204 /// Uses the `pl_node` union field with payload `Bin`.
205 div_ceil,
203 /// Implements the `@divTrunc` builtin.206 /// Implements the `@divTrunc` builtin.
204 /// Uses the `pl_node` union field with payload `Bin`.207 /// Uses the `pl_node` union field with payload `Bin`.
205 div_trunc,208 div_trunc,
...@@ -1267,6 +1270,7 @@ pub const Inst = struct {...@@ -1267,6 +1270,7 @@ pub const Inst = struct {
1267 .bit_reverse,1270 .bit_reverse,
1268 .div_exact,1271 .div_exact,
1269 .div_floor,1272 .div_floor,
1273 .div_ceil,
1270 .div_trunc,1274 .div_trunc,
1271 .mod,1275 .mod,
1272 .rem,1276 .rem,
...@@ -1547,6 +1551,7 @@ pub const Inst = struct {...@@ -1547,6 +1551,7 @@ pub const Inst = struct {
1547 .bit_reverse,1551 .bit_reverse,
1548 .div_exact,1552 .div_exact,
1549 .div_floor,1553 .div_floor,
1554 .div_ceil,
1550 .div_trunc,1555 .div_trunc,
1551 .mod,1556 .mod,
1552 .rem,1557 .rem,
...@@ -1815,6 +1820,7 @@ pub const Inst = struct {...@@ -1815,6 +1820,7 @@ pub const Inst = struct {
18151820
1816 .div_exact = .pl_node,1821 .div_exact = .pl_node,
1817 .div_floor = .pl_node,1822 .div_floor = .pl_node,
1823 .div_ceil = .pl_node,
1818 .div_trunc = .pl_node,1824 .div_trunc = .pl_node,
1819 .mod = .pl_node,1825 .mod = .pl_node,
1820 .rem = .pl_node,1826 .rem = .pl_node,
...@@ -4115,6 +4121,7 @@ fn findTrackableInner(...@@ -4115,6 +4121,7 @@ fn findTrackableInner(
4115 .mul_sat,4121 .mul_sat,
4116 .div_exact,4122 .div_exact,
4117 .div_floor,4123 .div_floor,
4124 .div_ceil,
4118 .div_trunc,4125 .div_trunc,
4119 .mod,4126 .mod,
4120 .rem,4127 .rem,
...@@ -5272,7 +5279,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe...@@ -5272,7 +5279,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe
5272 break :lens @ptrCast(lens);5279 break :lens @ptrCast(lens);
5273 } else null;5280 } else null;
5274 const field_comptime_bits: ?[]const u32 = if (small.any_comptime_fields) bits: {5281 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);
5276 const bits = zir.extra[extra_index..][0..bits_len];5283 const bits = zir.extra[extra_index..][0..bits_len];
5277 extra_index += bits_len;5284 extra_index += bits_len;
5278 break :bits bits;5285 break :bits bits;
lib/zig.h+32
...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \
815 } \815 } \
816\
817 static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
818 return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
819 } \
820\
821 static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
822 return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
823 ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
824 } \
816\825\
817 zig_basic_operator(uint##w##_t, mod_u##w, %) \826 zig_basic_operator(uint##w##_t, mod_u##w, %) \
818\827\
...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
2058 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));2067 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
2059}2068}
20602069
2070static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
2071 zig_u128 rem = zig_rem_u128(lhs, rhs);
2072 uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
2073 ? UINT64_C(1) : UINT64_C(0);
2074 return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
2075}
2076
2077static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
2078 zig_i128 rem = zig_rem_i128(lhs, rhs);
2079 int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
2080 ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
2081 : INT64_C(0);
2082 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
2083}
2084
2061#define zig_mod_u128 zig_rem_u1282085#define zig_mod_u128 zig_rem_u128
20622086
2063static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {2087static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs
3251 zig_trap();3275 zig_trap();
3252}3276}
32533277
3278static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3279 zig_trap();
3280}
3281
3254zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);3282zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
3255static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {3283static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3256 if (!is_signed) {3284 if (!is_signed) {
...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
4010 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \4038 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
4011 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \4039 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
4012 } \4040 } \
4041\
4042 static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
4043 return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
4044 } \
4013\4045\
4014 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \4046 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
4015 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \4047 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
src/Air.zig+14-3
...@@ -143,6 +143,13 @@ pub const Inst = struct {...@@ -143,6 +143,13 @@ pub const Inst = struct {
143 div_floor,143 div_floor,
144 /// Same as `div_floor` with optimized float mode.144 /// Same as `div_floor` with optimized float mode.
145 div_floor_optimized,145 div_floor_optimized,
146 /// Ceiling integer or float division. For integers, wrapping is illegal behavior.
147 /// Both operands are guaranteed to be the same type, and the result type
148 /// is the same as both operands.
149 /// Uses the `bin_op` field.
150 div_ceil,
151 /// Same as `div_ceil` with optimized float mode.
152 div_ceil_optimized,
146 /// Integer or float division.153 /// Integer or float division.
147 /// If a remainder would be produced, illegal behavior occurs.154 /// If a remainder would be produced, illegal behavior occurs.
148 /// For integers, overflow is illegal behavior.155 /// For integers, overflow is illegal behavior.
...@@ -1510,7 +1517,7 @@ pub const ShuffleTwoMask = enum(u32) {...@@ -1510,7 +1517,7 @@ pub const ShuffleTwoMask = enum(u32) {
1510/// Trailing:1517/// Trailing:
1511/// 0. `Inst.Ref` for every outputs_len1518/// 0. `Inst.Ref` for every outputs_len
1512/// 1. `Inst.Ref` for every inputs_len1519/// 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)`.
1514/// Memory starting at this position is reinterpreted as the source bytes.1521/// Memory starting at this position is reinterpreted as the source bytes.
1515/// 3. for every outputs_len1522/// 3. for every outputs_len
1516/// - constraint: memory at this position is reinterpreted as a null1523/// - 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)...@@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1605 .div_float,1612 .div_float,
1606 .div_trunc,1613 .div_trunc,
1607 .div_floor,1614 .div_floor,
1615 .div_ceil,
1608 .div_exact,1616 .div_exact,
1609 .rem,1617 .rem,
1610 .mod,1618 .mod,
...@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1624 .div_float_optimized,1632 .div_float_optimized,
1625 .div_trunc_optimized,1633 .div_trunc_optimized,
1626 .div_floor_optimized,1634 .div_floor_optimized,
1635 .div_ceil_optimized,
1627 .div_exact_optimized,1636 .div_exact_optimized,
1628 .rem_optimized,1637 .rem_optimized,
1629 .mod_optimized,1638 .mod_optimized,
...@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1985 .div_trunc_optimized,1994 .div_trunc_optimized,
1986 .div_floor,1995 .div_floor,
1987 .div_floor_optimized,1996 .div_floor_optimized,
1997 .div_ceil,
1998 .div_ceil_optimized,
1988 .div_exact,1999 .div_exact,
1989 .div_exact_optimized,2000 .div_exact_optimized,
1990 .rem,2001 .rem,
...@@ -2214,7 +2225,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {...@@ -2214,7 +2225,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
2214 }2225 }
2215 const pl_op = inst.data.pl_op;2226 const pl_op = inst.data.pl_op;
2216 const extra = air.extraData(SwitchBr, pl_op.payload);2227 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);
2218 return .{2229 return .{
2219 .air = air,2230 .air = air,
2220 .operand = pl_op.operand,2231 .operand = pl_op.operand,
...@@ -2383,7 +2394,7 @@ pub const UnwrappedAsm = struct {...@@ -2383,7 +2394,7 @@ pub const UnwrappedAsm = struct {
2383 const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0);2394 const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0);
2384 // This equation accounts for the fact that even if we have exactly 4 bytes2395 // This equation accounts for the fact that even if we have exactly 4 bytes
2385 // for the string, we still use the next u32 for the null terminator.2396 // 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));
2387 self.constraint_names = self.constraint_names[next_offset..];2398 self.constraint_names = self.constraint_names[next_offset..];
23882399
2389 return .{2400 return .{
src/Air/Legalize.zig+194-3
...@@ -54,6 +54,8 @@ pub const Feature = enum {...@@ -54,6 +54,8 @@ pub const Feature = enum {
54 scalarize_div_trunc_optimized,54 scalarize_div_trunc_optimized,
55 scalarize_div_floor,55 scalarize_div_floor,
56 scalarize_div_floor_optimized,56 scalarize_div_floor_optimized,
57 scalarize_div_ceil,
58 scalarize_div_ceil_optimized,
57 scalarize_div_exact,59 scalarize_div_exact,
58 scalarize_div_exact_optimized,60 scalarize_div_exact_optimized,
59 scalarize_rem,61 scalarize_rem,
...@@ -173,6 +175,15 @@ pub const Feature = enum {...@@ -173,6 +175,15 @@ pub const Feature = enum {
173 /// Not compatible with `scalarize_mul_safe`.175 /// Not compatible with `scalarize_mul_safe`.
174 expand_mul_safe,176 expand_mul_safe,
175177
178 /// Replace `div_ceil` with truncating division followed by a remainder based adjustment for integers,
179 /// or division followed by ceil for floats.
180 /// Not compatible with `scalarize_div_ceil`.
181 expand_div_ceil,
182 /// Replace `div_ceil_optimized` with truncating division followed by a remainder based adjustment for integers,
183 /// or division followed by ceil for floats.
184 /// Not compatible with `scalarize_div_ceil_optimized`.
185 expand_div_ceil_optimized,
186
176 /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.187 /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.
177 /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the188 /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the
178 /// first byte of memory until bit pointers know their backing type.189 /// first byte of memory until bit pointers know their backing type.
...@@ -231,6 +242,8 @@ pub const Feature = enum {...@@ -231,6 +242,8 @@ pub const Feature = enum {
231 .div_trunc_optimized => .scalarize_div_trunc_optimized,242 .div_trunc_optimized => .scalarize_div_trunc_optimized,
232 .div_floor => .scalarize_div_floor,243 .div_floor => .scalarize_div_floor,
233 .div_floor_optimized => .scalarize_div_floor_optimized,244 .div_floor_optimized => .scalarize_div_floor_optimized,
245 .div_ceil => .scalarize_div_ceil,
246 .div_ceil_optimized => .scalarize_div_ceil_optimized,
234 .div_exact => .scalarize_div_exact,247 .div_exact => .scalarize_div_exact,
235 .div_exact_optimized => .scalarize_div_exact_optimized,248 .div_exact_optimized => .scalarize_div_exact_optimized,
236 .rem => .scalarize_rem,249 .rem => .scalarize_rem,
...@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
382 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {395 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
383 .none => {},396 .none => {},
384 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),397 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
385 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorBlockPayload(398 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
386 inst,399 inst,
387 bin_op.lhs,400 bin_op.lhs,
388 bin_op.rhs,401 bin_op.rhs,
...@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
596 continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));609 continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));
597 }610 }
598 },611 },
612 inline .div_ceil, .div_ceil_optimized => |air_tag| {
613 const expand_feature: Feature = switch (air_tag) {
614 .div_ceil => .expand_div_ceil,
615 .div_ceil_optimized => .expand_div_ceil_optimized,
616 else => unreachable,
617 };
618
619 if (l.features.has(expand_feature)) {
620 assert(!l.features.has(.scalarize(air_tag))); // it doesn't make sense to do both
621 continue :inst l.replaceInst(inst, .block, try l.divCeilBlockPayload(inst, air_tag));
622 } else {
623 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
624 switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
625 .none => {},
626 .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
627 .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
628 inst,
629 bin_op.lhs,
630 bin_op.rhs,
631 air_tag,
632 )),
633 }
634 }
635 },
599 inline .int_from_float_safe,636 inline .int_from_float_safe,
600 .int_from_float_optimized_safe,637 .int_from_float_optimized_safe,
601 => |air_tag| {638 => |air_tag| {
...@@ -709,7 +746,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -709,7 +746,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
709 .switch_br, .loop_switch_br => {746 .switch_br, .loop_switch_br => {
710 const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op;747 const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op;
711 const extra = l.extraData(Air.SwitchBr, pl_op.payload);748 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);
713 var extra_index = extra.end + hint_bag_count;750 var extra_index = extra.end + hint_bag_count;
714 for (0..extra.data.cases_len) |_| {751 for (0..extra.data.cases_len) |_| {
715 const case_extra = l.extraData(Air.SwitchBr.Case, extra_index);752 const case_extra = l.extraData(Air.SwitchBr.Case, extra_index);
...@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_...@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_
2419 } };2456 } };
2420}2457}
24212458
2459fn divCeilBlockPayload(
2460 l: *Legalize,
2461 orig_inst: Air.Inst.Index,
2462 air_tag: Air.Inst.Tag,
2463) Error!Air.Inst.Data {
2464 const pt = l.pt;
2465 const zcu = pt.zcu;
2466 const gpa = zcu.gpa;
2467
2468 const bin_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].bin_op;
2469 const operand_ty = l.typeOf(bin_op.lhs);
2470 assert(l.typeOf(bin_op.rhs).toIntern() == operand_ty.toIntern());
2471
2472 const scalar_ty = operand_ty.scalarType(zcu);
2473 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
2474
2475 switch (scalar_ty.zigTypeTag(zcu)) {
2476 .float => {
2477 // %result = ceil(lhs / rhs)
2478
2479 var inst_buf: [3]Air.Inst.Index = undefined;
2480 try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
2481
2482 var main_block: Block = .init(&inst_buf);
2483
2484 const div_tag: Air.Inst.Tag = switch (air_tag) {
2485 .div_ceil => .div_float,
2486 .div_ceil_optimized => .div_float_optimized,
2487 else => unreachable,
2488 };
2489
2490 const div_inst = main_block.add(l, .{
2491 .tag = div_tag,
2492 .data = .{ .bin_op = bin_op },
2493 });
2494
2495 const ceil_inst = main_block.add(l, .{
2496 .tag = .ceil,
2497 .data = .{ .un_op = div_inst.toRef() },
2498 });
2499
2500 main_block.addBr(l, orig_inst, ceil_inst.toRef());
2501
2502 _ = main_block.stealRemainingCapacity();
2503 return .{ .ty_pl = .{
2504 .ty = .fromType(operand_ty),
2505 .payload = try l.addBlockBody(main_block.body()),
2506 } };
2507 },
2508
2509 .int => {
2510 // Integer div_ceil:
2511 //
2512 // q = div_trunc(lhs, rhs)
2513 // r = rem(lhs, rhs)
2514 //
2515 // unsigned:
2516 // q + int(r != 0)
2517 //
2518 // signed:
2519 // q + int(r != 0 and same_sign(lhs, rhs))
2520 //
2521 // same_sign is `(lhs ^ rhs) >= 0`.
2522
2523 var inst_buf: [10]Air.Inst.Index = undefined;
2524 try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
2525
2526 var main_block: Block = .init(&inst_buf);
2527
2528 const q_inst = main_block.add(l, .{
2529 .tag = .div_trunc,
2530 .data = .{ .bin_op = bin_op },
2531 });
2532
2533 const r_inst = main_block.add(l, .{
2534 .tag = .rem,
2535 .data = .{ .bin_op = bin_op },
2536 });
2537
2538 const zero_ref: Air.Inst.Ref = if (is_vector) zero: {
2539 const zero_scalar = try pt.intValue(scalar_ty, 0);
2540 const zero_vec = try pt.aggregateSplatValue(operand_ty, zero_scalar);
2541 break :zero Air.internedToRef(zero_vec.toIntern());
2542 } else Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
2543
2544 const r_nonzero_inst = try main_block.addCmp(
2545 l,
2546 .neq,
2547 r_inst.toRef(),
2548 zero_ref,
2549 .{ .vector = is_vector },
2550 );
2551
2552 const int_info = scalar_ty.intInfo(zcu);
2553
2554 const need_adjust_inst: Air.Inst.Index = if (int_info.signedness == .unsigned) r_nonzero_inst else inst: {
2555 const sign_xor_inst = main_block.add(l, .{
2556 .tag = .xor,
2557 .data = .{ .bin_op = .{
2558 .lhs = bin_op.lhs,
2559 .rhs = bin_op.rhs,
2560 } },
2561 });
2562
2563 const signs_same_inst = try main_block.addCmp(
2564 l,
2565 .gte,
2566 sign_xor_inst.toRef(),
2567 zero_ref,
2568 .{ .vector = is_vector },
2569 );
2570
2571 break :inst main_block.add(l, .{
2572 .tag = .bit_and,
2573 .data = .{ .bin_op = .{
2574 .lhs = r_nonzero_inst.toRef(),
2575 .rhs = signs_same_inst.toRef(),
2576 } },
2577 });
2578 };
2579
2580 const adjust_u1_ty = if (is_vector)
2581 try pt.vectorType(.{
2582 .len = operand_ty.vectorLen(zcu),
2583 .child = Type.u1.toIntern(),
2584 })
2585 else
2586 Type.u1;
2587
2588 const adjust_u1_ref = main_block.addBitCast(l, adjust_u1_ty, need_adjust_inst.toRef());
2589 const adjust_inst = main_block.addTyOp(l, .int_cast, operand_ty, adjust_u1_ref);
2590
2591 const result_inst = main_block.add(l, .{
2592 .tag = .add,
2593 .data = .{ .bin_op = .{
2594 .lhs = q_inst.toRef(),
2595 .rhs = adjust_inst.toRef(),
2596 } },
2597 });
2598
2599 main_block.addBr(l, orig_inst, result_inst.toRef());
2600
2601 _ = main_block.stealRemainingCapacity();
2602 return .{ .ty_pl = .{
2603 .ty = .fromType(operand_ty),
2604 .payload = try l.addBlockBody(main_block.body()),
2605 } };
2606 },
2607
2608 else => unreachable,
2609 }
2610}
2611
2422fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {2612fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2423 const pt = l.pt;2613 const pt = l.pt;
2424 const zcu = pt.zcu;2614 const zcu = pt.zcu;
...@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(...@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(
3426 } };3616 } };
3427}3617}
34283618
3429fn softFloatDivTruncFloorBlockPayload(3619fn softFloatDivTruncFloorCeilBlockPayload(
3430 l: *Legalize,3620 l: *Legalize,
3431 orig_inst: Air.Inst.Index,3621 orig_inst: Air.Inst.Index,
3432 lhs: Air.Inst.Ref,3622 lhs: Air.Inst.Ref,
...@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(...@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(
3441 const floor_tag: Air.Inst.Tag = switch (air_tag) {3631 const floor_tag: Air.Inst.Tag = switch (air_tag) {
3442 .div_trunc, .div_trunc_optimized => .trunc_float,3632 .div_trunc, .div_trunc_optimized => .trunc_float,
3443 .div_floor, .div_floor_optimized => .floor,3633 .div_floor, .div_floor_optimized => .floor,
3634 .div_ceil, .div_ceil_optimized => .ceil,
3444 else => unreachable,3635 else => unreachable,
3445 };3636 };
34463637
src/Air/Liveness.zig+2
...@@ -417,6 +417,8 @@ fn analyzeInst(...@@ -417,6 +417,8 @@ fn analyzeInst(
417 .div_floor_optimized,417 .div_floor_optimized,
418 .div_exact,418 .div_exact,
419 .div_exact_optimized,419 .div_exact_optimized,
420 .div_ceil,
421 .div_ceil_optimized,
420 .rem,422 .rem,
421 .rem_optimized,423 .rem_optimized,
422 .mod,424 .mod,
src/Air/Liveness/Verify.zig+2
...@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
235 .div_trunc_optimized,235 .div_trunc_optimized,
236 .div_floor,236 .div_floor,
237 .div_floor_optimized,237 .div_floor_optimized,
238 .div_ceil,
239 .div_ceil_optimized,
238 .div_exact,240 .div_exact,
239 .div_exact_optimized,241 .div_exact_optimized,
240 .rem,242 .rem,
src/Air/Verify.zig+2
...@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {...@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
253 .div_trunc_optimized,253 .div_trunc_optimized,
254 .div_floor,254 .div_floor,
255 .div_floor_optimized,255 .div_floor_optimized,
256 .div_ceil,
257 .div_ceil_optimized,
256 .div_exact,258 .div_exact,
257 .div_exact_optimized,259 .div_exact_optimized,
258 .rem,260 .rem,
src/Air/print.zig+2
...@@ -132,6 +132,7 @@ const Writer = struct {...@@ -132,6 +132,7 @@ const Writer = struct {
132 .div_float,132 .div_float,
133 .div_trunc,133 .div_trunc,
134 .div_floor,134 .div_floor,
135 .div_ceil,
135 .div_exact,136 .div_exact,
136 .rem,137 .rem,
137 .mod,138 .mod,
...@@ -160,6 +161,7 @@ const Writer = struct {...@@ -160,6 +161,7 @@ const Writer = struct {
160 .div_float_optimized,161 .div_float_optimized,
161 .div_trunc_optimized,162 .div_trunc_optimized,
162 .div_floor_optimized,163 .div_floor_optimized,
164 .div_ceil_optimized,
163 .div_exact_optimized,165 .div_exact_optimized,
164 .rem_optimized,166 .rem_optimized,
165 .mod_optimized,167 .mod_optimized,
src/InternPool.zig+3-3
...@@ -3581,11 +3581,11 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {...@@ -3581,11 +3581,11 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
3581 .start = extra_index,3581 .start = extra_index,
3582 .len = extra.data.fields_len,3582 .len = extra.data.fields_len,
3583 } else .empty;3583 } else .empty;
3584 extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;3584 extra_index += @divCeil(field_aligns.len, 4);
3585 const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{3585 const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{
3586 .tid = unwrapped_index.tid,3586 .tid = unwrapped_index.tid,
3587 .start = extra_index,3587 .start = extra_index,
3588 .len = std.math.divCeil(u32, extra.data.fields_len, 32) catch unreachable,3588 .len = @divCeil(extra.data.fields_len, 32),
3589 } else .empty;3589 } else .empty;
3590 extra_index += field_is_comptime_bits.len;3590 extra_index += field_is_comptime_bits.len;
3591 const field_runtime_order: LoadedStructType.RuntimeOrder.Slice = if (extra.data.flags.layout == .auto) .{3591 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 {...@@ -3737,7 +3737,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
3737 .start = extra_index,3737 .start = extra_index,
3738 .len = extra.data.fields_len,3738 .len = extra.data.fields_len,
3739 } else .empty;3739 } else .empty;
3740 extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;3740 extra_index += @divCeil(field_aligns.len, 4);
37413741
3742 return .{3742 return .{
3743 .zir_index = extra.data.zir_index,3743 .zir_index = extra.data.zir_index,
src/Sema.zig+73-7
...@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(...@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(
1335 .div => try sema.zirDiv(block, inst),1335 .div => try sema.zirDiv(block, inst),
1336 .div_exact => try sema.zirDivExact(block, inst),1336 .div_exact => try sema.zirDivExact(block, inst),
1337 .div_floor => try sema.zirDivFloor(block, inst),1337 .div_floor => try sema.zirDivFloor(block, inst),
1338 .div_ceil => try sema.zirDivCeil(block, inst),
1338 .div_trunc => try sema.zirDivTrunc(block, inst),1339 .div_trunc => try sema.zirDivTrunc(block, inst),
13391340
1340 .mod_rem => try sema.zirModRem(block, inst),1341 .mod_rem => try sema.zirModRem(block, inst),
...@@ -10304,7 +10305,7 @@ fn finishSwitchBr(...@@ -10304,7 +10305,7 @@ fn finishSwitchBr(
10304 fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {10305 fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {
10305 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;10306 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
10306 if (unused_hints >= additional_count) return;10307 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);
10308 return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);10309 return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);
10309 }10310 }
10310 fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void {10311 fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void {
...@@ -10320,7 +10321,7 @@ fn finishSwitchBr(...@@ -10320,7 +10321,7 @@ fn finishSwitchBr(
10320 }10321 }
10321 };10322 };
10322 var branch_hints: BranchHints = hints: {10323 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);
10324 break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };10325 break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };
10325 };10326 };
10326 defer branch_hints.bags.deinit(gpa);10327 defer branch_hints.bags.deinit(gpa);
...@@ -12211,7 +12212,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion(...@@ -12211,7 +12212,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion(
12211 {12212 {
12212 // All branch hints are `.none`, so just add zero elems.12213 // All branch hints are `.none`, so just add zero elems.
12213 comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0);12214 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);
12215 try cases_extra.appendNTimes(gpa, 0, need_elems);12216 try cases_extra.appendNTimes(gpa, 0, need_elems);
12216 }12217 }
1221712218
...@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
13850 return sema.fail(13851 return sema.fail(
13851 block,13852 block,
13852 src,13853 src,
13853 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact",13854 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact",
13854 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },13855 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
13855 );13856 );
13856 }13857 }
...@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
14023 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);14024 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
14024}14025}
1402514026
14027fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14028 const pt = sema.pt;
14029 const zcu = pt.zcu;
14030 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
14031 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14032 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14033 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14034 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14035 const lhs = sema.resolveInst(extra.lhs);
14036 const rhs = sema.resolveInst(extra.rhs);
14037 const lhs_ty = sema.typeOf(lhs);
14038 const rhs_ty = sema.typeOf(rhs);
14039 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14040 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14041 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14042 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14043
14044 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14045 .override = &.{ lhs_src, rhs_src },
14046 });
14047
14048 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14049 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14050
14051 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14052 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14053
14054 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14055
14056 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil);
14057
14058 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14059 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14060
14061 const allow_div_zero = !is_int and
14062 resolved_type.toIntern() != .comptime_float_type and
14063 block.float_mode == .strict;
14064
14065 if (maybe_lhs_val) |lhs_val| {
14066 if (maybe_rhs_val) |rhs_val| {
14067 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil);
14068 return Air.internedToRef(result.toIntern());
14069 }
14070 if (allow_div_zero) {
14071 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14072 } else {
14073 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14074 }
14075 } else if (maybe_rhs_val) |rhs_val| {
14076 if (allow_div_zero) {
14077 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14078 } else {
14079 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14080 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14081 }
14082 }
14083
14084 if (block.wantSafety()) {
14085 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14086 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14087 }
14088
14089 return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs);
14090}
14091
14026fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {14092fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14027 const pt = sema.pt;14093 const pt = sema.pt;
14028 const zcu = pt.zcu;14094 const zcu = pt.zcu;
...@@ -18051,7 +18117,7 @@ fn analyzeRet(...@@ -18051,7 +18117,7 @@ fn analyzeRet(
18051fn floatOpAllowed(tag: Zir.Inst.Tag) bool {18117fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
18052 // extend this swich as additional operators are implemented18118 // extend this swich as additional operators are implemented
18053 return switch (tag) {18119 return switch (tag) {
18054 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,18120 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true,
18055 else => false,18121 else => false,
18056 };18122 };
18057}18123}
...@@ -18632,7 +18698,7 @@ fn finishStructInit(...@@ -18632,7 +18698,7 @@ fn finishStructInit(
18632 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);18698 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
18633 },18699 },
18634 .@"packed" => {18700 .@"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)));
18636 @memset(buf, 0);18702 @memset(buf, 0);
18637 var bit_offset: u16 = 0;18703 var bit_offset: u16 = 0;
18638 for (field_inits) |field_init| {18704 for (field_inits) |field_init| {
...@@ -29679,7 +29745,7 @@ pub fn bitCastVal(...@@ -29679,7 +29745,7 @@ pub fn bitCastVal(
29679 if (val.isUndef(zcu)) {29745 if (val.isUndef(zcu)) {
29680 return pt.undefValue(dest_ty);29746 return pt.undefValue(dest_ty);
29681 } else {29747 } 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)));
29683 @memset(buf, 0);29749 @memset(buf, 0);
29684 val.writeToPackedMemory(zcu, buf, 0);29750 val.writeToPackedMemory(zcu, buf, 0);
29685 return .readFromPackedMemory(dest_ty, pt, buf, 0);29751 return .readFromPackedMemory(dest_ty, pt, buf, 0);
src/Sema/arith.zig+69-3
...@@ -768,7 +768,7 @@ fn mulSatScalar(...@@ -768,7 +768,7 @@ fn mulSatScalar(
768 }768 }
769}769}
770770
771pub const DivOp = enum { div, div_trunc, div_floor, div_exact };771pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact };
772772
773/// Applies the `/` operator to comptime-known values.773/// Applies the `/` operator to comptime-known values.
774/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.774/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
...@@ -843,6 +843,11 @@ fn divScalar(...@@ -843,6 +843,11 @@ fn divScalar(
843 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);843 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
844 return res.val;844 return res.val;
845 },845 },
846 .div_ceil => {
847 const res = try intDivCeil(sema, lhs_val, rhs_val, ty);
848 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
849 return res.val;
850 },
846 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {851 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
847 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),852 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
848 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),853 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
...@@ -851,7 +856,7 @@ fn divScalar(...@@ -851,7 +856,7 @@ fn divScalar(
851 }856 }
852 } else {857 } else {
853 const allow_div_zero = switch (op) {858 const allow_div_zero = switch (op) {
854 .div, .div_trunc, .div_floor => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,859 .div, .div_trunc, .div_floor, .div_ceil => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
855 .div_exact => false,860 .div_exact => false,
856 };861 };
857 if (!allow_div_zero) {862 if (!allow_div_zero) {
...@@ -871,6 +876,7 @@ fn divScalar(...@@ -871,6 +876,7 @@ fn divScalar(
871 .div => return floatDiv(sema, lhs_val, rhs_val, ty),876 .div => return floatDiv(sema, lhs_val, rhs_val, ty),
872 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),877 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
873 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),878 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
879 .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty),
874 .div_exact => {880 .div_exact => {
875 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {881 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
876 return sema.fail(block, src, "exact division produced remainder", .{});882 return sema.fail(block, src, "exact division produced remainder", .{});
...@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1755 }1761 }
1756 return pt.intValue_big(ty, result_q.toConst());1762 return pt.intValue_big(ty, result_q.toConst());
1757}1763}
1764fn intDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1765 const result = intDivCeilInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1766 error.Overflow => {
1767 const result = intDivCeilInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1768 error.Overflow => unreachable,
1769 else => |e| return e,
1770 };
1771 return .{ .overflow = true, .val = result };
1772 },
1773 else => |e| return e,
1774 };
1775 return .{ .overflow = false, .val = result };
1776}
1777fn intDivCeilInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1778 const pt = sema.pt;
1779 const zcu = pt.zcu;
1780 var lhs_space: Value.BigIntSpace = undefined;
1781 var rhs_space: Value.BigIntSpace = undefined;
1782 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1783 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1784 const limbs_q = try sema.arena.alloc(
1785 std.math.big.Limb,
1786 lhs_bigint.limbs.len,
1787 );
1788 const limbs_r = try sema.arena.alloc(
1789 std.math.big.Limb,
1790 rhs_bigint.limbs.len,
1791 );
1792 const limbs_buf = try sema.arena.alloc(
1793 std.math.big.Limb,
1794 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1795 );
1796 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1797 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1798 result_q.divCeil(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1799 if (ty.toIntern() != .comptime_int_type) {
1800 const info = ty.intInfo(zcu);
1801 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1802 return error.Overflow;
1803 }
1804 }
1805 return pt.intValue_big(ty, result_q.toConst());
1806}
1758fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {1807fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1759 const pt = sema.pt;1808 const pt = sema.pt;
1760 const zcu = pt.zcu;1809 const zcu = pt.zcu;
...@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2140 .storage = storage,2189 .storage = storage,
2141 } }));2190 } }));
2142}2191}
2192fn floatDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2193 const pt = sema.pt;
2194 const zcu = pt.zcu;
2195 const target = zcu.getTarget();
2196 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
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}
2143fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {2209fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
2144 const zcu = sema.pt.zcu;2210 const zcu = sema.pt.zcu;
2145 const target = zcu.getTarget();2211 const target = zcu.getTarget();
...@@ -2238,7 +2304,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value {...@@ -2238,7 +2304,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value {
2238 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);2304 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
2239 const info = ty.intInfo(zcu);2305 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));
2242 @memset(buf, 0xAA);2308 @memset(buf, 0xAA);
22432309
2244 const limbs = try sema.arena.alloc(2310 const limbs = try sema.arena.alloc(
src/Type.zig+3-3
...@@ -968,7 +968,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {...@@ -968,7 +968,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
968 if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";968 if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
969 if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";969 if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
970 if (vector_type.len > 64) return .@"16";970 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);
972 return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));972 return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
973 }973 }
974 const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));974 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 {...@@ -1111,10 +1111,10 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
1111 .vector_type => |vec| {1111 .vector_type => |vec| {
1112 const elem_ty: Type = .fromInterned(vec.child);1112 const elem_ty: Type = .fromInterned(vec.child);
1113 const bytes = switch (zcu.comp.getZigBackend()) {1113 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),
1115 .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu),1115 .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu),
1116 .stage2_x86_64 => switch (elem_ty.toIntern()) {1116 .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),
1118 else => vec.len * elem_ty.abiSize(zcu),1118 else => vec.len * elem_ty.abiSize(zcu),
1119 },1119 },
1120 };1120 };
src/codegen/aarch64/Select.zig+12-10
...@@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
175 .div_trunc_optimized,175 .div_trunc_optimized,
176 .div_floor,176 .div_floor,
177 .div_floor_optimized,177 .div_floor_optimized,
178 .div_ceil,
179 .div_ceil_optimized,
178 .div_exact,180 .div_exact,
179 .div_exact_optimized,181 .div_exact_optimized,
180 .rem,182 .rem,
...@@ -403,11 +405,11 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -403,11 +405,11 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
403 .live_registers = undefined,405 .live_registers = undefined,
404 .repeat_list = undefined,406 .repeat_list = undefined,
405 });407 });
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)));
407 try isel.analyze(air_body_block.body);409 try isel.analyze(air_body_block.body);
408 for (410 for (
409 isel.dom.items[initial_dom_start..].ptr,411 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))],
411 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;413 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
412 isel.dom_start = initial_dom_start;414 isel.dom_start = initial_dom_start;
413 isel.dom_len = initial_dom_len;415 isel.dom_len = initial_dom_len;
...@@ -589,7 +591,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -589,7 +591,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
589 .live_registers = undefined,591 .live_registers = undefined,
590 .repeat_list = undefined,592 .repeat_list = undefined,
591 });593 });
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
594 var cases_it = switch_br.iterateCases();596 var cases_it = switch_br.iterateCases();
595 while (cases_it.next()) |case| try isel.analyze(case.body);597 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 {...@@ -597,7 +599,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
597599
598 for (600 for (
599 isel.dom.items[initial_dom_start..].ptr,601 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))],
601 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;603 ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
602 isel.dom_start = initial_dom_start;604 isel.dom_start = initial_dom_start;
603 isel.dom_len = initial_dom_len;605 isel.dom_len = initial_dom_len;
...@@ -10194,7 +10196,7 @@ pub const Value = struct {...@@ -10194,7 +10196,7 @@ pub const Value = struct {
10194 0 => unreachable,10196 0 => unreachable,
10195 1...64 => unreachable,10197 1...64 => unreachable,
10196 65...256 => |bits| if (offset == 0 and size == ty_size) {10198 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);
10198 vi.setParts(isel, @intCast(parts_len));10200 vi.setParts(isel, @intCast(parts_len));
10199 for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8);10201 for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8);
10200 },10202 },
...@@ -10238,7 +10240,7 @@ pub const Value = struct {...@@ -10238,7 +10240,7 @@ pub const Value = struct {
10238 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;10240 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10239 const array_len = array_type.lenIncludingSentinel();10241 const array_len = array_type.lenIncludingSentinel();
10240 if (array_len > Value.max_parts and10242 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)
10242 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});10244 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10243 const alignment = vi.alignment(isel);10245 const alignment = vi.alignment(isel);
10244 const Part = struct { offset: u64, size: u64 };10246 const Part = struct { offset: u64, size: u64 };
...@@ -10288,7 +10290,7 @@ pub const Value = struct {...@@ -10288,7 +10290,7 @@ pub const Value = struct {
10288 .anyframe_type => unreachable,10290 .anyframe_type => unreachable,
10289 .error_union_type => |error_union_type| {10291 .error_union_type => |error_union_type| {
10290 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;10292 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)
10292 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});10294 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10293 const alignment = vi.alignment(isel);10295 const alignment = vi.alignment(isel);
10294 const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);10296 const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
...@@ -10395,7 +10397,7 @@ pub const Value = struct {...@@ -10395,7 +10397,7 @@ pub const Value = struct {
10395 }10397 }
10396 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;10398 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10397 if (loaded_struct.field_types.len > Value.max_parts and10399 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)
10399 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});10401 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10400 const alignment = vi.alignment(isel);10402 const alignment = vi.alignment(isel);
10401 const Part = struct { offset: u64, size: u64, signedness: ?std.lang.Signedness, is_vector: bool };10403 const Part = struct { offset: u64, size: u64, signedness: ?std.lang.Signedness, is_vector: bool };
...@@ -10456,7 +10458,7 @@ pub const Value = struct {...@@ -10456,7 +10458,7 @@ pub const Value = struct {
10456 .tuple_type => |tuple_type| {10458 .tuple_type => |tuple_type| {
10457 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;10459 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10458 if (tuple_type.types.len > Value.max_parts and10460 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)
10460 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});10462 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10461 const alignment = vi.alignment(isel);10463 const alignment = vi.alignment(isel);
10462 const Part = struct { offset: u64, size: u64, is_vector: bool };10464 const Part = struct { offset: u64, size: u64, is_vector: bool };
...@@ -10511,7 +10513,7 @@ pub const Value = struct {...@@ -10511,7 +10513,7 @@ pub const Value = struct {
10511 } },10513 } },
10512 }10514 }
10513 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;10515 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)
10515 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});10517 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10516 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);10518 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
10517 const alignment = vi.alignment(isel);10519 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 {...@@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2675 try airBinBuiltinCall(f, inst, "fmod", .none);2675 try airBinBuiltinCall(f, inst, "fmod", .none);
2676 },2676 },
2677 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),2677 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
2678 .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none),
2678 .mod => try airBinBuiltinCall(f, inst, "mod", .none),2679 .mod => try airBinBuiltinCall(f, inst, "mod", .none),
2679 .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),2680 .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),
26802681
...@@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2856 .div_float_optimized,2857 .div_float_optimized,
2857 .div_trunc_optimized,2858 .div_trunc_optimized,
2858 .div_floor_optimized,2859 .div_floor_optimized,
2860 .div_ceil_optimized,
2859 .div_exact_optimized,2861 .div_exact_optimized,
2860 .rem_optimized,2862 .rem_optimized,
2861 .mod_optimized,2863 .mod_optimized,
src/codegen/llvm/FuncGen.zig+76-2
...@@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov...@@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
383 .div_float => try self.airDivFloat(inst, .normal),383 .div_float => try self.airDivFloat(inst, .normal),
384 .div_trunc => try self.airDivTrunc(inst, .normal),384 .div_trunc => try self.airDivTrunc(inst, .normal),
385 .div_floor => try self.airDivFloor(inst, .normal),385 .div_floor => try self.airDivFloor(inst, .normal),
386 .div_ceil => try self.airDivCeil(inst, .normal),
386 .div_exact => try self.airDivExact(inst, .normal),387 .div_exact => try self.airDivExact(inst, .normal),
387 .rem => try self.airRem(inst, .normal),388 .rem => try self.airRem(inst, .normal),
388 .mod => try self.airMod(inst, .normal),389 .mod => try self.airMod(inst, .normal),
...@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov...@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
400 .div_float_optimized => try self.airDivFloat(inst, .fast),401 .div_float_optimized => try self.airDivFloat(inst, .fast),
401 .div_trunc_optimized => try self.airDivTrunc(inst, .fast),402 .div_trunc_optimized => try self.airDivTrunc(inst, .fast),
402 .div_floor_optimized => try self.airDivFloor(inst, .fast),403 .div_floor_optimized => try self.airDivFloor(inst, .fast),
404 .div_ceil_optimized => try self.airDivCeil(inst, .fast),
403 .div_exact_optimized => try self.airDivExact(inst, .fast),405 .div_exact_optimized => try self.airDivExact(inst, .fast),
404 .rem_optimized => try self.airRem(inst, .fast),406 .rem_optimized => try self.airRem(inst, .fast),
405 .mod_optimized => try self.airMod(inst, .fast),407 .mod_optimized => try self.airMod(inst, .fast),
...@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)...@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
3578 return self.wip.bin(.udiv, lhs, rhs, "");3580 return self.wip.bin(.udiv, lhs, rhs, "");
3579}3581}
35803582
3583fn airDivCeil(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
3584 const o = self.object;
3585 const zcu = o.zcu;
3586 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3587 const lhs = try self.resolveInst(bin_op.lhs);
3588 const rhs = try self.resolveInst(bin_op.rhs);
3589 const inst_ty = self.typeOfIndex(inst);
3590 const scalar_ty = inst_ty.scalarType(zcu);
3591
3592 if (scalar_ty.isRuntimeFloat()) {
3593 const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs });
3594 return self.buildFloatOp(.ceil, fast, inst_ty, 1, .{result});
3595 }
3596 if (scalar_ty.isSignedInt(zcu)) {
3597 const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
3598 const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
3599
3600 const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb;
3601 var bfa_buf: ExpectedContents = undefined;
3602 var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa);
3603 const allocator = bfa.allocator();
3604
3605 const scalar_bits = scalar_ty.intInfo(zcu).bits;
3606 var smin_big_int: std.math.big.int.Mutable = .{
3607 .limbs = try allocator.alloc(
3608 std.math.big.Limb,
3609 std.math.big.int.calcTwosCompLimbCount(scalar_bits),
3610 ),
3611 .len = undefined,
3612 .positive = undefined,
3613 };
3614 defer allocator.free(smin_big_int.limbs);
3615 smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits);
3616 const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst(
3617 scalar_llvm_ty,
3618 smin_big_int.toConst(),
3619 ));
3620
3621 const zero = try o.builder.splatValue(
3622 inst_llvm_ty,
3623 try o.builder.intConst(scalar_llvm_ty, 0),
3624 );
3625
3626 const div = try self.wip.bin(.sdiv, lhs, rhs, "divCeil.div");
3627 const rem = try self.wip.bin(.srem, lhs, rhs, "divCeil.rem");
3628
3629 const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "divCeil.rhs_sign");
3630 const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "divCeil.rem_xor_rhs_sign");
3631
3632 const need_correction = try self.wip.icmp(.sgt, rem_xor_rhs_sign, zero, "divCeil.need_correction");
3633
3634 const correction = try self.wip.cast(.zext, need_correction, inst_llvm_ty, "divCeil.correction");
3635 return self.wip.bin(.@"add nsw", div, correction, "divCeil");
3636 } else {
3637 const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
3638 const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
3639
3640 const zero = try o.builder.splatValue(
3641 inst_llvm_ty,
3642 try o.builder.intConst(scalar_llvm_ty, 0),
3643 );
3644
3645 const div = try self.wip.bin(.udiv, lhs, rhs, "divCeil.div");
3646 const rem = try self.wip.bin(.urem, lhs, rhs, "divCeil.rem");
3647
3648 const rem_nonzero = try self.wip.icmp(.ne, rem, zero, "divCeil.rem_nonzero");
3649 const correction = try self.wip.cast(.zext, rem_nonzero, inst_llvm_ty, "divCeil.correction");
3650
3651 return self.wip.bin(.@"add nuw", div, correction, "divCeil");
3652 }
3653}
3654
3581fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {3655fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
3582 const zcu = self.object.zcu;3656 const zcu = self.object.zcu;
3583 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3657 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -6874,7 +6948,7 @@ const ParamTypeIterator = struct {...@@ -6874,7 +6948,7 @@ const ParamTypeIterator = struct {
6874 switch (ip.indexToKey(ty.toIntern())) {6948 switch (ip.indexToKey(ty.toIntern())) {
6875 .struct_type => {6949 .struct_type => {
6876 const size = ty.abiSize(zcu);6950 const size = ty.abiSize(zcu);
6877 assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);6951 assert(@divCeil(size, 8) == types_index);
6878 if (size % 8 > 0) {6952 if (size % 8 > 0) {
6879 it.types_buffer[types_index - 1] =6953 it.types_buffer[types_index - 1] =
6880 try it.object.builder.intType(@intCast(size % 8 * 8));6954 try it.object.builder.intType(@intCast(size % 8 * 8));
...@@ -7119,7 +7193,7 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E...@@ -7119,7 +7193,7 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E
7119 switch (ip.indexToKey(ret_ty.toIntern())) {7193 switch (ip.indexToKey(ret_ty.toIntern())) {
7120 .struct_type => {7194 .struct_type => {
7121 const size = ret_ty.abiSize(zcu);7195 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);
7123 if (size % 8 > 0) {7197 if (size % 8 > 0) {
7124 types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8));7198 types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8));
7125 }7199 }
src/codegen/riscv64/CodeGen.zig+3-2
...@@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1422 .mod,1422 .mod,
1423 .div_float,1423 .div_float,
1424 .div_floor,1424 .div_floor,
1425 .div_ceil,
1425 => return func.fail("TODO: {s}", .{@tagName(tag)}),1426 => return func.fail("TODO: {s}", .{@tagName(tag)}),
14261427
1427 .sqrt,1428 .sqrt,
...@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1621 .div_trunc_optimized,1622 .div_trunc_optimized,
1622 .div_floor_optimized,1623 .div_floor_optimized,
1623 .div_exact_optimized,1624 .div_exact_optimized,
1625 .div_ceil_optimized,
1624 .rem_optimized,1626 .rem_optimized,
1625 .mod_optimized,1627 .mod_optimized,
1626 .neg_optimized,1628 .neg_optimized,
...@@ -2215,8 +2217,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void {...@@ -2215,8 +2217,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void {
2215 };2217 };
22162218
2217 const dst_mcv = if (dst_int_info.bits <= src_storage_bits and2219 const dst_mcv = if (dst_int_info.bits <= src_storage_bits and
2218 math.divCeil(u16, dst_int_info.bits, 64) catch unreachable ==2220 @divCeil(dst_int_info.bits, 64) == @divCeil(src_storage_bits, 64) and
2219 math.divCeil(u32, src_storage_bits, 64) catch unreachable and
2220 func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {2221 func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
2221 const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true);2222 const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true);
2222 try func.genCopy(min_ty, dst_mcv, src_mcv);2223 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 {...@@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
523 .mul_with_overflow => try self.airMulWithOverflow(inst),523 .mul_with_overflow => try self.airMulWithOverflow(inst),
524 .shl_with_overflow => try self.airShlWithOverflow(inst),524 .shl_with_overflow => try self.airShlWithOverflow(inst),
525525
526 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),526 .div_float, .div_trunc, .div_floor, .div_ceil, .div_exact => try self.airDiv(inst),
527527
528 .cmp_lt => try self.airCmp(inst, .lt),528 .cmp_lt => try self.airCmp(inst, .lt),
529 .cmp_lte => try self.airCmp(inst, .lte),529 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
678 .div_float_optimized,678 .div_float_optimized,
679 .div_trunc_optimized,679 .div_trunc_optimized,
680 .div_floor_optimized,680 .div_floor_optimized,
681 .div_ceil_optimized,
681 .div_exact_optimized,682 .div_exact_optimized,
682 .rem_optimized,683 .rem_optimized,
683 .mod_optimized,684 .mod_optimized,
src/codegen/spirv/Assembler.zig+1-1
...@@ -375,7 +375,7 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {...@@ -375,7 +375,7 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {
375 },375 },
376 .string => |offset| {376 .string => |offset| {
377 const text = std.mem.sliceTo(ass.inst.string_bytes.items[offset..], 0);377 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));
379 try section.ensureUnusedCapacity(cg.gpa, size);379 try section.ensureUnusedCapacity(cg.gpa, size);
380 section.writeOperand(spec.LiteralString, text);380 section.writeOperand(spec.LiteralString, text);
381 },381 },
src/codegen/spirv/Section.zig+1-1
...@@ -232,7 +232,7 @@ fn operandSize(comptime Operand: type, operand: Operand) usize {...@@ -232,7 +232,7 @@ fn operandSize(comptime Operand: type, operand: Operand) usize {
232 return switch (Operand) {232 return switch (Operand) {
233 spec.LiteralSpecConstantOpInteger => unreachable,233 spec.LiteralSpecConstantOpInteger => unreachable,
234 spec.Id, spec.LiteralInteger, spec.LiteralExtInstInteger => 1,234 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)),
236 spec.LiteralContextDependentNumber => switch (operand) {236 spec.LiteralContextDependentNumber => switch (operand) {
237 .int32, .uint32, .float32 => 1,237 .int32, .uint32, .float32 => 1,
238 .int64, .uint64, .float64 => 2,238 .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 {...@@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
62 .scalarize_div_trunc_optimized,62 .scalarize_div_trunc_optimized,
63 .scalarize_div_floor,63 .scalarize_div_floor,
64 .scalarize_div_floor_optimized,64 .scalarize_div_floor_optimized,
65 .scalarize_div_ceil,
66 .scalarize_div_ceil_optimized,
65 .scalarize_div_exact,67 .scalarize_div_exact,
66 .scalarize_div_exact_optimized,68 .scalarize_div_exact_optimized,
67 .scalarize_rem,69 .scalarize_rem,
...@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1340 .div_exact,1342 .div_exact,
1341 .div_trunc,1343 .div_trunc,
1342 .div_floor,1344 .div_floor,
1345 .div_ceil,
1343 => |tag| {1346 => |tag| {
1344 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1347 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1345 const lhs = try cg.resolveInst(bin_op.lhs);1348 const lhs = try cg.resolveInst(bin_op.lhs);
...@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1366 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),1369 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),
1367 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),1370 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),
1368 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),1371 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),
1372 .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs),
1369 else => unreachable,1373 else => unreachable,
1370 };1374 };
13711375
...@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1384 .div_exact => try cg.intDiv(int_ty, lhs, rhs),1388 .div_exact => try cg.intDiv(int_ty, lhs, rhs),
1385 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),1389 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),
1386 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),1390 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),
1391 .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs),
1387 else => unreachable,1392 else => unreachable,
1388 };1393 };
13891394
...@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1881 .div_float_optimized,1886 .div_float_optimized,
1882 .div_trunc_optimized,1887 .div_trunc_optimized,
1883 .div_floor_optimized,1888 .div_floor_optimized,
1889 .div_ceil_optimized,
1884 .div_exact_optimized,1890 .div_exact_optimized,
1885 .rem_optimized,1891 .rem_optimized,
1886 .mod_optimized,1892 .mod_optimized,
...@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W...@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
2799 }2805 }
2800}2806}
28012807
2808fn intDivCeil(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
2809 switch (ty.bits) {
2810 0 => unreachable,
2811 1...32 => {
2812 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i32);
2813 defer q.free(cg);
2814
2815 const zero: WValue = .{ .imm32 = 0 };
2816
2817 const r = try cg.intRem(ty, lhs, rhs);
2818 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
2819 defer r_nonzero.free(cg);
2820
2821 if (!ty.is_signed) {
2822 try cg.emitWValue(q);
2823 try cg.emitWValue(r_nonzero);
2824 try cg.addTag(.i32_add);
2825 return .stack;
2826 }
2827
2828 const sign_xor = try cg.intXor(ty, lhs, rhs);
2829 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
2830 defer same_sign.free(cg);
2831
2832 try cg.emitWValue(q);
2833 const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
2834 try cg.emitWValue(need_adjust);
2835 try cg.addTag(.i32_add);
2836 return .stack;
2837 },
2838 33...64 => {
2839 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i64);
2840 defer q.free(cg);
2841
2842 const zero: WValue = .{ .imm64 = 0 };
2843
2844 const r = try cg.intRem(ty, lhs, rhs);
2845 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
2846 defer r_nonzero.free(cg);
2847
2848 if (!ty.is_signed) {
2849 try cg.emitWValue(q);
2850 try cg.emitWValue(r_nonzero);
2851 try cg.addTag(.i64_extend_i32_u);
2852 try cg.addTag(.i64_add);
2853 return .stack;
2854 }
2855
2856 const sign_xor = try cg.intXor(ty, lhs, rhs);
2857 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
2858 defer same_sign.free(cg);
2859
2860 try cg.emitWValue(q);
2861 const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
2862 try cg.emitWValue(need_adjust);
2863 try cg.addTag(.i64_extend_i32_u);
2864 try cg.addTag(.i64_add);
2865 return .stack;
2866 },
2867 else => {
2868 var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.usize);
2869 defer q.free(cg);
2870
2871 const zero = try cg.intZeroValue(ty);
2872
2873 const r = try cg.intRem(ty, lhs, rhs);
2874 var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.u32);
2875 defer r_nonzero.free(cg);
2876
2877 if (!ty.is_signed) {
2878 var adjust_bigint = try (try cg.intCast(ty, .u32, r_nonzero)).toLocal(cg, Type.usize);
2879 defer adjust_bigint.free(cg);
2880
2881 return try cg.intAdd(ty, q, adjust_bigint);
2882 }
2883
2884 const sign_xor = try cg.intXor(ty, lhs, rhs);
2885 var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.u32);
2886 defer same_sign.free(cg);
2887
2888 var adjust = try (try cg.intAnd(.u32, r_nonzero, same_sign)).toLocal(cg, Type.u32);
2889 defer adjust.free(cg);
2890
2891 var adjust_bigint = try (try cg.intCast(ty, .u32, adjust)).toLocal(cg, Type.usize);
2892 defer adjust_bigint.free(cg);
2893
2894 return try cg.intAdd(ty, q, adjust_bigint);
2895 },
2896 }
2897}
2898
2802fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {2899fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
2803 switch (ty.bits) {2900 switch (ty.bits) {
2804 0 => unreachable,2901 0 => unreachable,
...@@ -3581,7 +3678,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {...@@ -3581,7 +3678,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35813678
3582 const result = try cg.allocInt(ty);3679 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
3586 if (ty.bits % 64 != 0) {3683 if (ty.bits % 64 != 0) {
3587 try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 });3684 try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 });
...@@ -3647,7 +3744,7 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {...@@ -3647,7 +3744,7 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
3647 } else {3744 } else {
3648 const result = try cg.allocInt(int_ty);3745 const result = try cg.allocInt(int_ty);
3649 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);3746 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
3652 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF });3749 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 {...@@ -3681,7 +3778,7 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
3681 } else {3778 } else {
3682 const result = try cg.allocInt(int_ty);3779 const result = try cg.allocInt(int_ty);
3683 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);3780 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
3686 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 });3783 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 });
3687 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - (used_len - 8) * 8 - 1) }, Type.u64, used_len - 8);3784 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...@@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr
4265 return cg.floatFloor(ty, div_result);4362 return cg.floatFloor(ty, div_result);
4266}4363}
42674364
4365// div_ceil(a, b) = ceil(a / b)
4366fn floatDivCeil(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
4367 const div_result = try cg.floatDiv(ty, lhs, rhs);
4368 return cg.floatCeil(ty, div_result);
4369}
4370
4268// mod(a, b) = fmod(fmod(a, b) + b, b)4371// mod(a, b) = fmod(fmod(a, b) + b, b)
4269fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {4372fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
4270 const r = try cg.floatRem(ty, lhs, rhs);4373 const r = try cg.floatRem(ty, lhs, rhs);
src/codegen/x86_64/CodeGen.zig+11-6
...@@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {...@@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
70 .expand_sub_safe,70 .expand_sub_safe,
71 .expand_mul_safe,71 .expand_mul_safe,
7272
73 .expand_div_ceil,
74 .expand_div_ceil_optimized,
75
73 .expand_packed_load,76 .expand_packed_load,
74 .expand_packed_store,77 .expand_packed_store,
75 .expand_packed_agg_field_val,78 .expand_packed_agg_field_val,
...@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173873 for (ops) |op| try op.die(cg);173876 for (ops) |op| try op.die(cg);
173874 },173877 },
173875173878
173879 .div_ceil, .div_ceil_optimized => unreachable,
173880
173876 // No soft-float `Legalize` features are enabled, so this instruction never appears.173881 // No soft-float `Legalize` features are enabled, so this instruction never appears.
173877 .legalize_compiler_rt_call => unreachable,173882 .legalize_compiler_rt_call => unreachable,
173878173883
...@@ -174781,7 +174786,7 @@ fn genShiftBinOpMir(...@@ -174781,7 +174786,7 @@ fn genShiftBinOpMir(
174781 try self.spillEflagsIfOccupied();174786 try self.spillEflagsIfOccupied();
174782174787
174783 if (abi_size > 16) {174788 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);
174785 assert(shift_abi_size >= 1 and shift_abi_size <= 2);174790 assert(shift_abi_size >= 1 and shift_abi_size <= 2);
174786174791
174787 const rcx_lock: ?RegisterLock = switch (rhs_mcv) {174792 const rcx_lock: ?RegisterLock = switch (rhs_mcv) {
...@@ -179593,7 +179598,7 @@ fn genSetReg(...@@ -179593,7 +179598,7 @@ fn genSetReg(
179593 if (pack_alias != sign_alias) try cg.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, sign_alias);179598 if (pack_alias != sign_alias) try cg.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, sign_alias);
179594 try cg.asmRegisterRegister(.{ .p_b, .ackssw }, pack_alias, pack_alias);179599 try cg.asmRegisterRegister(.{ .p_b, .ackssw }, pack_alias, pack_alias);
179595 }179600 }
179596 mask_size = std.math.divCeil(u32, mask_size, 2) catch unreachable;179601 mask_size = @divCeil(mask_size, 2);
179597 break :pack_reg pack_reg;179602 break :pack_reg pack_reg;
179598 },179603 },
179599 };179604 };
...@@ -180157,7 +180162,7 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -180157,7 +180162,7 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
180157 const bit_size = dst_ty.bitSize(zcu);180162 const bit_size = dst_ty.bitSize(zcu);
180158 if (abi_size * 8 <= bit_size) break :result dst_mcv;180163 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));
180161 const high_mcv: MCValue = switch (dst_mcv) {180166 const high_mcv: MCValue = switch (dst_mcv) {
180162 .register => |dst_reg| .{ .register = dst_reg },180167 .register => |dst_reg| .{ .register = dst_reg },
180163 .register_pair => |dst_regs| .{ .register = dst_regs[1] },180168 .register_pair => |dst_regs| .{ .register = dst_regs[1] },
...@@ -183550,7 +183555,7 @@ const Temp = struct {...@@ -183550,7 +183555,7 @@ const Temp = struct {
183550 const part_ty: Type = if (src_regs.len == 1)183555 const part_ty: Type = if (src_regs.len == 1)
183551 src_ty183556 src_ty
183552 else if (cg.intInfo(src_ty)) |int_info| part_ty: {183557 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));
183554 break :part_ty .u64;183559 break :part_ty .u64;
183555 } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {183560 } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {
183556 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),183561 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
...@@ -183560,7 +183565,7 @@ const Temp = struct {...@@ -183560,7 +183565,7 @@ const Temp = struct {
183560 break :part_ty .usize;183565 break :part_ty .usize;
183561 },183566 },
183562 .array_type => {183567 .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));
183564 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_size, 8));183569 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_size, 8));
183565 },183570 },
183566 .vector_type => |vector_type| switch (@divExact(vector_type.len, src_regs.len)) {183571 .vector_type => |vector_type| switch (@divExact(vector_type.len, src_regs.len)) {
...@@ -183580,7 +183585,7 @@ const Temp = struct {...@@ -183580,7 +183585,7 @@ const Temp = struct {
183580 },183585 },
183581 },183586 },
183582 .struct_type, .union_type => {183587 .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));
183584 break :part_ty switch (src_size) {183589 break :part_ty switch (src_size) {
183585 0, 3, 5...7 => unreachable,183590 0, 3, 5...7 => unreachable,
183586 1 => .u8,183591 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...@@ -772,7 +772,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
772 const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {772 const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
773 .rip_inst => 4,773 .rip_inst => 4,
774 else => unreachable,774 else => unreachable,
775 } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);775 } else @intCast(@divCeil(op.immBitSize(), 8));
776 reloc_offset -= enc_length;776 reloc_offset -= enc_length;
777 if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };777 if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
778 assert(!is_mem);778 assert(!is_mem);
src/link/Dwarf.zig+3-3
...@@ -2137,7 +2137,7 @@ pub const WipNav = struct {...@@ -2137,7 +2137,7 @@ pub const WipNav = struct {
2137 .signed => DW.FORM.sdata,2137 .signed => DW.FORM.sdata,
2138 .unsigned => DW.FORM.udata,2138 .unsigned => DW.FORM.udata,
2139 }));2139 }));
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));
2141 var bit: usize = 0;2141 var bit: usize = 0;
2142 var carry: u1 = 1;2142 var carry: u1 = 1;
2143 while (bit < bits) {2143 while (bit < bits) {
...@@ -2158,7 +2158,7 @@ pub const WipNav = struct {...@@ -2158,7 +2158,7 @@ pub const WipNav = struct {
2158 }2158 }
2159 } else {2159 } else {
2160 try diw.writeUleb128(DW.FORM.block);2160 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));
2162 try diw.writeUleb128(bytes);2162 try diw.writeUleb128(bytes);
2163 try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes));2163 try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes));
2164 big_int.writeTwosComplement(2164 big_int.writeTwosComplement(
...@@ -4275,7 +4275,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co...@@ -4275,7 +4275,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
4275 try wip_nav.abbrevCode(.aggregate_undefined_comptime_value);4275 try wip_nav.abbrevCode(.aggregate_undefined_comptime_value);
4276 try wip_nav.refType(.fromInterned(error_union.ty));4276 try wip_nav.refType(.fromInterned(error_union.ty));
4277 var err_buf: [4]u8 = undefined;4277 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)];
4279 dwarf.writeInt(err_bytes, switch (error_union.val) {4279 dwarf.writeInt(err_bytes, switch (error_union.val) {
4280 .err_name => |err_name| try pt.getErrorValue(err_name),4280 .err_name => |err_name| try pt.getErrorValue(err_name),
4281 .payload => 0,4281 .payload => 0,
src/main.zig+1-2
...@@ -162,8 +162,7 @@ const use_safe_allocator = build_options.debug_gpa or...@@ -162,8 +162,7 @@ const use_safe_allocator = build_options.debug_gpa or
162 .ReleaseFast, .ReleaseSmall => false,162 .ReleaseFast, .ReleaseSmall => false,
163 });163 });
164164
165// TODO: The `align(@alignOf(std.heap.SafeAllocator))` can be removed the next time zig1.wasm is updated165var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{
166var safe_allocator: std.heap.SafeAllocator align(@alignOf(std.heap.SafeAllocator)) = .init(std.heap.page_allocator, .{
167 .stack_trace_frames = build_options.mem_leak_frames,166 .stack_trace_frames = build_options.mem_leak_frames,
168});167});
169168
src/print_zir.zig+1
...@@ -392,6 +392,7 @@ const Writer = struct {...@@ -392,6 +392,7 @@ const Writer = struct {
392 .truncate,392 .truncate,
393 .div_exact,393 .div_exact,
394 .div_floor,394 .div_floor,
395 .div_ceil,
395 .div_trunc,396 .div_trunc,
396 .mod,397 .mod,
397 .rem,398 .rem,
stage1/zig.h+32
...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;...@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \813 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \814 return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \
815 } \815 } \
816\
817 static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
818 return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
819 } \
820\
821 static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
822 return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
823 ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
824 } \
816\825\
817 zig_basic_operator(uint##w##_t, mod_u##w, %) \826 zig_basic_operator(uint##w##_t, mod_u##w, %) \
818\827\
...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
2058 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));2067 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
2059}2068}
20602069
2070static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
2071 zig_u128 rem = zig_rem_u128(lhs, rhs);
2072 uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
2073 ? UINT64_C(1) : UINT64_C(0);
2074 return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
2075}
2076
2077static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
2078 zig_i128 rem = zig_rem_i128(lhs, rhs);
2079 int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
2080 ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
2081 : INT64_C(0);
2082 return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
2083}
2084
2061#define zig_mod_u128 zig_rem_u1282085#define zig_mod_u128 zig_rem_u128
20622086
2063static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {2087static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs...@@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs
3251 zig_trap();3275 zig_trap();
3252}3276}
32533277
3278static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3279 zig_trap();
3280}
3281
3254zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);3282zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
3255static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {3283static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
3256 if (!is_signed) {3284 if (!is_signed) {
...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))...@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
4010 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \4038 static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
4011 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \4039 return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
4012 } \4040 } \
4041\
4042 static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
4043 return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
4044 } \
4013\4045\
4014 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \4046 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
4015 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \4047 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
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" {...@@ -59,6 +59,7 @@ test "int128" {
59 const a: i128 = -170141183460469231731687303715884105728;59 const a: i128 = -170141183460469231731687303715884105728;
60 const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;60 const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;
61 try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);61 try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);
62 try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884);
62 try expect(a == b);63 try expect(a == b);
63}64}
6465
test/behavior/math.zig+89
...@@ -488,6 +488,36 @@ fn testIntDivision() !void {...@@ -488,6 +488,36 @@ fn testIntDivision() !void {
488 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);488 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);
489 try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);489 try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);
490490
491 try expect(divCeil(i32, 5, 3) == 2);
492 try expect(divCeil(i32, -5, 3) == -1);
493 try expect(divCeil(i32, -0x80000000, -2) == 0x40000000);
494 try expect(divCeil(i32, 0, -0x80000000) == 0);
495 try expect(divCeil(i32, -0x40000001, 0x40000000) == -1);
496 try expect(divCeil(i32, -0x80000000, 1) == -0x80000000);
497 try expect(divCeil(i32, 10, 12) == 1);
498 try expect(divCeil(i32, -14, 12) == -1);
499 try expect(divCeil(i32, -2, 12) == 0);
500
501 try expect(divCeil(u32, 5, 3) == 2);
502 try expect(divCeil(u32, 16, 4) == 4);
503 try expect(divCeil(u32, 0, 100) == 0);
504 try expect(divCeil(u32, maxInt(u32) - 1, 100) == 42949673);
505
506 try expect(divCeil(i64, 5, 3) == 2);
507 try expect(divCeil(i64, -5, 3) == -1);
508 try expect(divCeil(i64, -0x80000000, -2) == 0x40000000);
509 try expect(divCeil(i64, 0, -0x80000000) == 0);
510 try expect(divCeil(i64, -0x40000001, 0x40000000) == -1);
511 try expect(divCeil(i64, -0x80000000, 1) == -0x80000000);
512 try expect(divCeil(i64, 10, 12) == 1);
513 try expect(divCeil(i64, -14, 12) == -1);
514 try expect(divCeil(i64, -2, 12) == 0);
515
516 try expect(divCeil(u64, 5, 3) == 2);
517 try expect(divCeil(u64, 16, 4) == 4);
518 try expect(divCeil(u64, 0, 100) == 0);
519 try expect(divCeil(u64, maxInt(u64) - 1, 10000) == 1844674407370956);
520
491 try expect(divTrunc(i32, 5, 3) == 1);521 try expect(divTrunc(i32, 5, 3) == 1);
492 try expect(divTrunc(i32, -5, 3) == -1);522 try expect(divTrunc(i32, -5, 3) == -1);
493 try expect(divTrunc(i32, 9, -10) == 0);523 try expect(divTrunc(i32, 9, -10) == 0);
...@@ -531,6 +561,24 @@ fn testIntDivision() !void {...@@ -531,6 +561,24 @@ fn testIntDivision() !void {
531 try expect(561 try expect(
532 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,562 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
533 );563 );
564 try expect(
565 @divFloor(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -3,
566 );
567 try expect(
568 @divFloor(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -3,
569 );
570 try expect(
571 @divFloor(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
572 );
573 try expect(
574 @divCeil(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
575 );
576 try expect(
577 @divCeil(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
578 );
579 try expect(
580 @divCeil(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 3,
581 );
534 try expect(582 try expect(
535 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,583 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
536 );584 );
...@@ -559,6 +607,13 @@ fn testFloatDivision() !void {...@@ -559,6 +607,13 @@ fn testFloatDivision() !void {
559 try expect(divFloor(f16, -43.0, 12.0) == -4.0);607 try expect(divFloor(f16, -43.0, 12.0) == -4.0);
560 try expect(divFloor(f64, -90.0, -9.0) == 10.0);608 try expect(divFloor(f64, -90.0, -9.0) == 10.0);
561609
610 try expect(divCeil(f32, 5.0, 3.0) == 2.0);
611 try expect(divCeil(f32, -5.0, 3.0) == -1.0);
612 try expect(divCeil(f32, 56.0, 9.0) == 7.0);
613 try expect(divCeil(f32, 1053.0, -41.0) == -25.0);
614 try expect(divCeil(f16, -43.0, 12.0) == -3.0);
615 try expect(divCeil(f64, -90.0, -9.0) == 10.0);
616
562 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);617 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
563 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);618 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
564 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);619 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
...@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {...@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {
607662
608 try expect(divFloor(f16, 5.0, 3.0) == 1.0);663 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
609 try expect(divFloor(f16, -5.0, 3.0) == -2.0);664 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
665 try expect(divCeil(f16, 5.0, 3.0) == 2.0);
666 try expect(divCeil(f16, -5.0, 3.0) == -1.0);
610 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);667 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
611 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);668 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
612 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);669 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
...@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {...@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {
622fn divFloor(comptime T: type, a: T, b: T) T {679fn divFloor(comptime T: type, a: T, b: T) T {
623 return @divFloor(a, b);680 return @divFloor(a, b);
624}681}
682fn divCeil(comptime T: type, a: T, b: T) T {
683 return @divCeil(a, b);
684}
625fn divTrunc(comptime T: type, a: T, b: T) T {685fn divTrunc(comptime T: type, a: T, b: T) T {
626 return @divTrunc(a, b);686 return @divTrunc(a, b);
627}687}
...@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {...@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {
1846 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);1906 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
1847}1907}
18481908
1909fn testDivCeil(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1910 try expect(@divCeil(numerator, denominator) == expected);
1911}
1912
1913test "@divCeil > 128 bits" {
1914 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1915 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1916 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1917
1918 try testDivCeil(u140, 0, maxInt(u140), 0);
1919 try testDivCeil(u140, maxInt(u140), maxInt(u140), 1);
1920 try testDivCeil(u140, maxInt(u140), 2, maxInt(u140) / 2 + 1);
1921 try testDivCeil(u140, (1 << 139) + 5, 1 << 70, (1 << 69) + 1);
1922 try testDivCeil(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 2);
1923 try testDivCeil(u200, 123, 1 << 100, 1);
1924 try testDivCeil(u200, 1 << 120, 1 << 60, 1 << 60);
1925 try testDivCeil(u200, maxInt(u200), 1 << 100, 1 << 100);
1926
1927 try testDivCeil(i140, 0, maxInt(i140), 0);
1928 try testDivCeil(i140, maxInt(i140), maxInt(i140), 1);
1929 try testDivCeil(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50));
1930 try testDivCeil(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50));
1931 try testDivCeil(i140, -((1 << 100) + 1), -(1 << 50), (1 << 50) + 1);
1932 try testDivCeil(i200, -3, 2, -1);
1933 try testDivCeil(i200, minInt(i200), 1, minInt(i200));
1934 try testDivCeil(i200, minInt(i200), -2, 1 << 198);
1935 try testDivCeil(i200, maxInt(i200), 2, 1 << 198);
1936}
1937
1849fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {1938fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1850 try expect(@divTrunc(numerator, denominator) == expected);1939 try expect(@divTrunc(numerator, denominator) == expected);
1851}1940}
test/behavior/vector.zig+35-1
...@@ -510,8 +510,37 @@ test "vector division operators" {...@@ -510,8 +510,37 @@ test "vector division operators" {
510 inline for (@as([4]T, d2), 0..) |v, i| {510 inline for (@as([4]T, d2), 0..) |v, i| {
511 try expect(@divFloor(x[i], y[i]) == v);511 try expect(@divFloor(x[i], y[i]) == v);
512 }512 }
513 const d3 = @divTrunc(x, y);513 const d3 = @divCeil(x, y);
514 inline for (@as([4]T, d3), 0..) |v, i| {514 inline for (@as([4]T, d3), 0..) |v, i| {
515 try expect(@divCeil(x[i], y[i]) == v);
516 }
517 const d4 = @divTrunc(x, y);
518 inline for (@as([4]T, d4), 0..) |v, i| {
519 try expect(@divTrunc(x[i], y[i]) == v);
520 }
521 }
522
523 fn doTheTestDivNoExact(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {
524 const is_signed_int = switch (@typeInfo(T)) {
525 .int => |info| info.signedness == .signed,
526 else => false,
527 };
528 if (!is_signed_int) {
529 const d0 = x / y;
530 inline for (@as([4]T, d0), 0..) |v, i| {
531 try expect(x[i] / y[i] == v);
532 }
533 }
534 const d2 = @divFloor(x, y);
535 inline for (@as([4]T, d2), 0..) |v, i| {
536 try expect(@divFloor(x[i], y[i]) == v);
537 }
538 const d3 = @divCeil(x, y);
539 inline for (@as([4]T, d3), 0..) |v, i| {
540 try expect(@divCeil(x[i], y[i]) == v);
541 }
542 const d4 = @divTrunc(x, y);
543 inline for (@as([4]T, d4), 0..) |v, i| {
515 try expect(@divTrunc(x[i], y[i]) == v);544 try expect(@divTrunc(x[i], y[i]) == v);
516 }545 }
517 }546 }
...@@ -566,6 +595,9 @@ test "vector division operators" {...@@ -566,6 +595,9 @@ test "vector division operators" {
566 try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });595 try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
567 try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });596 try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
568 try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });597 try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
598
599 try doTheTestDivNoExact(u64, [4]u64{ 4, 5, 6, 7 }, [4]u64{ 4, 4, 4, 4 });
600 try doTheTestDivNoExact(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 3, 3, -3, -3 });
569 }601 }
570 };602 };
571603
...@@ -1318,11 +1350,13 @@ test "zero divisor" {...@@ -1318,11 +1350,13 @@ test "zero divisor" {
1318 const v2 = @divExact(zeros, ones);1350 const v2 = @divExact(zeros, ones);
1319 const v3 = @divTrunc(zeros, ones);1351 const v3 = @divTrunc(zeros, ones);
1320 const v4 = @divFloor(zeros, ones);1352 const v4 = @divFloor(zeros, ones);
1353 const v5 = @divCeil(zeros, ones);
13211354
1322 _ = v1[0];1355 _ = v1[0];
1323 _ = v2[0];1356 _ = v2[0];
1324 _ = v3[0];1357 _ = v3[0];
1325 _ = v4[0];1358 _ = v4[0];
1359 _ = v5[0];
1326}1360}
13271361
1328test "zero multiplicand" {1362test "zero multiplicand" {
test/behavior/x86_64/binary.zig+21
...@@ -5279,6 +5279,27 @@ test divFloorOptimized {...@@ -5279,6 +5279,27 @@ test divFloorOptimized {
5279 try test_div_floor_optimized.testFloatVectors();5279 try test_div_floor_optimized.testFloatVectors();
5280}5280}
52815281
5282inline fn divCeilUnoptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
5283 return @divCeil(lhs, rhs);
5284}
5285test divCeilUnoptimized {
5286 const test_div_ceil_unoptimized = binary(divCeilUnoptimized, .{ .compare = .approx_int });
5287 try test_div_ceil_unoptimized.testInts();
5288 try test_div_ceil_unoptimized.testIntVectors();
5289 try test_div_ceil_unoptimized.testFloats();
5290 try test_div_ceil_unoptimized.testFloatVectors();
5291}
5292
5293inline fn divCeilOptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
5294 @setFloatMode(.optimized);
5295 return @divCeil(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0)));
5296}
5297test divCeilOptimized {
5298 const test_div_ceil_optimized = binary(divCeilOptimized, .{ .compare = .approx_int });
5299 try test_div_ceil_optimized.testFloats();
5300 try test_div_ceil_optimized.testFloatVectors();
5301}
5302
5282inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {5303inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {
5283 return @rem(lhs, rhs);5304 return @rem(lhs, rhs);
5284}5305}
test/cases/compile_errors/signed_integer_division.zig+1-1
...@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {...@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {
44
5// error5// error
6//6//
7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact7// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact