| author | |
| committer | |
| log | de7fdfecd81e74c115395256179c64cb480cb860 |
| tree | 6bdf7898cf24cf909be6a96b06a3bb40936333d2 |
| parent | ac7bf53d9486d6c9a079a818dc87891551eaeac1 |
| parent | e42cb3b2369b3034c15be3a6f0bf519db4baac98 |
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 | 1370 | <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li> |
| 1371 | 1371 | <li>Signed integer operands must be comptime-known and positive. In other cases, use |
| 1372 | 1372 | {#link|@divTrunc#}, |
| 1373 | {#link|@divFloor#}, or | |
| 1373 | {#link|@divFloor#}, | |
| 1374 | {#link|@divCeil#}, or | |
| 1374 | 1375 | {#link|@divExact#} instead. |
| 1375 | 1376 | </li> |
| 1376 | 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 | 4736 | <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li> |
| 4736 | 4737 | </ul> |
| 4737 | 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 | 4740 | {#header_close#} |
| 4740 | 4741 | {#header_open|@divFloor#} |
| 4741 | 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 | 4750 | <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li> |
| 4750 | 4751 | </ul> |
| 4751 | 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 | 4754 | {#header_close#} |
| 4754 | 4755 | {#header_open|@divTrunc#} |
| 4755 | 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 | 4764 | <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li> |
| 4764 | 4765 | </ul> |
| 4765 | 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 | 4781 | {#header_close#} |
| 4768 | 4782 | |
| 4769 | 4783 | {#header_open|@embedFile#} |
| ... | ... | @@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 6095 | 6109 | <li>{#syntax#}/{#endsyntax#} (division)</li> |
| 6096 | 6110 | <li>{#link|@divTrunc#} (division)</li> |
| 6097 | 6111 | <li>{#link|@divFloor#} (division)</li> |
| 6112 | <li>{#link|@divCeil#} (division)</li> | |
| 6098 | 6113 | <li>{#link|@divExact#} (division)</li> |
| 6099 | 6114 | </ul> |
| 6100 | 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 | 6127 | <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li> |
| 6113 | 6128 | <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li> |
| 6114 | 6129 | <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li> |
| 6130 | <li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li> | |
| 6115 | 6131 | <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li> |
| 6116 | 6132 | <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li> |
| 6117 | 6133 | </ul> |
lib/compiler/Maker/WebServer.zig+1-1| ... | ... | @@ -163,7 +163,7 @@ pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void { |
| 163 | 163 | assert(idx == step_names_trailing.len); |
| 164 | 164 | } |
| 165 | 165 | |
| 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 | 167 | errdefer gpa.free(step_status_bits); |
| 168 | 168 | @memset(step_status_bits, 0); |
| 169 | 169 |
lib/compiler_rt/limb64.zig+1-2| ... | ... | @@ -3,7 +3,6 @@ const testing = std.testing; |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | const minInt = std.math.minInt; |
| 6 | const divCeil = std.math.divCeil; | |
| 7 | 6 | |
| 8 | 7 | const builtin = @import("builtin"); |
| 9 | 8 | const compiler_rt = @import("../compiler_rt.zig"); |
| ... | ... | @@ -26,7 +25,7 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void { |
| 26 | 25 | } |
| 27 | 26 | |
| 28 | 27 | fn usedLimbCount(bits: u16) u16 { |
| 29 | return divCeil(u16, bits, 64) catch unreachable; | |
| 28 | return @divCeil(bits, 64); | |
| 30 | 29 | } |
| 31 | 30 | |
| 32 | 31 | fn limbCount(bits: u16) u16 { |
lib/compiler_rt/udivmodei4.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const shl = std.math.shl; |
| 8 | 8 | const compiler_rt = @import("../compiler_rt.zig"); |
| 9 | 9 | const symbol = @import("../compiler_rt.zig").symbol; |
| 10 | 10 | |
| 11 | const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max supported type is u65535 | |
| 11 | const max_limbs = @divCeil(65535, 32); // max supported type is u65535 | |
| 12 | 12 | |
| 13 | 13 | comptime { |
| 14 | 14 | symbol(&__udivei4, "__udivei4"); |
lib/fuzzer.zig+1-1| ... | ... | @@ -52,7 +52,7 @@ var fuzzer: Fuzzer = undefined; |
| 52 | 52 | var current_test_name: ?[]const u8 = null; |
| 53 | 53 | |
| 54 | 54 | fn bitsetUsizes(elems: usize) usize { |
| 55 | return math.divCeil(usize, elems, @bitSizeOf(usize)) catch unreachable; | |
| 55 | return @divCeil(elems, @bitSizeOf(usize)); | |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | const Executable = struct { |
lib/std/Random.zig+1-1| ... | ... | @@ -126,7 +126,7 @@ pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: ty |
| 126 | 126 | pub fn int(r: Random, comptime T: type) T { |
| 127 | 127 | const bits = @typeInfo(T).int.bits; |
| 128 | 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 | 130 | const ByteAlignedT = @Int(.unsigned, ceil_bytes * 8); |
| 131 | 131 | |
| 132 | 132 | var rand_bytes: [ceil_bytes]u8 = undefined; |
lib/std/Target.zig+1-1| ... | ... | @@ -1232,7 +1232,7 @@ pub const Cpu = struct { |
| 1232 | 1232 | ints: [usize_count]usize, |
| 1233 | 1233 | |
| 1234 | 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 | 1236 | pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize); |
| 1237 | 1237 | pub const Index = std.math.Log2Int(@Int(.unsigned, usize_count * @bitSizeOf(usize))); |
| 1238 | 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 | 39 | mem.writeInt(u32, j[nonce_length..][0..4], 1, .big); |
| 40 | 40 | aes.encrypt(&t, &j); |
| 41 | 41 | |
| 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 | 43 | var mac = Ghash.initForBlockCount(&h, block_count); |
| 44 | 44 | mac.update(ad); |
| 45 | 45 | mac.pad(); |
| ... | ... | @@ -81,7 +81,7 @@ fn AesGcm(comptime Aes: anytype) type { |
| 81 | 81 | mem.writeInt(u32, j[nonce_length..][0..4], 1, .big); |
| 82 | 82 | aes.encrypt(&t, &j); |
| 83 | 83 | |
| 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 | 85 | var mac = Ghash.initForBlockCount(&h, block_count); |
| 86 | 86 | mac.update(ad); |
| 87 | 87 | mac.pad(); |
lib/std/crypto/ascon.zig+1-1| ... | ... | @@ -198,7 +198,7 @@ pub fn State(comptime endian: std.builtin.Endian) type { |
| 198 | 198 | /// |
| 199 | 199 | /// Note: Clears complete words that contain the specified byte range |
| 200 | 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 | } |
| 203 | 203 | |
| 204 | 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 | 61 | |
| 62 | 62 | return struct { |
| 63 | 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); | |
| 65 | 65 | |
| 66 | 66 | limbs_buffer: [max_limbs_count]Limb, |
| 67 | 67 | /// The number of active limbs. |
| 68 | 68 | limbs_len: usize, |
| 69 | 69 | |
| 70 | 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); | |
| 72 | 72 | |
| 73 | 73 | /// Constant slice of active limbs. |
| 74 | 74 | fn limbsConst(self: *const Self) []const Limb { |
| ... | ... | @@ -847,7 +847,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 847 | 847 | } |
| 848 | 848 | var e_normalized = Fe{ .v = e.v.normalize() }; |
| 849 | 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 | 851 | e_normalized.toBytes(buf, .little) catch unreachable; |
| 852 | 852 | const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]); |
| 853 | 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 | 74 | // block |
| 75 | 75 | // |
| 76 | 76 | |
| 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 | 78 | var r = dk_len % h_len; |
| 79 | 79 | if (r == 0) { |
| 80 | 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 | 474 | return struct { |
| 475 | 475 | const Self = @This(); |
| 476 | 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 | 478 | pub const Options = struct {}; |
| 479 | 479 | |
| 480 | 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 | 58 | st: State, |
| 59 | 59 | |
| 60 | 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 | 62 | /// The block length, or rate, in bytes. |
| 63 | 63 | pub const block_length = State.rate; |
| 64 | 64 | /// The delimiter can be overwritten in the options. |
| ... | ... | @@ -464,7 +464,7 @@ pub const NistLengthEncoding = enum { |
| 464 | 464 | /// Encode a length according to NIST SP 800-185. |
| 465 | 465 | pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length { |
| 466 | 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); | |
| 468 | 468 | |
| 469 | 469 | var res = Length{ .len = len_bytes + 1 }; |
| 470 | 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 | 349 | var chunks = mem.window(u8, bytes, 16, 16); |
| 350 | 350 | while (chunks.next()) |window| { |
| 351 | 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 | 353 | try t.setColor(.dim); |
| 354 | 354 | // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. |
| 355 | 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 | 1391 | const max_index: comptime_int = std.math.maxInt(RangedType); |
| 1392 | 1392 | const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) { |
| 1393 | 1393 | .unsigned => 0, |
| 1394 | .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable, | |
| 1394 | .signed => @divCeil(max_index, 2), | |
| 1395 | 1395 | }; |
| 1396 | 1396 | |
| 1397 | 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 | 99 | } else { |
| 100 | 100 | // Take only the part containing the key value, the remaining |
| 101 | 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 | 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 | 922 | pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T { |
| 923 | 923 | @setRuntimeSafety(false); |
| 924 | 924 | if (denominator == 0) return error.DivisionByZero; |
| 925 | const info = @typeInfo(T); | |
| 926 | switch (info) { | |
| 927 | .comptime_float, .float => return @ceil(numerator / denominator), | |
| 928 | .comptime_int, .int => { | |
| 929 | if (numerator < 0 and denominator < 0) { | |
| 930 | if (info == .int and numerator == minInt(T) and denominator == -1) | |
| 931 | return error.Overflow; | |
| 932 | return @divFloor(numerator + 1, denominator) + 1; | |
| 933 | } | |
| 934 | if (numerator > 0 and denominator > 0) | |
| 935 | return @divFloor(numerator - 1, denominator) + 1; | |
| 936 | return @divTrunc(numerator, denominator); | |
| 937 | }, | |
| 938 | else => @compileError("divCeil unsupported on " ++ @typeName(T)), | |
| 925 | if (@typeInfo(T) == .int and numerator == minInt(T) and denominator == -1) { | |
| 926 | return error.Overflow; | |
| 939 | 927 | } |
| 928 | return @divCeil(numerator, denominator); | |
| 940 | 929 | } |
| 941 | 930 | |
| 942 | 931 | test divCeil { |
lib/std/math/big/int.zig+76-1| ... | ... | @@ -122,7 +122,7 @@ pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize { |
| 122 | 122 | /// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero |
| 123 | 123 | /// and this big integer implementation stores zero using one limb. |
| 124 | 124 | pub 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 | } |
| 127 | 127 | |
| 128 | 128 | /// a + b * c + *carry, sets carry to the overflow bits |
| ... | ... | @@ -1221,6 +1221,62 @@ pub const Mutable = struct { |
| 1221 | 1221 | } |
| 1222 | 1222 | } |
| 1223 | 1223 | |
| 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 | 1280 | /// q = a / b (rem r) |
| 1225 | 1281 | /// |
| 1226 | 1282 | /// a / b are truncated (rounded towards -inf). |
| ... | ... | @@ -3314,6 +3370,25 @@ pub const Managed = struct { |
| 3314 | 3370 | r.setMetadata(mr.positive, mr.len); |
| 3315 | 3371 | } |
| 3316 | 3372 | |
| 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 | 3392 | /// q = a / b (rem r) |
| 3318 | 3393 | /// |
| 3319 | 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 | 2127 | try testing.expectEqual(10, try r.toInt(i32)); |
| 2128 | 2128 | } |
| 2129 | 2129 | |
| 2130 | fn 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 | ||
| 2147 | test "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 | ||
| 2155 | test "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 | ||
| 2130 | 2168 | test "div multi-multi with rem" { |
| 2131 | 2169 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 2132 | 2170 |
lib/std/mem.zig+3-3| ... | ... | @@ -1937,7 +1937,7 @@ fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T |
| 1937 | 1937 | const bit_count = @as(usize, @bitSizeOf(T)); |
| 1938 | 1938 | const bit_shift = @as(u3, @intCast(bit_offset % 8)); |
| 1939 | 1939 | |
| 1940 | const load_size = (bit_count + 7) / 8; | |
| 1940 | const load_size = @divCeil(bit_count, 8); | |
| 1941 | 1941 | const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count)); |
| 1942 | 1942 | const LoadInt = @Int(.unsigned, load_size * 8); |
| 1943 | 1943 | |
| ... | ... | @@ -1964,9 +1964,9 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T { |
| 1964 | 1964 | |
| 1965 | 1965 | const bit_count = @as(usize, @bitSizeOf(T)); |
| 1966 | 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); | |
| 1968 | 1968 | |
| 1969 | const load_size = (bit_count + 7) / 8; | |
| 1969 | const load_size = @divCeil(bit_count, 8); | |
| 1970 | 1970 | const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count)); |
| 1971 | 1971 | const LoadInt = @Int(.unsigned, load_size * 8); |
| 1972 | 1972 |
lib/std/zig/AstGen.zig+3-1| ... | ... | @@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2845 | 2845 | .bit_reverse, |
| 2846 | 2846 | .div_exact, |
| 2847 | 2847 | .div_floor, |
| 2848 | .div_ceil, | |
| 2848 | 2849 | .div_trunc, |
| 2849 | 2850 | .mod, |
| 2850 | 2851 | .rem, |
| ... | ... | @@ -4895,7 +4896,7 @@ fn structDeclInner( |
| 4895 | 4896 | const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len); |
| 4896 | 4897 | const field_comptime_bits = try scratch.addOptionalSlice( |
| 4897 | 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 | 4901 | if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0); |
| 4901 | 4902 | |
| ... | ... | @@ -9392,6 +9393,7 @@ fn builtinCall( |
| 9392 | 9393 | |
| 9393 | 9394 | .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), |
| 9394 | 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 | 9397 | .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), |
| 9396 | 9398 | .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), |
| 9397 | 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 | 936 | }, |
| 937 | 937 | .div_exact, |
| 938 | 938 | .div_floor, |
| 939 | .div_ceil, | |
| 939 | 940 | .div_trunc, |
| 940 | 941 | .mod, |
| 941 | 942 | .rem, |
lib/std/zig/BuiltinFn.zig+8| ... | ... | @@ -31,6 +31,7 @@ pub const Tag = enum { |
| 31 | 31 | c_va_copy, |
| 32 | 32 | c_va_end, |
| 33 | 33 | c_va_start, |
| 34 | div_ceil, | |
| 34 | 35 | div_exact, |
| 35 | 36 | div_floor, |
| 36 | 37 | div_trunc, |
| ... | ... | @@ -398,6 +399,13 @@ pub const list = list: { |
| 398 | 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 | 410 | "@divTrunc", |
| 403 | 411 | .{ |
lib/std/zig/Zir.zig+8-1| ... | ... | @@ -200,6 +200,9 @@ pub const Inst = struct { |
| 200 | 200 | /// Implements the `@divFloor` builtin. |
| 201 | 201 | /// Uses the `pl_node` union field with payload `Bin`. |
| 202 | 202 | div_floor, |
| 203 | /// Implements the `@divCeil` builtin. | |
| 204 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 205 | div_ceil, | |
| 203 | 206 | /// Implements the `@divTrunc` builtin. |
| 204 | 207 | /// Uses the `pl_node` union field with payload `Bin`. |
| 205 | 208 | div_trunc, |
| ... | ... | @@ -1267,6 +1270,7 @@ pub const Inst = struct { |
| 1267 | 1270 | .bit_reverse, |
| 1268 | 1271 | .div_exact, |
| 1269 | 1272 | .div_floor, |
| 1273 | .div_ceil, | |
| 1270 | 1274 | .div_trunc, |
| 1271 | 1275 | .mod, |
| 1272 | 1276 | .rem, |
| ... | ... | @@ -1547,6 +1551,7 @@ pub const Inst = struct { |
| 1547 | 1551 | .bit_reverse, |
| 1548 | 1552 | .div_exact, |
| 1549 | 1553 | .div_floor, |
| 1554 | .div_ceil, | |
| 1550 | 1555 | .div_trunc, |
| 1551 | 1556 | .mod, |
| 1552 | 1557 | .rem, |
| ... | ... | @@ -1815,6 +1820,7 @@ pub const Inst = struct { |
| 1815 | 1820 | |
| 1816 | 1821 | .div_exact = .pl_node, |
| 1817 | 1822 | .div_floor = .pl_node, |
| 1823 | .div_ceil = .pl_node, | |
| 1818 | 1824 | .div_trunc = .pl_node, |
| 1819 | 1825 | .mod = .pl_node, |
| 1820 | 1826 | .rem = .pl_node, |
| ... | ... | @@ -4115,6 +4121,7 @@ fn findTrackableInner( |
| 4115 | 4121 | .mul_sat, |
| 4116 | 4122 | .div_exact, |
| 4117 | 4123 | .div_floor, |
| 4124 | .div_ceil, | |
| 4118 | 4125 | .div_trunc, |
| 4119 | 4126 | .mod, |
| 4120 | 4127 | .rem, |
| ... | ... | @@ -5272,7 +5279,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe |
| 5272 | 5279 | break :lens @ptrCast(lens); |
| 5273 | 5280 | } else null; |
| 5274 | 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 | 5283 | const bits = zir.extra[extra_index..][0..bits_len]; |
| 5277 | 5284 | extra_index += bits_len; |
| 5278 | 5285 | break :bits bits; |
lib/zig.h+32| ... | ... | @@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t; |
| 813 | 813 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 814 | 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 | 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 | 2067 | return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask)); |
| 2059 | 2068 | } |
| 2060 | 2069 | |
| 2070 | static 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 | ||
| 2077 | static 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 | 2085 | #define zig_mod_u128 zig_rem_u128 |
| 2062 | 2086 | |
| 2063 | 2087 | static 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 | 3275 | zig_trap(); |
| 3252 | 3276 | } |
| 3253 | 3277 | |
| 3278 | static 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 | ||
| 3254 | 3282 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); |
| 3255 | 3283 | static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { |
| 3256 | 3284 | if (!is_signed) { |
| ... | ... | @@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) |
| 4010 | 4038 | static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4011 | 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 | 4046 | static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4015 | 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 | 143 | div_floor, |
| 144 | 144 | /// Same as `div_floor` with optimized float mode. |
| 145 | 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 | 153 | /// Integer or float division. |
| 147 | 154 | /// If a remainder would be produced, illegal behavior occurs. |
| 148 | 155 | /// For integers, overflow is illegal behavior. |
| ... | ... | @@ -1510,7 +1517,7 @@ pub const ShuffleTwoMask = enum(u32) { |
| 1510 | 1517 | /// Trailing: |
| 1511 | 1518 | /// 0. `Inst.Ref` for every outputs_len |
| 1512 | 1519 | /// 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 | 1521 | /// Memory starting at this position is reinterpreted as the source bytes. |
| 1515 | 1522 | /// 3. for every outputs_len |
| 1516 | 1523 | /// - 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 | 1612 | .div_float, |
| 1606 | 1613 | .div_trunc, |
| 1607 | 1614 | .div_floor, |
| 1615 | .div_ceil, | |
| 1608 | 1616 | .div_exact, |
| 1609 | 1617 | .rem, |
| 1610 | 1618 | .mod, |
| ... | ... | @@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1624 | 1632 | .div_float_optimized, |
| 1625 | 1633 | .div_trunc_optimized, |
| 1626 | 1634 | .div_floor_optimized, |
| 1635 | .div_ceil_optimized, | |
| 1627 | 1636 | .div_exact_optimized, |
| 1628 | 1637 | .rem_optimized, |
| 1629 | 1638 | .mod_optimized, |
| ... | ... | @@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1985 | 1994 | .div_trunc_optimized, |
| 1986 | 1995 | .div_floor, |
| 1987 | 1996 | .div_floor_optimized, |
| 1997 | .div_ceil, | |
| 1998 | .div_ceil_optimized, | |
| 1988 | 1999 | .div_exact, |
| 1989 | 2000 | .div_exact_optimized, |
| 1990 | 2001 | .rem, |
| ... | ... | @@ -2214,7 +2225,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch { |
| 2214 | 2225 | } |
| 2215 | 2226 | const pl_op = inst.data.pl_op; |
| 2216 | 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 | 2229 | return .{ |
| 2219 | 2230 | .air = air, |
| 2220 | 2231 | .operand = pl_op.operand, |
| ... | ... | @@ -2383,7 +2394,7 @@ pub const UnwrappedAsm = struct { |
| 2383 | 2394 | const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0); |
| 2384 | 2395 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 2385 | 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 | 2398 | self.constraint_names = self.constraint_names[next_offset..]; |
| 2388 | 2399 | |
| 2389 | 2400 | return .{ |
src/Air/Legalize.zig+194-3| ... | ... | @@ -54,6 +54,8 @@ pub const Feature = enum { |
| 54 | 54 | scalarize_div_trunc_optimized, |
| 55 | 55 | scalarize_div_floor, |
| 56 | 56 | scalarize_div_floor_optimized, |
| 57 | scalarize_div_ceil, | |
| 58 | scalarize_div_ceil_optimized, | |
| 57 | 59 | scalarize_div_exact, |
| 58 | 60 | scalarize_div_exact_optimized, |
| 59 | 61 | scalarize_rem, |
| ... | ... | @@ -173,6 +175,15 @@ pub const Feature = enum { |
| 173 | 175 | /// Not compatible with `scalarize_mul_safe`. |
| 174 | 176 | expand_mul_safe, |
| 175 | 177 | |
| 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 | 187 | /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`. |
| 177 | 188 | /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the |
| 178 | 189 | /// first byte of memory until bit pointers know their backing type. |
| ... | ... | @@ -231,6 +242,8 @@ pub const Feature = enum { |
| 231 | 242 | .div_trunc_optimized => .scalarize_div_trunc_optimized, |
| 232 | 243 | .div_floor => .scalarize_div_floor, |
| 233 | 244 | .div_floor_optimized => .scalarize_div_floor_optimized, |
| 245 | .div_ceil => .scalarize_div_ceil, | |
| 246 | .div_ceil_optimized => .scalarize_div_ceil_optimized, | |
| 234 | 247 | .div_exact => .scalarize_div_exact, |
| 235 | 248 | .div_exact_optimized => .scalarize_div_exact_optimized, |
| 236 | 249 | .rem => .scalarize_rem, |
| ... | ... | @@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 382 | 395 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { |
| 383 | 396 | .none => {}, |
| 384 | 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 | 399 | inst, |
| 387 | 400 | bin_op.lhs, |
| 388 | 401 | bin_op.rhs, |
| ... | ... | @@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 596 | 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 | 636 | inline .int_from_float_safe, |
| 600 | 637 | .int_from_float_optimized_safe, |
| 601 | 638 | => |air_tag| { |
| ... | ... | @@ -709,7 +746,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 709 | 746 | .switch_br, .loop_switch_br => { |
| 710 | 747 | const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 711 | 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 | 750 | var extra_index = extra.end + hint_bag_count; |
| 714 | 751 | for (0..extra.data.cases_len) |_| { |
| 715 | 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 | 2456 | } }; |
| 2420 | 2457 | } |
| 2421 | 2458 | |
| 2459 | fn 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 | ||
| 2422 | 2612 | fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { |
| 2423 | 2613 | const pt = l.pt; |
| 2424 | 2614 | const zcu = pt.zcu; |
| ... | ... | @@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload( |
| 3426 | 3616 | } }; |
| 3427 | 3617 | } |
| 3428 | 3618 | |
| 3429 | fn softFloatDivTruncFloorBlockPayload( | |
| 3619 | fn softFloatDivTruncFloorCeilBlockPayload( | |
| 3430 | 3620 | l: *Legalize, |
| 3431 | 3621 | orig_inst: Air.Inst.Index, |
| 3432 | 3622 | lhs: Air.Inst.Ref, |
| ... | ... | @@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload( |
| 3441 | 3631 | const floor_tag: Air.Inst.Tag = switch (air_tag) { |
| 3442 | 3632 | .div_trunc, .div_trunc_optimized => .trunc_float, |
| 3443 | 3633 | .div_floor, .div_floor_optimized => .floor, |
| 3634 | .div_ceil, .div_ceil_optimized => .ceil, | |
| 3444 | 3635 | else => unreachable, |
| 3445 | 3636 | }; |
| 3446 | 3637 |
src/Air/Liveness.zig+2| ... | ... | @@ -417,6 +417,8 @@ fn analyzeInst( |
| 417 | 417 | .div_floor_optimized, |
| 418 | 418 | .div_exact, |
| 419 | 419 | .div_exact_optimized, |
| 420 | .div_ceil, | |
| 421 | .div_ceil_optimized, | |
| 420 | 422 | .rem, |
| 421 | 423 | .rem_optimized, |
| 422 | 424 | .mod, |
src/Air/Liveness/Verify.zig+2| ... | ... | @@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 235 | 235 | .div_trunc_optimized, |
| 236 | 236 | .div_floor, |
| 237 | 237 | .div_floor_optimized, |
| 238 | .div_ceil, | |
| 239 | .div_ceil_optimized, | |
| 238 | 240 | .div_exact, |
| 239 | 241 | .div_exact_optimized, |
| 240 | 242 | .rem, |
src/Air/Verify.zig+2| ... | ... | @@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void { |
| 253 | 253 | .div_trunc_optimized, |
| 254 | 254 | .div_floor, |
| 255 | 255 | .div_floor_optimized, |
| 256 | .div_ceil, | |
| 257 | .div_ceil_optimized, | |
| 256 | 258 | .div_exact, |
| 257 | 259 | .div_exact_optimized, |
| 258 | 260 | .rem, |
src/Air/print.zig+2| ... | ... | @@ -132,6 +132,7 @@ const Writer = struct { |
| 132 | 132 | .div_float, |
| 133 | 133 | .div_trunc, |
| 134 | 134 | .div_floor, |
| 135 | .div_ceil, | |
| 135 | 136 | .div_exact, |
| 136 | 137 | .rem, |
| 137 | 138 | .mod, |
| ... | ... | @@ -160,6 +161,7 @@ const Writer = struct { |
| 160 | 161 | .div_float_optimized, |
| 161 | 162 | .div_trunc_optimized, |
| 162 | 163 | .div_floor_optimized, |
| 164 | .div_ceil_optimized, | |
| 163 | 165 | .div_exact_optimized, |
| 164 | 166 | .rem_optimized, |
| 165 | 167 | .mod_optimized, |
src/InternPool.zig+3-3| ... | ... | @@ -3581,11 +3581,11 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { |
| 3581 | 3581 | .start = extra_index, |
| 3582 | 3582 | .len = extra.data.fields_len, |
| 3583 | 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 | 3585 | const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{ |
| 3586 | 3586 | .tid = unwrapped_index.tid, |
| 3587 | 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 | 3589 | } else .empty; |
| 3590 | 3590 | extra_index += field_is_comptime_bits.len; |
| 3591 | 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 | 3737 | .start = extra_index, |
| 3738 | 3738 | .len = extra.data.fields_len, |
| 3739 | 3739 | } else .empty; |
| 3740 | extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable; | |
| 3740 | extra_index += @divCeil(field_aligns.len, 4); | |
| 3741 | 3741 | |
| 3742 | 3742 | return .{ |
| 3743 | 3743 | .zir_index = extra.data.zir_index, |
src/Sema.zig+73-7| ... | ... | @@ -1335,6 +1335,7 @@ fn analyzeBodyInner( |
| 1335 | 1335 | .div => try sema.zirDiv(block, inst), |
| 1336 | 1336 | .div_exact => try sema.zirDivExact(block, inst), |
| 1337 | 1337 | .div_floor => try sema.zirDivFloor(block, inst), |
| 1338 | .div_ceil => try sema.zirDivCeil(block, inst), | |
| 1338 | 1339 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 1339 | 1340 | |
| 1340 | 1341 | .mod_rem => try sema.zirModRem(block, inst), |
| ... | ... | @@ -10304,7 +10305,7 @@ fn finishSwitchBr( |
| 10304 | 10305 | fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void { |
| 10305 | 10306 | const unused_hints = hints.bags.capacity * hints_per_bag - hints.count; |
| 10306 | 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 | 10309 | return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required); |
| 10309 | 10310 | } |
| 10310 | 10311 | fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void { |
| ... | ... | @@ -10320,7 +10321,7 @@ fn finishSwitchBr( |
| 10320 | 10321 | } |
| 10321 | 10322 | }; |
| 10322 | 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 | 10325 | break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 }; |
| 10325 | 10326 | }; |
| 10326 | 10327 | defer branch_hints.bags.deinit(gpa); |
| ... | ... | @@ -12211,7 +12212,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion( |
| 12211 | 12212 | { |
| 12212 | 12213 | // All branch hints are `.none`, so just add zero elems. |
| 12213 | 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 | 12216 | try cases_extra.appendNTimes(gpa, 0, need_elems); |
| 12216 | 12217 | } |
| 12217 | 12218 | |
| ... | ... | @@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 13850 | 13851 | return sema.fail( |
| 13851 | 13852 | block, |
| 13852 | 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 | 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 | 14024 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); |
| 14024 | 14025 | } |
| 14025 | 14026 | |
| 14027 | fn 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 | ||
| 14026 | 14092 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14027 | 14093 | const pt = sema.pt; |
| 14028 | 14094 | const zcu = pt.zcu; |
| ... | ... | @@ -18051,7 +18117,7 @@ fn analyzeRet( |
| 18051 | 18117 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 18052 | 18118 | // extend this swich as additional operators are implemented |
| 18053 | 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 | 18121 | else => false, |
| 18056 | 18122 | }; |
| 18057 | 18123 | } |
| ... | ... | @@ -18632,7 +18698,7 @@ fn finishStructInit( |
| 18632 | 18698 | return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); |
| 18633 | 18699 | }, |
| 18634 | 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 | 18702 | @memset(buf, 0); |
| 18637 | 18703 | var bit_offset: u16 = 0; |
| 18638 | 18704 | for (field_inits) |field_init| { |
| ... | ... | @@ -29679,7 +29745,7 @@ pub fn bitCastVal( |
| 29679 | 29745 | if (val.isUndef(zcu)) { |
| 29680 | 29746 | return pt.undefValue(dest_ty); |
| 29681 | 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 | 29749 | @memset(buf, 0); |
| 29684 | 29750 | val.writeToPackedMemory(zcu, buf, 0); |
| 29685 | 29751 | return .readFromPackedMemory(dest_ty, pt, buf, 0); |
src/Sema/arith.zig+69-3| ... | ... | @@ -768,7 +768,7 @@ fn mulSatScalar( |
| 768 | 768 | } |
| 769 | 769 | } |
| 770 | 770 | |
| 771 | pub const DivOp = enum { div, div_trunc, div_floor, div_exact }; | |
| 771 | pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact }; | |
| 772 | 772 | |
| 773 | 773 | /// Applies the `/` operator to comptime-known values. |
| 774 | 774 | /// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`. |
| ... | ... | @@ -843,6 +843,11 @@ fn divScalar( |
| 843 | 843 | if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx); |
| 844 | 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 | 851 | .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) { |
| 847 | 852 | .remainder => return sema.fail(block, src, "exact division produced remainder", .{}), |
| 848 | 853 | .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx), |
| ... | ... | @@ -851,7 +856,7 @@ fn divScalar( |
| 851 | 856 | } |
| 852 | 857 | } else { |
| 853 | 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 | 860 | .div_exact => false, |
| 856 | 861 | }; |
| 857 | 862 | if (!allow_div_zero) { |
| ... | ... | @@ -871,6 +876,7 @@ fn divScalar( |
| 871 | 876 | .div => return floatDiv(sema, lhs_val, rhs_val, ty), |
| 872 | 877 | .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty), |
| 873 | 878 | .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty), |
| 879 | .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty), | |
| 874 | 880 | .div_exact => { |
| 875 | 881 | if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) { |
| 876 | 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 | 1761 | } |
| 1756 | 1762 | return pt.intValue_big(ty, result_q.toConst()); |
| 1757 | 1763 | } |
| 1764 | fn 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 | } | |
| 1777 | fn 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 | } | |
| 1758 | 1807 | fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { |
| 1759 | 1808 | const pt = sema.pt; |
| 1760 | 1809 | const zcu = pt.zcu; |
| ... | ... | @@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { |
| 2140 | 2189 | .storage = storage, |
| 2141 | 2190 | } })); |
| 2142 | 2191 | } |
| 2192 | fn 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 | } | |
| 2143 | 2209 | fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool { |
| 2144 | 2210 | const zcu = sema.pt.zcu; |
| 2145 | 2211 | const target = zcu.getTarget(); |
| ... | ... | @@ -2238,7 +2304,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value { |
| 2238 | 2304 | if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0); |
| 2239 | 2305 | const info = ty.intInfo(zcu); |
| 2240 | 2306 | |
| 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 | 2308 | @memset(buf, 0xAA); |
| 2243 | 2309 | |
| 2244 | 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 | 968 | if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64"; |
| 969 | 969 | if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32"; |
| 970 | 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 | 972 | return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes)); |
| 973 | 973 | } |
| 974 | 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 | 1111 | .vector_type => |vec| { |
| 1112 | 1112 | const elem_ty: Type = .fromInterned(vec.child); |
| 1113 | 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 | 1115 | .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu), |
| 1116 | 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 | 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 | 175 | .div_trunc_optimized, |
| 176 | 176 | .div_floor, |
| 177 | 177 | .div_floor_optimized, |
| 178 | .div_ceil, | |
| 179 | .div_ceil_optimized, | |
| 178 | 180 | .div_exact, |
| 179 | 181 | .div_exact_optimized, |
| 180 | 182 | .rem, |
| ... | ... | @@ -403,11 +405,11 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { |
| 403 | 405 | .live_registers = undefined, |
| 404 | 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 | 409 | try isel.analyze(air_body_block.body); |
| 408 | 410 | for ( |
| 409 | 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 | 413 | ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom; |
| 412 | 414 | isel.dom_start = initial_dom_start; |
| 413 | 415 | isel.dom_len = initial_dom_len; |
| ... | ... | @@ -589,7 +591,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { |
| 589 | 591 | .live_registers = undefined, |
| 590 | 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))); | |
| 593 | 595 | |
| 594 | 596 | var cases_it = switch_br.iterateCases(); |
| 595 | 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 | 599 | |
| 598 | 600 | for ( |
| 599 | 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 | 603 | ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom; |
| 602 | 604 | isel.dom_start = initial_dom_start; |
| 603 | 605 | isel.dom_len = initial_dom_len; |
| ... | ... | @@ -10194,7 +10196,7 @@ pub const Value = struct { |
| 10194 | 10196 | 0 => unreachable, |
| 10195 | 10197 | 1...64 => unreachable, |
| 10196 | 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 | 10200 | vi.setParts(isel, @intCast(parts_len)); |
| 10199 | 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 | 10240 | const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; |
| 10239 | 10241 | const array_len = array_type.lenIncludingSentinel(); |
| 10240 | 10242 | 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 | 10244 | return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}); |
| 10243 | 10245 | const alignment = vi.alignment(isel); |
| 10244 | 10246 | const Part = struct { offset: u64, size: u64 }; |
| ... | ... | @@ -10288,7 +10290,7 @@ pub const Value = struct { |
| 10288 | 10290 | .anyframe_type => unreachable, |
| 10289 | 10291 | .error_union_type => |error_union_type| { |
| 10290 | 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 | 10294 | return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}); |
| 10293 | 10295 | const alignment = vi.alignment(isel); |
| 10294 | 10296 | const payload_ty: ZigType = .fromInterned(error_union_type.payload_type); |
| ... | ... | @@ -10395,7 +10397,7 @@ pub const Value = struct { |
| 10395 | 10397 | } |
| 10396 | 10398 | const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; |
| 10397 | 10399 | 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 | 10401 | return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}); |
| 10400 | 10402 | const alignment = vi.alignment(isel); |
| 10401 | 10403 | const Part = struct { offset: u64, size: u64, signedness: ?std.lang.Signedness, is_vector: bool }; |
| ... | ... | @@ -10456,7 +10458,7 @@ pub const Value = struct { |
| 10456 | 10458 | .tuple_type => |tuple_type| { |
| 10457 | 10459 | const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; |
| 10458 | 10460 | 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 | 10462 | return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}); |
| 10461 | 10463 | const alignment = vi.alignment(isel); |
| 10462 | 10464 | const Part = struct { offset: u64, size: u64, is_vector: bool }; |
| ... | ... | @@ -10511,7 +10513,7 @@ pub const Value = struct { |
| 10511 | 10513 | } }, |
| 10512 | 10514 | } |
| 10513 | 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 | 10517 | return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}); |
| 10516 | 10518 | const union_layout = ZigType.getUnionLayout(loaded_union, zcu); |
| 10517 | 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 | 2675 | try airBinBuiltinCall(f, inst, "fmod", .none); |
| 2676 | 2676 | }, |
| 2677 | 2677 | .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), |
| 2678 | .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none), | |
| 2678 | 2679 | .mod => try airBinBuiltinCall(f, inst, "mod", .none), |
| 2679 | 2680 | .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none), |
| 2680 | 2681 | |
| ... | ... | @@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 2856 | 2857 | .div_float_optimized, |
| 2857 | 2858 | .div_trunc_optimized, |
| 2858 | 2859 | .div_floor_optimized, |
| 2860 | .div_ceil_optimized, | |
| 2859 | 2861 | .div_exact_optimized, |
| 2860 | 2862 | .rem_optimized, |
| 2861 | 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 | 383 | .div_float => try self.airDivFloat(inst, .normal), |
| 384 | 384 | .div_trunc => try self.airDivTrunc(inst, .normal), |
| 385 | 385 | .div_floor => try self.airDivFloor(inst, .normal), |
| 386 | .div_ceil => try self.airDivCeil(inst, .normal), | |
| 386 | 387 | .div_exact => try self.airDivExact(inst, .normal), |
| 387 | 388 | .rem => try self.airRem(inst, .normal), |
| 388 | 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 | 401 | .div_float_optimized => try self.airDivFloat(inst, .fast), |
| 401 | 402 | .div_trunc_optimized => try self.airDivTrunc(inst, .fast), |
| 402 | 403 | .div_floor_optimized => try self.airDivFloor(inst, .fast), |
| 404 | .div_ceil_optimized => try self.airDivCeil(inst, .fast), | |
| 403 | 405 | .div_exact_optimized => try self.airDivExact(inst, .fast), |
| 404 | 406 | .rem_optimized => try self.airRem(inst, .fast), |
| 405 | 407 | .mod_optimized => try self.airMod(inst, .fast), |
| ... | ... | @@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) |
| 3578 | 3580 | return self.wip.bin(.udiv, lhs, rhs, ""); |
| 3579 | 3581 | } |
| 3580 | 3582 | |
| 3583 | fn 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 | ||
| 3581 | 3655 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { |
| 3582 | 3656 | const zcu = self.object.zcu; |
| 3583 | 3657 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | ... | @@ -6874,7 +6948,7 @@ const ParamTypeIterator = struct { |
| 6874 | 6948 | switch (ip.indexToKey(ty.toIntern())) { |
| 6875 | 6949 | .struct_type => { |
| 6876 | 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 | 6952 | if (size % 8 > 0) { |
| 6879 | 6953 | it.types_buffer[types_index - 1] = |
| 6880 | 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 | 7193 | switch (ip.indexToKey(ret_ty.toIntern())) { |
| 7120 | 7194 | .struct_type => { |
| 7121 | 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 | 7197 | if (size % 8 > 0) { |
| 7124 | 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 | 1422 | .mod, |
| 1423 | 1423 | .div_float, |
| 1424 | 1424 | .div_floor, |
| 1425 | .div_ceil, | |
| 1425 | 1426 | => return func.fail("TODO: {s}", .{@tagName(tag)}), |
| 1426 | 1427 | |
| 1427 | 1428 | .sqrt, |
| ... | ... | @@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1621 | 1622 | .div_trunc_optimized, |
| 1622 | 1623 | .div_floor_optimized, |
| 1623 | 1624 | .div_exact_optimized, |
| 1625 | .div_ceil_optimized, | |
| 1624 | 1626 | .rem_optimized, |
| 1625 | 1627 | .mod_optimized, |
| 1626 | 1628 | .neg_optimized, |
| ... | ... | @@ -2215,8 +2217,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void { |
| 2215 | 2217 | }; |
| 2216 | 2218 | |
| 2217 | 2219 | const dst_mcv = if (dst_int_info.bits <= src_storage_bits and |
| 2218 | math.divCeil(u16, dst_int_info.bits, 64) catch unreachable == | |
| 2219 | math.divCeil(u32, src_storage_bits, 64) catch unreachable and | |
| 2220 | @divCeil(dst_int_info.bits, 64) == @divCeil(src_storage_bits, 64) and | |
| 2220 | 2221 | func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 2221 | 2222 | const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true); |
| 2222 | 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 | 523 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 524 | 524 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 525 | 525 | |
| 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), | |
| 527 | 527 | |
| 528 | 528 | .cmp_lt => try self.airCmp(inst, .lt), |
| 529 | 529 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | ... | @@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 678 | 678 | .div_float_optimized, |
| 679 | 679 | .div_trunc_optimized, |
| 680 | 680 | .div_floor_optimized, |
| 681 | .div_ceil_optimized, | |
| 681 | 682 | .div_exact_optimized, |
| 682 | 683 | .rem_optimized, |
| 683 | 684 | .mod_optimized, |
src/codegen/spirv/Assembler.zig+1-1| ... | ... | @@ -375,7 +375,7 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue { |
| 375 | 375 | }, |
| 376 | 376 | .string => |offset| { |
| 377 | 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 | 379 | try section.ensureUnusedCapacity(cg.gpa, size); |
| 380 | 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 | 232 | return switch (Operand) { |
| 233 | 233 | spec.LiteralSpecConstantOpInteger => unreachable, |
| 234 | 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 | 236 | spec.LiteralContextDependentNumber => switch (operand) { |
| 237 | 237 | .int32, .uint32, .float32 => 1, |
| 238 | 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 | 62 | .scalarize_div_trunc_optimized, |
| 63 | 63 | .scalarize_div_floor, |
| 64 | 64 | .scalarize_div_floor_optimized, |
| 65 | .scalarize_div_ceil, | |
| 66 | .scalarize_div_ceil_optimized, | |
| 65 | 67 | .scalarize_div_exact, |
| 66 | 68 | .scalarize_div_exact_optimized, |
| 67 | 69 | .scalarize_rem, |
| ... | ... | @@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1340 | 1342 | .div_exact, |
| 1341 | 1343 | .div_trunc, |
| 1342 | 1344 | .div_floor, |
| 1345 | .div_ceil, | |
| 1343 | 1346 | => |tag| { |
| 1344 | 1347 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1345 | 1348 | const lhs = try cg.resolveInst(bin_op.lhs); |
| ... | ... | @@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1366 | 1369 | .div_exact => try cg.floatDiv(float_ty, lhs, rhs), |
| 1367 | 1370 | .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs), |
| 1368 | 1371 | .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs), |
| 1372 | .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs), | |
| 1369 | 1373 | else => unreachable, |
| 1370 | 1374 | }; |
| 1371 | 1375 | |
| ... | ... | @@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1384 | 1388 | .div_exact => try cg.intDiv(int_ty, lhs, rhs), |
| 1385 | 1389 | .div_trunc => try cg.intDiv(int_ty, lhs, rhs), |
| 1386 | 1390 | .div_floor => try cg.intDivFloor(int_ty, lhs, rhs), |
| 1391 | .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs), | |
| 1387 | 1392 | else => unreachable, |
| 1388 | 1393 | }; |
| 1389 | 1394 | |
| ... | ... | @@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1881 | 1886 | .div_float_optimized, |
| 1882 | 1887 | .div_trunc_optimized, |
| 1883 | 1888 | .div_floor_optimized, |
| 1889 | .div_ceil_optimized, | |
| 1884 | 1890 | .div_exact_optimized, |
| 1885 | 1891 | .rem_optimized, |
| 1886 | 1892 | .mod_optimized, |
| ... | ... | @@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W |
| 2799 | 2805 | } |
| 2800 | 2806 | } |
| 2801 | 2807 | |
| 2808 | fn 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 | ||
| 2802 | 2899 | fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2803 | 2900 | switch (ty.bits) { |
| 2804 | 2901 | 0 => unreachable, |
| ... | ... | @@ -3581,7 +3678,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3581 | 3678 | |
| 3582 | 3679 | const result = try cg.allocInt(ty); |
| 3583 | 3680 | |
| 3584 | const used_len = (math.divCeil(u16, ty.bits, 64) catch unreachable) * 8; | |
| 3681 | const used_len = @divCeil(ty.bits, 64) * 8; | |
| 3585 | 3682 | |
| 3586 | 3683 | if (ty.bits % 64 != 0) { |
| 3587 | 3684 | try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 }); |
| ... | ... | @@ -3647,7 +3744,7 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3647 | 3744 | } else { |
| 3648 | 3745 | const result = try cg.allocInt(int_ty); |
| 3649 | 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; | |
| 3651 | 3748 | |
| 3652 | 3749 | try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF }); |
| 3653 | 3750 | |
| ... | ... | @@ -3681,7 +3778,7 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3681 | 3778 | } else { |
| 3682 | 3779 | const result = try cg.allocInt(int_ty); |
| 3683 | 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; | |
| 3685 | 3782 | |
| 3686 | 3783 | try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 }); |
| 3687 | 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 | 4362 | return cg.floatFloor(ty, div_result); |
| 4266 | 4363 | } |
| 4267 | 4364 | |
| 4365 | // div_ceil(a, b) = ceil(a / b) | |
| 4366 | fn 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 | 4371 | // mod(a, b) = fmod(fmod(a, b) + b, b) |
| 4269 | 4372 | fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 4270 | 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 | 70 | .expand_sub_safe, |
| 71 | 71 | .expand_mul_safe, |
| 72 | 72 | |
| 73 | .expand_div_ceil, | |
| 74 | .expand_div_ceil_optimized, | |
| 75 | ||
| 73 | 76 | .expand_packed_load, |
| 74 | 77 | .expand_packed_store, |
| 75 | 78 | .expand_packed_agg_field_val, |
| ... | ... | @@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 173873 | 173876 | for (ops) |op| try op.die(cg); |
| 173874 | 173877 | }, |
| 173875 | 173878 | |
| 173879 | .div_ceil, .div_ceil_optimized => unreachable, | |
| 173880 | ||
| 173876 | 173881 | // No soft-float `Legalize` features are enabled, so this instruction never appears. |
| 173877 | 173882 | .legalize_compiler_rt_call => unreachable, |
| 173878 | 173883 | |
| ... | ... | @@ -174781,7 +174786,7 @@ fn genShiftBinOpMir( |
| 174781 | 174786 | try self.spillEflagsIfOccupied(); |
| 174782 | 174787 | |
| 174783 | 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 | 174790 | assert(shift_abi_size >= 1 and shift_abi_size <= 2); |
| 174786 | 174791 | |
| 174787 | 174792 | const rcx_lock: ?RegisterLock = switch (rhs_mcv) { |
| ... | ... | @@ -179593,7 +179598,7 @@ fn genSetReg( |
| 179593 | 179598 | if (pack_alias != sign_alias) try cg.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, sign_alias); |
| 179594 | 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 | 179602 | break :pack_reg pack_reg; |
| 179598 | 179603 | }, |
| 179599 | 179604 | }; |
| ... | ... | @@ -180157,7 +180162,7 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void { |
| 180157 | 180162 | const bit_size = dst_ty.bitSize(zcu); |
| 180158 | 180163 | if (abi_size * 8 <= bit_size) break :result dst_mcv; |
| 180159 | 180164 | |
| 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 | 180166 | const high_mcv: MCValue = switch (dst_mcv) { |
| 180162 | 180167 | .register => |dst_reg| .{ .register = dst_reg }, |
| 180163 | 180168 | .register_pair => |dst_regs| .{ .register = dst_regs[1] }, |
| ... | ... | @@ -183550,7 +183555,7 @@ const Temp = struct { |
| 183550 | 183555 | const part_ty: Type = if (src_regs.len == 1) |
| 183551 | 183556 | src_ty |
| 183552 | 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 | 183559 | break :part_ty .u64; |
| 183555 | 183560 | } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) { |
| 183556 | 183561 | else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }), |
| ... | ... | @@ -183560,7 +183565,7 @@ const Temp = struct { |
| 183560 | 183565 | break :part_ty .usize; |
| 183561 | 183566 | }, |
| 183562 | 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 | 183569 | break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_size, 8)); |
| 183565 | 183570 | }, |
| 183566 | 183571 | .vector_type => |vector_type| switch (@divExact(vector_type.len, src_regs.len)) { |
| ... | ... | @@ -183580,7 +183585,7 @@ const Temp = struct { |
| 183580 | 183585 | }, |
| 183581 | 183586 | }, |
| 183582 | 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 | 183589 | break :part_ty switch (src_size) { |
| 183585 | 183590 | 0, 3, 5...7 => unreachable, |
| 183586 | 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 | 772 | const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) { |
| 773 | 773 | .rip_inst => 4, |
| 774 | 774 | else => unreachable, |
| 775 | } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable); | |
| 775 | } else @intCast(@divCeil(op.immBitSize(), 8)); | |
| 776 | 776 | reloc_offset -= enc_length; |
| 777 | 777 | if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length }; |
| 778 | 778 | assert(!is_mem); |
src/link/Dwarf.zig+3-3| ... | ... | @@ -2137,7 +2137,7 @@ pub const WipNav = struct { |
| 2137 | 2137 | .signed => DW.FORM.sdata, |
| 2138 | 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 | 2141 | var bit: usize = 0; |
| 2142 | 2142 | var carry: u1 = 1; |
| 2143 | 2143 | while (bit < bits) { |
| ... | ... | @@ -2158,7 +2158,7 @@ pub const WipNav = struct { |
| 2158 | 2158 | } |
| 2159 | 2159 | } else { |
| 2160 | 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 | 2162 | try diw.writeUleb128(bytes); |
| 2163 | 2163 | try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes)); |
| 2164 | 2164 | big_int.writeTwosComplement( |
| ... | ... | @@ -4275,7 +4275,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co |
| 4275 | 4275 | try wip_nav.abbrevCode(.aggregate_undefined_comptime_value); |
| 4276 | 4276 | try wip_nav.refType(.fromInterned(error_union.ty)); |
| 4277 | 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 | 4279 | dwarf.writeInt(err_bytes, switch (error_union.val) { |
| 4280 | 4280 | .err_name => |err_name| try pt.getErrorValue(err_name), |
| 4281 | 4281 | .payload => 0, |
src/main.zig+1-2| ... | ... | @@ -162,8 +162,7 @@ const use_safe_allocator = build_options.debug_gpa or |
| 162 | 162 | .ReleaseFast, .ReleaseSmall => false, |
| 163 | 163 | }); |
| 164 | 164 | |
| 165 | // TODO: The `align(@alignOf(std.heap.SafeAllocator))` can be removed the next time zig1.wasm is updated | |
| 166 | var safe_allocator: std.heap.SafeAllocator align(@alignOf(std.heap.SafeAllocator)) = .init(std.heap.page_allocator, .{ | |
| 165 | var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{ | |
| 167 | 166 | .stack_trace_frames = build_options.mem_leak_frames, |
| 168 | 167 | }); |
| 169 | 168 |
src/print_zir.zig+1| ... | ... | @@ -392,6 +392,7 @@ const Writer = struct { |
| 392 | 392 | .truncate, |
| 393 | 393 | .div_exact, |
| 394 | 394 | .div_floor, |
| 395 | .div_ceil, | |
| 395 | 396 | .div_trunc, |
| 396 | 397 | .mod, |
| 397 | 398 | .rem, |
stage1/zig.h+32| ... | ... | @@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t; |
| 813 | 813 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 814 | 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 | 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 | 2067 | return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask)); |
| 2059 | 2068 | } |
| 2060 | 2069 | |
| 2070 | static 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 | ||
| 2077 | static 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 | 2085 | #define zig_mod_u128 zig_rem_u128 |
| 2062 | 2086 | |
| 2063 | 2087 | static 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 | 3275 | zig_trap(); |
| 3252 | 3276 | } |
| 3253 | 3277 | |
| 3278 | static 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 | ||
| 3254 | 3282 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); |
| 3255 | 3283 | static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { |
| 3256 | 3284 | if (!is_signed) { |
| ... | ... | @@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) |
| 4010 | 4038 | static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4011 | 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 | 4046 | static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4015 | 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 | 59 | const a: i128 = -170141183460469231731687303715884105728; |
| 60 | 60 | const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000; |
| 61 | 61 | try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885); |
| 62 | try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884); | |
| 62 | 63 | try expect(a == b); |
| 63 | 64 | } |
| 64 | 65 |
test/behavior/math.zig+89| ... | ... | @@ -488,6 +488,36 @@ fn testIntDivision() !void { |
| 488 | 488 | try expect(divFloor(i64, -0x80000000, -2) == 0x40000000); |
| 489 | 489 | try expect(divFloor(i64, -0x40000001, 0x40000000) == -2); |
| 490 | 490 | |
| 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 | 521 | try expect(divTrunc(i32, 5, 3) == 1); |
| 492 | 522 | try expect(divTrunc(i32, -5, 3) == -1); |
| 493 | 523 | try expect(divTrunc(i32, 9, -10) == 0); |
| ... | ... | @@ -531,6 +561,24 @@ fn testIntDivision() !void { |
| 531 | 561 | try expect( |
| 532 | 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 | 582 | try expect( |
| 535 | 583 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, |
| 536 | 584 | ); |
| ... | ... | @@ -559,6 +607,13 @@ fn testFloatDivision() !void { |
| 559 | 607 | try expect(divFloor(f16, -43.0, 12.0) == -4.0); |
| 560 | 608 | try expect(divFloor(f64, -90.0, -9.0) == 10.0); |
| 561 | 609 | |
| 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 | 617 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); |
| 563 | 618 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 564 | 619 | try expect(divTrunc(f32, 9.0, -10.0) == 0.0); |
| ... | ... | @@ -607,6 +662,8 @@ fn testDivisionFP16() !void { |
| 607 | 662 | |
| 608 | 663 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); |
| 609 | 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 | 667 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); |
| 611 | 668 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); |
| 612 | 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 | 679 | fn divFloor(comptime T: type, a: T, b: T) T { |
| 623 | 680 | return @divFloor(a, b); |
| 624 | 681 | } |
| 682 | fn divCeil(comptime T: type, a: T, b: T) T { | |
| 683 | return @divCeil(a, b); | |
| 684 | } | |
| 625 | 685 | fn divTrunc(comptime T: type, a: T, b: T) T { |
| 626 | 686 | return @divTrunc(a, b); |
| 627 | 687 | } |
| ... | ... | @@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" { |
| 1846 | 1906 | try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1); |
| 1847 | 1907 | } |
| 1848 | 1908 | |
| 1909 | fn testDivCeil(comptime T: type, numerator: T, denominator: T, expected: T) !void { | |
| 1910 | try expect(@divCeil(numerator, denominator) == expected); | |
| 1911 | } | |
| 1912 | ||
| 1913 | test "@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 | ||
| 1849 | 1938 | fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void { |
| 1850 | 1939 | try expect(@divTrunc(numerator, denominator) == expected); |
| 1851 | 1940 | } |
test/behavior/vector.zig+35-1| ... | ... | @@ -510,8 +510,37 @@ test "vector division operators" { |
| 510 | 510 | inline for (@as([4]T, d2), 0..) |v, i| { |
| 511 | 511 | try expect(@divFloor(x[i], y[i]) == v); |
| 512 | 512 | } |
| 513 | const d3 = @divTrunc(x, y); | |
| 513 | const d3 = @divCeil(x, y); | |
| 514 | 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 | 544 | try expect(@divTrunc(x[i], y[i]) == v); |
| 516 | 545 | } |
| 517 | 546 | } |
| ... | ... | @@ -566,6 +595,9 @@ test "vector division operators" { |
| 566 | 595 | try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); |
| 567 | 596 | try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); |
| 568 | 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 | }; |
| 571 | 603 | |
| ... | ... | @@ -1318,11 +1350,13 @@ test "zero divisor" { |
| 1318 | 1350 | const v2 = @divExact(zeros, ones); |
| 1319 | 1351 | const v3 = @divTrunc(zeros, ones); |
| 1320 | 1352 | const v4 = @divFloor(zeros, ones); |
| 1353 | const v5 = @divCeil(zeros, ones); | |
| 1321 | 1354 | |
| 1322 | 1355 | _ = v1[0]; |
| 1323 | 1356 | _ = v2[0]; |
| 1324 | 1357 | _ = v3[0]; |
| 1325 | 1358 | _ = v4[0]; |
| 1359 | _ = v5[0]; | |
| 1326 | 1360 | } |
| 1327 | 1361 | |
| 1328 | 1362 | test "zero multiplicand" { |
test/behavior/x86_64/binary.zig+21| ... | ... | @@ -5279,6 +5279,27 @@ test divFloorOptimized { |
| 5279 | 5279 | try test_div_floor_optimized.testFloatVectors(); |
| 5280 | 5280 | } |
| 5281 | 5281 | |
| 5282 | inline fn divCeilUnoptimized(comptime Type: type, lhs: Type, rhs: Type) Type { | |
| 5283 | return @divCeil(lhs, rhs); | |
| 5284 | } | |
| 5285 | test 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 | ||
| 5293 | inline 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 | } | |
| 5297 | test 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 | ||
| 5282 | 5303 | inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5283 | 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 | |
| 5 | 5 | // error |
| 6 | 6 | // |
| 7 | // :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact | |
| 7 | // :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact |