diff --git a/doc/langref.html.in b/doc/langref.html.in
index d9312249b74737e7b985c38696ffcbee14661878..f24f03c981f5d78513468aa31c3983b06ae2670d 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}
Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.
Signed integer operands must be comptime-known and positive. In other cases, use
{#link|@divTrunc#},
- {#link|@divFloor#}, or
+ {#link|@divFloor#},
+ {#link|@divCeil#}, or
{#link|@divExact#} instead.
Invokes {#link|Peer Type Resolution#} for the operands.
@@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@divExact(a, b) * b == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.
- {#see_also|@divTrunc|@divFloor#}
+ {#see_also|@divTrunc|@divFloor|@divCeil#}
{#header_close#}
{#header_open|@divFloor#}
{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}
@@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.
- {#see_also|@divTrunc|@divExact#}
+ {#see_also|@divTrunc|@divCeil|@divExact#}
{#header_close#}
{#header_open|@divTrunc#}
{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}
@@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.
- {#see_also|@divFloor|@divExact#}
+ {#see_also|@divFloor|@divCeil|@divExact#}
+ {#header_close#}
+ {#header_open|@divCeil#}
+ {#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}
+
+ Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and
+ {#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}.
+
+
+ - {#syntax#}@divCeil(5, 3) == 2{#endsyntax#}
+ - {#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}
+
+ For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.
+ {#see_also|@divFloor|@divTrunc|@divExact#}
{#header_close#}
{#header_open|@embedFile#}
@@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}/{#endsyntax#} (division)
{#link|@divTrunc#} (division)
{#link|@divFloor#} (division)
+ {#link|@divCeil#} (division)
{#link|@divExact#} (division)
Example with addition at compile-time:
@@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@import("std").math.mul{#endsyntax#}
{#syntax#}@import("std").math.divTrunc{#endsyntax#}
{#syntax#}@import("std").math.divFloor{#endsyntax#}
+ {#syntax#}@import("std").math.divCeil{#endsyntax#}
{#syntax#}@import("std").math.divExact{#endsyntax#}
{#syntax#}@import("std").math.shl{#endsyntax#}
diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig
index f77c367f6103724c7990a05c0e3be94a38ecf736..54527c805c35cfc4bdfbeadf27acee68fce8d691 100644
--- a/lib/compiler/Maker/WebServer.zig
+++ b/lib/compiler/Maker/WebServer.zig
@@ -163,7 +163,7 @@ pub fn updateConfiguration(ws: *WebServer, maker: *Maker) !void {
assert(idx == step_names_trailing.len);
}
- const step_status_bits = try gpa.alloc(u8, std.math.divCeil(usize, all_steps.len, 4) catch unreachable);
+ const step_status_bits = try gpa.alloc(u8, @divCeil(all_steps.len, 4));
errdefer gpa.free(step_status_bits);
@memset(step_status_bits, 0);
diff --git a/lib/compiler_rt/limb64.zig b/lib/compiler_rt/limb64.zig
index 56e4bfc817373e3ba1e90c3525f83ee6a99fd995..bfe4c441f0525b2e7f5e858e3fa98f81a2e9c865 100644
--- a/lib/compiler_rt/limb64.zig
+++ b/lib/compiler_rt/limb64.zig
@@ -3,7 +3,6 @@ const testing = std.testing;
const assert = std.debug.assert;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
-const divCeil = std.math.divCeil;
const builtin = @import("builtin");
const compiler_rt = @import("../compiler_rt.zig");
@@ -26,7 +25,7 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {
}
fn usedLimbCount(bits: u16) u16 {
- return divCeil(u16, bits, 64) catch unreachable;
+ return @divCeil(bits, 64);
}
fn limbCount(bits: u16) u16 {
diff --git a/lib/compiler_rt/udivmodei4.zig b/lib/compiler_rt/udivmodei4.zig
index 5aa8421bdb920c33c7d0f3a10c32ee9db6c21da0..41ba32cdbef1532de8b77e0f91db4bbf4b39f29a 100644
--- a/lib/compiler_rt/udivmodei4.zig
+++ b/lib/compiler_rt/udivmodei4.zig
@@ -8,7 +8,7 @@ const shl = std.math.shl;
const compiler_rt = @import("../compiler_rt.zig");
const symbol = @import("../compiler_rt.zig").symbol;
-const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max supported type is u65535
+const max_limbs = @divCeil(65535, 32); // max supported type is u65535
comptime {
symbol(&__udivei4, "__udivei4");
diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig
index acb23b66c58777bc2a6738081e23fcb52ee3c1d9..d4353db874490765967e47e90aaa923c871f2011 100644
--- a/lib/fuzzer.zig
+++ b/lib/fuzzer.zig
@@ -52,7 +52,7 @@ var fuzzer: Fuzzer = undefined;
var current_test_name: ?[]const u8 = null;
fn bitsetUsizes(elems: usize) usize {
- return math.divCeil(usize, elems, @bitSizeOf(usize)) catch unreachable;
+ return @divCeil(elems, @bitSizeOf(usize));
}
const Executable = struct {
diff --git a/lib/std/Random.zig b/lib/std/Random.zig
index 7593e9680f9f714d9853275c115acfdbe95d13fb..4ab514844eb2e4ecf485f33beb2085853732a4ff 100644
--- a/lib/std/Random.zig
+++ b/lib/std/Random.zig
@@ -126,7 +126,7 @@ pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: ty
pub fn int(r: Random, comptime T: type) T {
const bits = @typeInfo(T).int.bits;
const UnsignedT = @Int(.unsigned, bits);
- const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable;
+ const ceil_bytes = @divCeil(bits, 8);
const ByteAlignedT = @Int(.unsigned, ceil_bytes * 8);
var rand_bytes: [ceil_bytes]u8 = undefined;
diff --git a/lib/std/Target.zig b/lib/std/Target.zig
index 387f96d04b75c647832e946bfe1d2cacc5c78e15..103d8842de5a2dde6b7e6c9bea0a1f6e47bd555b 100644
--- a/lib/std/Target.zig
+++ b/lib/std/Target.zig
@@ -1232,7 +1232,7 @@ pub const Cpu = struct {
ints: [usize_count]usize,
pub const needed_bit_count = 347;
- pub const byte_count = (needed_bit_count + 7) / 8;
+ pub const byte_count = @divCeil(needed_bit_count, 8);
pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);
pub const Index = std.math.Log2Int(@Int(.unsigned, usize_count * @bitSizeOf(usize)));
pub const ShiftInt = std.math.Log2Int(usize);
diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig
index 2bc56b8c8ee05c91ef432894030a2c36a84292f9..d4e3e3168571ea55438b295c5be787a7f8e54e0b 100644
--- a/lib/std/crypto/aes_gcm.zig
+++ b/lib/std/crypto/aes_gcm.zig
@@ -39,7 +39,7 @@ fn AesGcm(comptime Aes: anytype) type {
mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
aes.encrypt(&t, &j);
- const block_count = (math.divCeil(usize, ad.len, Ghash.block_length) catch unreachable) + (math.divCeil(usize, c.len, Ghash.block_length) catch unreachable) + 1;
+ const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
var mac = Ghash.initForBlockCount(&h, block_count);
mac.update(ad);
mac.pad();
@@ -81,7 +81,7 @@ fn AesGcm(comptime Aes: anytype) type {
mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
aes.encrypt(&t, &j);
- const block_count = (math.divCeil(usize, ad.len, Ghash.block_length) catch unreachable) + (math.divCeil(usize, c.len, Ghash.block_length) catch unreachable) + 1;
+ const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
var mac = Ghash.initForBlockCount(&h, block_count);
mac.update(ad);
mac.pad();
diff --git a/lib/std/crypto/ascon.zig b/lib/std/crypto/ascon.zig
index 3142bc0a8972c394be1e927361f8ad162780b040..9d2ac52e83dc500062c93aa4af56cd9b50f11f03 100644
--- a/lib/std/crypto/ascon.zig
+++ b/lib/std/crypto/ascon.zig
@@ -198,7 +198,7 @@ pub fn State(comptime endian: std.builtin.Endian) type {
///
/// Note: Clears complete words that contain the specified byte range
pub fn clear(self: *Self, from: usize, to: usize) void {
- @memset(self.st[from / 8 .. (to + 7) / 8], 0);
+ @memset(self.st[from / 8 .. @divCeil(to, 8)], 0);
}
/// Clear the entire state, disabling compiler optimizations.
diff --git a/lib/std/crypto/ff.zig b/lib/std/crypto/ff.zig
index 796a11e435eed7e956311232b523bf49bf301eb0..bf9584dfa3ad033652130705937972171370669c 100644
--- a/lib/std/crypto/ff.zig
+++ b/lib/std/crypto/ff.zig
@@ -61,14 +61,14 @@ pub fn Uint(comptime max_bits: comptime_int) type {
return struct {
const Self = @This();
- const max_limbs_count = math.divCeil(usize, max_bits, t_bits) catch unreachable;
+ const max_limbs_count = @divCeil(max_bits, t_bits);
limbs_buffer: [max_limbs_count]Limb,
/// The number of active limbs.
limbs_len: usize,
/// Number of bytes required to serialize an integer.
- pub const encoded_bytes = math.divCeil(usize, max_bits, 8) catch unreachable;
+ pub const encoded_bytes = @divCeil(max_bits, 8);
/// Constant slice of active limbs.
fn limbsConst(self: *const Self) []const Limb {
@@ -847,7 +847,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
}
var e_normalized = Fe{ .v = e.v.normalize() };
var buf_: [Fe.encoded_bytes]u8 = undefined;
- var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_len * t_bits, 8) catch unreachable];
+ var buf = buf_[0..@divCeil(e_normalized.v.limbs_len * t_bits, 8)];
e_normalized.toBytes(buf, .little) catch unreachable;
const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]);
buf = buf[0 .. buf.len - leading / 8];
diff --git a/lib/std/crypto/pbkdf2.zig b/lib/std/crypto/pbkdf2.zig
index 88b6cf690dc40107e615ac253bebf5bfa2b25bb4..5e7f45087f3c8fd703700cf4fba2fa7667c30787 100644
--- a/lib/std/crypto/pbkdf2.zig
+++ b/lib/std/crypto/pbkdf2.zig
@@ -74,7 +74,7 @@ pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, com
// block
//
- const blocks_count = @as(u32, @intCast(std.math.divCeil(usize, dk_len, h_len) catch unreachable));
+ const blocks_count: u32 = @intCast(@divCeil(dk_len, h_len));
var r = dk_len % h_len;
if (r == 0) {
r = h_len;
diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig
index 78e0bbd7858ff55dbfb62a9cb4bcdb3c58f1b2e8..3c5773af50fc827bc34cd10af4d90185d86f27d7 100644
--- a/lib/std/crypto/sha2.zig
+++ b/lib/std/crypto/sha2.zig
@@ -474,7 +474,7 @@ fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {
return struct {
const Self = @This();
pub const block_length = 128;
- pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable;
+ pub const digest_length = @divCeil(digest_bits, 8);
pub const Options = struct {};
s: Iv64,
diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig
index d5e62904703c71ae685fb4b22b2062fe697179ce..6fe661981ab71fa49906f5998400aa5e1250d48c 100644
--- a/lib/std/crypto/sha3.zig
+++ b/lib/std/crypto/sha3.zig
@@ -58,7 +58,7 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim
st: State,
/// The output length, in bytes.
- pub const digest_length = std.math.divCeil(comptime_int, output_bits, 8) catch unreachable;
+ pub const digest_length: comptime_int = @divCeil(output_bits, 8);
/// The block length, or rate, in bytes.
pub const block_length = State.rate;
/// The delimiter can be overwritten in the options.
@@ -464,7 +464,7 @@ pub const NistLengthEncoding = enum {
/// Encode a length according to NIST SP 800-185.
pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length {
const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3;
- const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable;
+ const len_bytes = @divCeil(len_bits, 8);
var res = Length{ .len = len_bytes + 1 };
if (encoding == .right) {
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index 819360ec28f296518e7eddc06d6f5f7a97c4c97a..db6060d7e8bc620c9c05b88bead578c2b09f548a 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -349,7 +349,7 @@ pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void {
var chunks = mem.window(u8, bytes, 16, 16);
while (chunks.next()) |window| {
// 1. Print the address.
- const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10;
+ const address = (@intFromPtr(bytes.ptr) + 0x10 * @divCeil(chunks.index orelse bytes.len, 16) - 0x10);
try t.setColor(.dim);
// We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
// Also, make sure all lines are aligned by padding the address.
diff --git a/lib/std/enums.zig b/lib/std/enums.zig
index 10b85ddeeb570510f3b6c93ada06f9bfdcdaa07f..47e0e4d0bb9266e5cd3d6a5ce5409b276e28527a 100644
--- a/lib/std/enums.zig
+++ b/lib/std/enums.zig
@@ -1391,7 +1391,7 @@ test "EnumIndexer non-exhaustive" {
const max_index: comptime_int = std.math.maxInt(RangedType);
const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) {
.unsigned => 0,
- .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable,
+ .signed => @divCeil(max_index, 2),
};
try testing.expectEqual(E, Indexer.Key);
diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig
index 2562c5157355aa312b3fdd5a89e652a131035d61..31f565ea93f9440316e11f2656a3d38e0f14475d 100644
--- a/lib/std/hash/auto_hash.zig
+++ b/lib/std/hash/auto_hash.zig
@@ -99,7 +99,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
} else {
// Take only the part containing the key value, the remaining
// bytes are undefined and must not be hashed!
- const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Key), 8) catch unreachable;
+ const byte_size = @divCeil(@bitSizeOf(Key), 8);
@call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key)[0..byte_size] });
}
},
diff --git a/lib/std/math.zig b/lib/std/math.zig
index ec4f47edc1f0ff887ed728ffb636d363e66dfaad..90ff8a4abdede9f325b77ca143996840f14a3060 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -922,21 +922,10 @@ fn testDivFloor() !void {
pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0) return error.DivisionByZero;
- const info = @typeInfo(T);
- switch (info) {
- .comptime_float, .float => return @ceil(numerator / denominator),
- .comptime_int, .int => {
- if (numerator < 0 and denominator < 0) {
- if (info == .int and numerator == minInt(T) and denominator == -1)
- return error.Overflow;
- return @divFloor(numerator + 1, denominator) + 1;
- }
- if (numerator > 0 and denominator > 0)
- return @divFloor(numerator - 1, denominator) + 1;
- return @divTrunc(numerator, denominator);
- },
- else => @compileError("divCeil unsupported on " ++ @typeName(T)),
+ if (@typeInfo(T) == .int and numerator == minInt(T) and denominator == -1) {
+ return error.Overflow;
}
+ return @divCeil(numerator, denominator);
}
test divCeil {
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
index 75a1eb4c70552e0b7cc3eaf4ef324570df1a1387..5f33b91b52a95b0e699b5db8e247c9d1390083d1 100644
--- a/lib/std/math/big/int.zig
+++ b/lib/std/math/big/int.zig
@@ -122,7 +122,7 @@ pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize {
/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero
/// and this big integer implementation stores zero using one limb.
pub fn calcTwosCompLimbCount(bit_count: usize) usize {
- return @max(std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable, 1);
+ return @max(@divCeil(bit_count, @bitSizeOf(Limb)), 1);
}
/// a + b * c + *carry, sets carry to the overflow bits
@@ -1221,6 +1221,62 @@ pub const Mutable = struct {
}
}
+ /// q = a / b (rem r)
+ ///
+ /// a / b are ceiled (rounded towards +inf).
+ /// q may alias with a or b.
+ ///
+ /// Asserts there is enough memory to store q and r.
+ /// The upper bound for r limb count is `b.limbs.len`.
+ /// The upper bound for q limb count is given by `a.limbs`.
+ ///
+ /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
+ pub fn divCeil(
+ q: *Mutable,
+ r: *Mutable,
+ a: Const,
+ b: Const,
+ limbs_buffer: []Limb,
+ ) void {
+ const sep = a.limbs.len + 2;
+ var x = a.toMutable(limbs_buffer[0..sep]);
+ var y = b.toMutable(limbs_buffer[sep..]);
+
+ // div performs truncating division (@divTrunc) which rounds towards negative
+ // infinity if the result is positive and towards positive infinity if the result is
+ // negative.
+ div(q, r, &x, &y);
+
+ // @rem gives the remainder after @divTrunc, and is defined by:
+ // x * @divTrunc(x, y) + @rem(x, y) = x
+ // For all integers x, y with y != 0.
+ // In the following comments, a, b will be integers with a >= 0, b > 0, and we will take
+ // modCeil to be the remainder after @divCeil, defined by:
+ // x * @divCeil(x, y) + modCeil(x, y) = x
+ // For all integers x, y with y != 0.
+
+ if (a.positive != b.positive or r.eqlZero()) {
+ // In this case either the result is negative or the remainder is 0.
+ // If the result is negative then the default truncating division already rounds
+ // towards positive infinity, so no adjustment is needed.
+ // If the remainder is 0 then the division is exact and no adjustment is needed.
+ } else {
+ // Same sign.
+ // We have:
+ // modCeil(a, b) != 0
+ // => @divCeil(a, b) = @divTrunc(a, b) + 1
+ // And:
+ // b * @divTrunc(a, b) + @rem(a, b) = a
+ // b * @divCeil(a, b) + modCeil(a, b) = a
+ // => b * @divTrunc(a, b) + b + modCeil(a, b) = a
+ // => modCeil(a, b) = @rem(a, b) - b
+ //
+ // This works for both positive and negative b because b keeps its sign.
+ q.addScalar(q.toConst(), 1);
+ r.sub(r.toConst(), y.toConst());
+ }
+ }
+
/// q = a / b (rem r)
///
/// a / b are truncated (rounded towards -inf).
@@ -3314,6 +3370,25 @@ pub const Managed = struct {
r.setMetadata(mr.positive, mr.len);
}
+ /// q = a / b (rem r)
+ ///
+ /// a / b are ceiled (rounded towards positive infinity).
+ ///
+ /// Returns an error if memory could not be allocated.
+ pub fn divCeil(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void {
+ const q_alias = limbsAliasDistinct(q, a) or limbsAliasDistinct(q, b);
+ const r_alias = limbsAliasDistinct(r, a) or limbsAliasDistinct(r, b);
+ try q.ensureAliasAwareCapacity(a.len(), q_alias);
+ try r.ensureAliasAwareCapacity(b.len(), r_alias);
+ var mq = q.toMutable();
+ var mr = r.toMutable();
+ const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.len(), b.len()));
+ defer q.allocator.free(limbs_buffer);
+ mq.divCeil(&mr, a.toConst(), b.toConst(), limbs_buffer);
+ q.setMetadata(mq.positive, mq.len);
+ r.setMetadata(mr.positive, mr.len);
+ }
+
/// q = a / b (rem r)
///
/// a / b are truncated (rounded towards -inf).
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
index 0bc1acd87670ae03cd3d2d957dc6e696dde31a19..485ae4918cce7545119b5b014f7c8133656aaf0b 100644
--- a/lib/std/math/big/int_test.zig
+++ b/lib/std/math/big/int_test.zig
@@ -2127,6 +2127,44 @@ test "div floor positive close to zero" {
try testing.expectEqual(10, try r.toInt(i32));
}
+fn testDivCeil(comptime T: type, u: T, v: T, eq: T, er: T) !void {
+ var a = try Managed.initSet(testing.allocator, u);
+ defer a.deinit();
+ var b = try Managed.initSet(testing.allocator, v);
+ defer b.deinit();
+
+ var q = try Managed.init(testing.allocator);
+ defer q.deinit();
+ var r = try Managed.init(testing.allocator);
+ defer r.deinit();
+
+ try Managed.divCeil(&q, &r, &a, &b);
+
+ try testing.expectEqual(eq, try q.toInt(T));
+ try testing.expectEqual(er, try r.toInt(T));
+}
+
+test "div ceil small" {
+ try testDivCeil(i32, 5, 3, 2, -1);
+ try testDivCeil(i32, -5, 3, -1, -2);
+ try testDivCeil(i32, 5, -3, -1, 2);
+ try testDivCeil(i32, -5, -3, 2, 1);
+ try testDivCeil(i32, -0x80000000, 1, -0x80000000, 0);
+}
+
+test "div ceil multi-limb" {
+ {
+ const a = (@as(i128, 1) << 100) + 3;
+ const b: i128 = 4;
+ try testDivCeil(i128, a, b, (1 << 98) + 1, -1);
+ }
+ {
+ const a = -((@as(i128, 1) << 100) + 3);
+ const b: i128 = 4;
+ try testDivCeil(i128, a, b, -(1 << 98), -3);
+ }
+}
+
test "div multi-multi with rem" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
index 2e0f3cb0ccee44adabf55ff740bd247ad762a5a6..d2c98e4c58e11a803db66a3da86d46dfb7e40bee 100644
--- a/lib/std/mem.zig
+++ b/lib/std/mem.zig
@@ -1937,7 +1937,7 @@ fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T
const bit_count = @as(usize, @bitSizeOf(T));
const bit_shift = @as(u3, @intCast(bit_offset % 8));
- const load_size = (bit_count + 7) / 8;
+ const load_size = @divCeil(bit_count, 8);
const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
const LoadInt = @Int(.unsigned, load_size * 8);
@@ -1964,9 +1964,9 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T {
const bit_count = @as(usize, @bitSizeOf(T));
const bit_shift = @as(u3, @intCast(bit_offset % 8));
- const byte_count = (@as(usize, bit_shift) + bit_count + 7) / 8;
+ const byte_count = @divCeil(@as(usize, bit_shift) + bit_count, 8);
- const load_size = (bit_count + 7) / 8;
+ const load_size = @divCeil(bit_count, 8);
const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
const LoadInt = @Int(.unsigned, load_size * 8);
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
index 4adf7d0232804afe4738add9bf7a5307e2f1cdd3..46aa2fea1231792ad4c8ec64c6f167c9340a0fdd 100644
--- a/lib/std/zig/AstGen.zig
+++ b/lib/std/zig/AstGen.zig
@@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.bit_reverse,
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
@@ -4895,7 +4896,7 @@ fn structDeclInner(
const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len);
const field_comptime_bits = try scratch.addOptionalSlice(
scan_result.any_comptime_fields,
- std.math.divCeil(u32, scan_result.fields_len, 32) catch unreachable,
+ @divCeil(scan_result.fields_len, 32),
);
if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0);
@@ -9392,6 +9393,7 @@ fn builtinCall(
.div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
.div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
+ .div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil),
.div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
.mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
.rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig
index a9b680c39b2a9e01e967ab33f3b03fe43999392a..c42528e3ee2cf8d91509e86fd4a444994027be81 100644
--- a/lib/std/zig/AstRlAnnotate.zig
+++ b/lib/std/zig/AstRlAnnotate.zig
@@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
},
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig
index 4464d1fa46c93a1170238627dc5fd0b03c32048e..7ff834487ce43f5700e96de0f58b5f12664343ac 100644
--- a/lib/std/zig/BuiltinFn.zig
+++ b/lib/std/zig/BuiltinFn.zig
@@ -31,6 +31,7 @@ pub const Tag = enum {
c_va_copy,
c_va_end,
c_va_start,
+ div_ceil,
div_exact,
div_floor,
div_trunc,
@@ -398,6 +399,13 @@ pub const list = list: {
.param_count = 2,
},
},
+ .{
+ "@divCeil",
+ .{
+ .tag = .div_ceil,
+ .param_count = 2,
+ },
+ },
.{
"@divTrunc",
.{
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
index 49897a755f8a08cedc86d421250a887afe03a720..6e14e5ee9096b1e6ef9f190d6cb5dfd7e23de5a0 100644
--- a/lib/std/zig/Zir.zig
+++ b/lib/std/zig/Zir.zig
@@ -200,6 +200,9 @@ pub const Inst = struct {
/// Implements the `@divFloor` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_floor,
+ /// Implements the `@divCeil` builtin.
+ /// Uses the `pl_node` union field with payload `Bin`.
+ div_ceil,
/// Implements the `@divTrunc` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_trunc,
@@ -1267,6 +1270,7 @@ pub const Inst = struct {
.bit_reverse,
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
@@ -1547,6 +1551,7 @@ pub const Inst = struct {
.bit_reverse,
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
@@ -1815,6 +1820,7 @@ pub const Inst = struct {
.div_exact = .pl_node,
.div_floor = .pl_node,
+ .div_ceil = .pl_node,
.div_trunc = .pl_node,
.mod = .pl_node,
.rem = .pl_node,
@@ -4115,6 +4121,7 @@ fn findTrackableInner(
.mul_sat,
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
@@ -5272,7 +5279,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe
break :lens @ptrCast(lens);
} else null;
const field_comptime_bits: ?[]const u32 = if (small.any_comptime_fields) bits: {
- const bits_len = std.math.divCeil(u32, fields_len, 32) catch unreachable;
+ const bits_len = @divCeil(fields_len, 32);
const bits = zir.extra[extra_index..][0..bits_len];
extra_index += bits_len;
break :bits bits;
diff --git a/lib/zig.h b/lib/zig.h
index db94ac20445b942586741451f4e02bc7c579aae3..fbc924ca334e99eb12d2f37e3ebffa970ced7b9d 100644
--- a/lib/zig.h
+++ b/lib/zig.h
@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
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)); \
} \
+\
+ static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
+ return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
+ } \
+\
+ static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
+ return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
+ ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
+ } \
\
zig_basic_operator(uint##w##_t, mod_u##w, %) \
\
@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
}
+static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
+ zig_u128 rem = zig_rem_u128(lhs, rhs);
+ uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
+ ? UINT64_C(1) : UINT64_C(0);
+ return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
+}
+
+static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
+ zig_i128 rem = zig_rem_i128(lhs, rhs);
+ int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
+ ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
+ : INT64_C(0);
+ return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
+}
+
#define zig_mod_u128 zig_rem_u128
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
zig_trap();
}
+static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
+ zig_trap();
+}
+
zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
if (!is_signed) {
@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
} \
+\
+ static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
+ return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
+ } \
\
static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
diff --git a/src/Air.zig b/src/Air.zig
index 1c14a7c138cca6d445b8228ebb30c33100ded5aa..cdae23d2f5cf1b4ff470ee5f4bb0584b07e26a52 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -143,6 +143,13 @@ pub const Inst = struct {
div_floor,
/// Same as `div_floor` with optimized float mode.
div_floor_optimized,
+ /// Ceiling integer or float division. For integers, wrapping is illegal behavior.
+ /// Both operands are guaranteed to be the same type, and the result type
+ /// is the same as both operands.
+ /// Uses the `bin_op` field.
+ div_ceil,
+ /// Same as `div_ceil` with optimized float mode.
+ div_ceil_optimized,
/// Integer or float division.
/// If a remainder would be produced, illegal behavior occurs.
/// For integers, overflow is illegal behavior.
@@ -1510,7 +1517,7 @@ pub const ShuffleTwoMask = enum(u32) {
/// Trailing:
/// 0. `Inst.Ref` for every outputs_len
/// 1. `Inst.Ref` for every inputs_len
-/// 2. A number of u32 elements follow according to the equation `(source_len + 3) / 4`.
+/// 2. A number of u32 elements follow according to the equation `@divCeil(source_len, 4)`.
/// Memory starting at this position is reinterpreted as the source bytes.
/// 3. for every outputs_len
/// - 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)
.div_float,
.div_trunc,
.div_floor,
+ .div_ceil,
.div_exact,
.rem,
.mod,
@@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
+ .div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
@@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
+ .div_ceil,
+ .div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
@@ -2214,7 +2225,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
}
const pl_op = inst.data.pl_op;
const extra = air.extraData(SwitchBr, pl_op.payload);
- const hint_bag_count = std.math.divCeil(usize, extra.data.cases_len + 1, 10) catch unreachable;
+ const hint_bag_count = @divCeil(extra.data.cases_len + 1, 10);
return .{
.air = air,
.operand = pl_op.operand,
@@ -2383,7 +2394,7 @@ pub const UnwrappedAsm = struct {
const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
- const next_offset = std.math.divCeil(usize, constraint.len + 1 + name.len + 1, @sizeOf(u32)) catch unreachable;
+ const next_offset = @divCeil(constraint.len + 1 + name.len + 1, @sizeOf(u32));
self.constraint_names = self.constraint_names[next_offset..];
return .{
diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig
index 65c4a6afe023dae9ee7a03a08c77d585e9e1bd2e..81df10c169d0f358adc769d0681ce27bf45e6484 100644
--- a/src/Air/Legalize.zig
+++ b/src/Air/Legalize.zig
@@ -54,6 +54,8 @@ pub const Feature = enum {
scalarize_div_trunc_optimized,
scalarize_div_floor,
scalarize_div_floor_optimized,
+ scalarize_div_ceil,
+ scalarize_div_ceil_optimized,
scalarize_div_exact,
scalarize_div_exact_optimized,
scalarize_rem,
@@ -173,6 +175,15 @@ pub const Feature = enum {
/// Not compatible with `scalarize_mul_safe`.
expand_mul_safe,
+ /// Replace `div_ceil` with truncating division followed by a remainder based adjustment for integers,
+ /// or division followed by ceil for floats.
+ /// Not compatible with `scalarize_div_ceil`.
+ expand_div_ceil,
+ /// Replace `div_ceil_optimized` with truncating division followed by a remainder based adjustment for integers,
+ /// or division followed by ceil for floats.
+ /// Not compatible with `scalarize_div_ceil_optimized`.
+ expand_div_ceil_optimized,
+
/// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`.
/// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the
/// first byte of memory until bit pointers know their backing type.
@@ -231,6 +242,8 @@ pub const Feature = enum {
.div_trunc_optimized => .scalarize_div_trunc_optimized,
.div_floor => .scalarize_div_floor,
.div_floor_optimized => .scalarize_div_floor_optimized,
+ .div_ceil => .scalarize_div_ceil,
+ .div_ceil_optimized => .scalarize_div_ceil_optimized,
.div_exact => .scalarize_div_exact,
.div_exact_optimized => .scalarize_div_exact_optimized,
.rem => .scalarize_rem,
@@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
.none => {},
.scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
- .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorBlockPayload(
+ .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
inst,
bin_op.lhs,
bin_op.rhs,
@@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op));
}
},
+ inline .div_ceil, .div_ceil_optimized => |air_tag| {
+ const expand_feature: Feature = switch (air_tag) {
+ .div_ceil => .expand_div_ceil,
+ .div_ceil_optimized => .expand_div_ceil_optimized,
+ else => unreachable,
+ };
+
+ if (l.features.has(expand_feature)) {
+ assert(!l.features.has(.scalarize(air_tag))); // it doesn't make sense to do both
+ continue :inst l.replaceInst(inst, .block, try l.divCeilBlockPayload(inst, air_tag));
+ } else {
+ const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
+ switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) {
+ .none => {},
+ .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)),
+ .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload(
+ inst,
+ bin_op.lhs,
+ bin_op.rhs,
+ air_tag,
+ )),
+ }
+ }
+ },
inline .int_from_float_safe,
.int_from_float_optimized_safe,
=> |air_tag| {
@@ -709,7 +746,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
.switch_br, .loop_switch_br => {
const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op;
const extra = l.extraData(Air.SwitchBr, pl_op.payload);
- const hint_bag_count = std.math.divCeil(usize, extra.data.cases_len + 1, 10) catch unreachable;
+ const hint_bag_count = @divCeil(extra.data.cases_len + 1, 10);
var extra_index = extra.end + hint_bag_count;
for (0..extra.data.cases_len) |_| {
const case_extra = l.extraData(Air.SwitchBr.Case, extra_index);
@@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_
} };
}
+fn divCeilBlockPayload(
+ l: *Legalize,
+ orig_inst: Air.Inst.Index,
+ air_tag: Air.Inst.Tag,
+) Error!Air.Inst.Data {
+ const pt = l.pt;
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ const bin_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].bin_op;
+ const operand_ty = l.typeOf(bin_op.lhs);
+ assert(l.typeOf(bin_op.rhs).toIntern() == operand_ty.toIntern());
+
+ const scalar_ty = operand_ty.scalarType(zcu);
+ const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
+
+ switch (scalar_ty.zigTypeTag(zcu)) {
+ .float => {
+ // %result = ceil(lhs / rhs)
+
+ var inst_buf: [3]Air.Inst.Index = undefined;
+ try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
+
+ var main_block: Block = .init(&inst_buf);
+
+ const div_tag: Air.Inst.Tag = switch (air_tag) {
+ .div_ceil => .div_float,
+ .div_ceil_optimized => .div_float_optimized,
+ else => unreachable,
+ };
+
+ const div_inst = main_block.add(l, .{
+ .tag = div_tag,
+ .data = .{ .bin_op = bin_op },
+ });
+
+ const ceil_inst = main_block.add(l, .{
+ .tag = .ceil,
+ .data = .{ .un_op = div_inst.toRef() },
+ });
+
+ main_block.addBr(l, orig_inst, ceil_inst.toRef());
+
+ _ = main_block.stealRemainingCapacity();
+ return .{ .ty_pl = .{
+ .ty = .fromType(operand_ty),
+ .payload = try l.addBlockBody(main_block.body()),
+ } };
+ },
+
+ .int => {
+ // Integer div_ceil:
+ //
+ // q = div_trunc(lhs, rhs)
+ // r = rem(lhs, rhs)
+ //
+ // unsigned:
+ // q + int(r != 0)
+ //
+ // signed:
+ // q + int(r != 0 and same_sign(lhs, rhs))
+ //
+ // same_sign is `(lhs ^ rhs) >= 0`.
+
+ var inst_buf: [10]Air.Inst.Index = undefined;
+ try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
+
+ var main_block: Block = .init(&inst_buf);
+
+ const q_inst = main_block.add(l, .{
+ .tag = .div_trunc,
+ .data = .{ .bin_op = bin_op },
+ });
+
+ const r_inst = main_block.add(l, .{
+ .tag = .rem,
+ .data = .{ .bin_op = bin_op },
+ });
+
+ const zero_ref: Air.Inst.Ref = if (is_vector) zero: {
+ const zero_scalar = try pt.intValue(scalar_ty, 0);
+ const zero_vec = try pt.aggregateSplatValue(operand_ty, zero_scalar);
+ break :zero Air.internedToRef(zero_vec.toIntern());
+ } else Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
+
+ const r_nonzero_inst = try main_block.addCmp(
+ l,
+ .neq,
+ r_inst.toRef(),
+ zero_ref,
+ .{ .vector = is_vector },
+ );
+
+ const int_info = scalar_ty.intInfo(zcu);
+
+ const need_adjust_inst: Air.Inst.Index = if (int_info.signedness == .unsigned) r_nonzero_inst else inst: {
+ const sign_xor_inst = main_block.add(l, .{
+ .tag = .xor,
+ .data = .{ .bin_op = .{
+ .lhs = bin_op.lhs,
+ .rhs = bin_op.rhs,
+ } },
+ });
+
+ const signs_same_inst = try main_block.addCmp(
+ l,
+ .gte,
+ sign_xor_inst.toRef(),
+ zero_ref,
+ .{ .vector = is_vector },
+ );
+
+ break :inst main_block.add(l, .{
+ .tag = .bit_and,
+ .data = .{ .bin_op = .{
+ .lhs = r_nonzero_inst.toRef(),
+ .rhs = signs_same_inst.toRef(),
+ } },
+ });
+ };
+
+ const adjust_u1_ty = if (is_vector)
+ try pt.vectorType(.{
+ .len = operand_ty.vectorLen(zcu),
+ .child = Type.u1.toIntern(),
+ })
+ else
+ Type.u1;
+
+ const adjust_u1_ref = main_block.addBitCast(l, adjust_u1_ty, need_adjust_inst.toRef());
+ const adjust_inst = main_block.addTyOp(l, .int_cast, operand_ty, adjust_u1_ref);
+
+ const result_inst = main_block.add(l, .{
+ .tag = .add,
+ .data = .{ .bin_op = .{
+ .lhs = q_inst.toRef(),
+ .rhs = adjust_inst.toRef(),
+ } },
+ });
+
+ main_block.addBr(l, orig_inst, result_inst.toRef());
+
+ _ = main_block.stealRemainingCapacity();
+ return .{ .ty_pl = .{
+ .ty = .fromType(operand_ty),
+ .payload = try l.addBlockBody(main_block.body()),
+ } };
+ },
+
+ else => unreachable,
+ }
+}
+
fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
const pt = l.pt;
const zcu = pt.zcu;
@@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload(
} };
}
-fn softFloatDivTruncFloorBlockPayload(
+fn softFloatDivTruncFloorCeilBlockPayload(
l: *Legalize,
orig_inst: Air.Inst.Index,
lhs: Air.Inst.Ref,
@@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload(
const floor_tag: Air.Inst.Tag = switch (air_tag) {
.div_trunc, .div_trunc_optimized => .trunc_float,
.div_floor, .div_floor_optimized => .floor,
+ .div_ceil, .div_ceil_optimized => .ceil,
else => unreachable,
};
diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig
index 0570962e5475c2d6281c84168654d01db0cda490..fbfd74a772c0ec3028b56b22201d33d8895d166b 100644
--- a/src/Air/Liveness.zig
+++ b/src/Air/Liveness.zig
@@ -417,6 +417,8 @@ fn analyzeInst(
.div_floor_optimized,
.div_exact,
.div_exact_optimized,
+ .div_ceil,
+ .div_ceil_optimized,
.rem,
.rem_optimized,
.mod,
diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig
index 200110fbfbfd89248a6ef262e7dd74a3bd3ef5ad..24737ebedf6e14446e5d699af845e7c8648f1833 100644
--- a/src/Air/Liveness/Verify.zig
+++ b/src/Air/Liveness/Verify.zig
@@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
+ .div_ceil,
+ .div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
diff --git a/src/Air/Verify.zig b/src/Air/Verify.zig
index f9813732130d2f093cd47f66634d6ddde92e846f..b2812f8660d136ce40a4b088b5c30b3bb138e385 100644
--- a/src/Air/Verify.zig
+++ b/src/Air/Verify.zig
@@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
+ .div_ceil,
+ .div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
diff --git a/src/Air/print.zig b/src/Air/print.zig
index 20c50983ec5d0c565ec6c6724562bb365369380b..b263c20634f3ea5a0442e6fa14af24ef77b9b64e 100644
--- a/src/Air/print.zig
+++ b/src/Air/print.zig
@@ -132,6 +132,7 @@ const Writer = struct {
.div_float,
.div_trunc,
.div_floor,
+ .div_ceil,
.div_exact,
.rem,
.mod,
@@ -160,6 +161,7 @@ const Writer = struct {
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
+ .div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
diff --git a/src/InternPool.zig b/src/InternPool.zig
index 792b7636a46f0cf40686efe14bc490ef7d68f1f9..eae7f32c7f17782b694a21d06c52bfe456049dcf 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -3581,11 +3581,11 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.start = extra_index,
.len = extra.data.fields_len,
} else .empty;
- extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
+ extra_index += @divCeil(field_aligns.len, 4);
const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{
.tid = unwrapped_index.tid,
.start = extra_index,
- .len = std.math.divCeil(u32, extra.data.fields_len, 32) catch unreachable,
+ .len = @divCeil(extra.data.fields_len, 32),
} else .empty;
extra_index += field_is_comptime_bits.len;
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 {
.start = extra_index,
.len = extra.data.fields_len,
} else .empty;
- extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable;
+ extra_index += @divCeil(field_aligns.len, 4);
return .{
.zir_index = extra.data.zir_index,
diff --git a/src/Sema.zig b/src/Sema.zig
index 0e58f0c819bf00d512bd58aa7f346e634bdb0368..dfd37d70ec9787b7d8ded314ea5314b1e8513b64 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1335,6 +1335,7 @@ fn analyzeBodyInner(
.div => try sema.zirDiv(block, inst),
.div_exact => try sema.zirDivExact(block, inst),
.div_floor => try sema.zirDivFloor(block, inst),
+ .div_ceil => try sema.zirDivCeil(block, inst),
.div_trunc => try sema.zirDivTrunc(block, inst),
.mod_rem => try sema.zirModRem(block, inst),
@@ -10304,7 +10305,7 @@ fn finishSwitchBr(
fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {
const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
if (unused_hints >= additional_count) return;
- const bags_required = std.math.divCeil(u32, hints.count + additional_count, hints_per_bag) catch unreachable;
+ const bags_required = @divCeil(hints.count + additional_count, hints_per_bag);
return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);
}
fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void {
@@ -10320,7 +10321,7 @@ fn finishSwitchBr(
}
};
var branch_hints: BranchHints = hints: {
- const num_bags = std.math.divCeil(u32, estimated_cases_len, BranchHints.hints_per_bag) catch unreachable;
+ const num_bags = @divCeil(estimated_cases_len, BranchHints.hints_per_bag);
break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };
};
defer branch_hints.bags.deinit(gpa);
@@ -12211,7 +12212,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion(
{
// All branch hints are `.none`, so just add zero elems.
comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0);
- const need_elems = std.math.divCeil(usize, field_indices.len + 1, 10) catch unreachable;
+ const need_elems = @divCeil(field_indices.len + 1, 10);
try cases_extra.appendNTimes(gpa, 0, need_elems);
}
@@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
return sema.fail(
block,
src,
- "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact",
+ "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact",
.{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
);
}
@@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
}
+fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
+ const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
+ const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
+ const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const lhs = sema.resolveInst(extra.lhs);
+ const rhs = sema.resolveInst(extra.rhs);
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
+ const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
+
+ const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
+ .override = &.{ lhs_src, rhs_src },
+ });
+
+ const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
+ const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
+
+ const lhs_scalar_ty = lhs_ty.scalarType(zcu);
+ const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
+
+ const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
+
+ try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil);
+
+ const maybe_lhs_val = sema.resolveValue(casted_lhs);
+ const maybe_rhs_val = sema.resolveValue(casted_rhs);
+
+ const allow_div_zero = !is_int and
+ resolved_type.toIntern() != .comptime_float_type and
+ block.float_mode == .strict;
+
+ if (maybe_lhs_val) |lhs_val| {
+ if (maybe_rhs_val) |rhs_val| {
+ const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil);
+ return Air.internedToRef(result.toIntern());
+ }
+ if (allow_div_zero) {
+ if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
+ } else {
+ try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
+ }
+ } else if (maybe_rhs_val) |rhs_val| {
+ if (allow_div_zero) {
+ if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
+ } else {
+ try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
+ if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+
+ if (block.wantSafety()) {
+ try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
+ try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+ }
+
+ return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs);
+}
+
fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -18051,7 +18117,7 @@ fn analyzeRet(
fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
// extend this swich as additional operators are implemented
return switch (tag) {
- .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
+ .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true,
else => false,
};
}
@@ -18632,7 +18698,7 @@ fn finishStructInit(
return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
},
.@"packed" => {
- const buf = try sema.arena.alloc(u8, @intCast((struct_ty.bitSize(zcu) + 7) / 8));
+ const buf = try sema.arena.alloc(u8, @intCast(@divCeil(struct_ty.bitSize(zcu), 8)));
@memset(buf, 0);
var bit_offset: u16 = 0;
for (field_inits) |field_init| {
@@ -29679,7 +29745,7 @@ pub fn bitCastVal(
if (val.isUndef(zcu)) {
return pt.undefValue(dest_ty);
} else {
- const buf = try sema.arena.alloc(u8, @intCast((bit_size + 7) / 8));
+ const buf = try sema.arena.alloc(u8, @intCast(@divCeil(bit_size, 8)));
@memset(buf, 0);
val.writeToPackedMemory(zcu, buf, 0);
return .readFromPackedMemory(dest_ty, pt, buf, 0);
diff --git a/src/Sema/arith.zig b/src/Sema/arith.zig
index cf1c699e4a5682c6cd1d3669fe17aebad97d4b89..570cd3e533c5ffedae974f4198aedeee1e2268d7 100644
--- a/src/Sema/arith.zig
+++ b/src/Sema/arith.zig
@@ -768,7 +768,7 @@ fn mulSatScalar(
}
}
-pub const DivOp = enum { div, div_trunc, div_floor, div_exact };
+pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact };
/// Applies the `/` operator to comptime-known values.
/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
@@ -843,6 +843,11 @@ fn divScalar(
if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
return res.val;
},
+ .div_ceil => {
+ const res = try intDivCeil(sema, lhs_val, rhs_val, ty);
+ if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
+ return res.val;
+ },
.div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
.remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
.overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
@@ -851,7 +856,7 @@ fn divScalar(
}
} else {
const allow_div_zero = switch (op) {
- .div, .div_trunc, .div_floor => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
+ .div, .div_trunc, .div_floor, .div_ceil => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
.div_exact => false,
};
if (!allow_div_zero) {
@@ -871,6 +876,7 @@ fn divScalar(
.div => return floatDiv(sema, lhs_val, rhs_val, ty),
.div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
.div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
+ .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty),
.div_exact => {
if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
return sema.fail(block, src, "exact division produced remainder", .{});
@@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
}
return pt.intValue_big(ty, result_q.toConst());
}
+fn intDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
+ const result = intDivCeilInner(sema, lhs, rhs, ty) catch |err| switch (err) {
+ error.Overflow => {
+ const result = intDivCeilInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
+ error.Overflow => unreachable,
+ else => |e| return e,
+ };
+ return .{ .overflow = true, .val = result };
+ },
+ else => |e| return e,
+ };
+ return .{ .overflow = false, .val = result };
+}
+fn intDivCeilInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
+ const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
+ const limbs_q = try sema.arena.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len,
+ );
+ const limbs_r = try sema.arena.alloc(
+ std.math.big.Limb,
+ rhs_bigint.limbs.len,
+ );
+ const limbs_buf = try sema.arena.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
+ );
+ var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
+ var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
+ result_q.divCeil(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
+ if (ty.toIntern() != .comptime_int_type) {
+ const info = ty.intInfo(zcu);
+ if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
+ return error.Overflow;
+ }
+ }
+ return pt.intValue_big(ty, result_q.toConst());
+}
fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
.storage = storage,
} }));
}
+fn floatDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const target = zcu.getTarget();
+ const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
+ 16 => .{ .f16 = @divCeil(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
+ 32 => .{ .f32 = @divCeil(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
+ 64 => .{ .f64 = @divCeil(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
+ 80 => .{ .f80 = @divCeil(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
+ 128 => .{ .f128 = @divCeil(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
+ else => unreachable,
+ };
+ return .fromInterned(try pt.intern(.{ .float = .{
+ .ty = ty.toIntern(),
+ .storage = storage,
+ } }));
+}
fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
const zcu = sema.pt.zcu;
const target = zcu.getTarget();
@@ -2238,7 +2304,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value {
if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
const info = ty.intInfo(zcu);
- const buf = try sema.arena.alloc(u8, (info.bits + 7) / 8);
+ const buf = try sema.arena.alloc(u8, @divCeil(info.bits, 8));
@memset(buf, 0xAA);
const limbs = try sema.arena.alloc(
diff --git a/src/Type.zig b/src/Type.zig
index fc867194e3f3ebe2a3a772946a0f57d90d87dbe9..6372c1e157172f6a7b4ee97f3d441af76e9a500b 100644
--- a/src/Type.zig
+++ b/src/Type.zig
@@ -968,7 +968,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
if (vector_type.len > 64) return .@"16";
- const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
+ const bytes = @divCeil(vector_type.len, 8);
return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
}
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 {
.vector_type => |vec| {
const elem_ty: Type = .fromInterned(vec.child);
const bytes = switch (zcu.comp.getZigBackend()) {
- else => std.math.divCeil(u64, vec.len * elem_ty.bitSize(zcu), 8) catch unreachable,
+ else => @divCeil(vec.len * elem_ty.bitSize(zcu), 8),
.stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu),
.stage2_x86_64 => switch (elem_ty.toIntern()) {
- .bool_type => std.math.divCeil(u64, vec.len, 8) catch unreachable,
+ .bool_type => @divCeil(vec.len, 8),
else => vec.len * elem_ty.abiSize(zcu),
},
};
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index ba8cde4e9c45bbdca010d1d87eb1e8a2b3ef2f52..b74c7fe9ec580071a887498cd8ab0ef04e287257 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
+ .div_ceil,
+ .div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
@@ -403,11 +405,11 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
.live_registers = undefined,
.repeat_list = undefined,
});
- try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
+ try isel.dom.appendNTimes(gpa, 0, @divCeil(isel.dom_len, @bitSizeOf(DomInt)));
try isel.analyze(air_body_block.body);
for (
isel.dom.items[initial_dom_start..].ptr,
- isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
+ isel.dom.items[isel.dom_start..][0..@divCeil(initial_dom_len, @bitSizeOf(DomInt))],
) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
isel.dom_start = initial_dom_start;
isel.dom_len = initial_dom_len;
@@ -589,7 +591,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
.live_registers = undefined,
.repeat_list = undefined,
});
- try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
+ try isel.dom.appendNTimes(gpa, 0, @divCeil(isel.dom_len, @bitSizeOf(DomInt)));
var cases_it = switch_br.iterateCases();
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 {
for (
isel.dom.items[initial_dom_start..].ptr,
- isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
+ isel.dom.items[isel.dom_start..][0..@divCeil(initial_dom_len, @bitSizeOf(DomInt))],
) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
isel.dom_start = initial_dom_start;
isel.dom_len = initial_dom_len;
@@ -10194,7 +10196,7 @@ pub const Value = struct {
0 => unreachable,
1...64 => unreachable,
65...256 => |bits| if (offset == 0 and size == ty_size) {
- const parts_len = std.math.divCeil(u16, bits, 64) catch unreachable;
+ const parts_len = @divCeil(bits, 64);
vi.setParts(isel, @intCast(parts_len));
for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8);
},
@@ -10238,7 +10240,7 @@ pub const Value = struct {
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
const array_len = array_type.lenIncludingSentinel();
if (array_len > Value.max_parts and
- (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
const alignment = vi.alignment(isel);
const Part = struct { offset: u64, size: u64 };
@@ -10288,7 +10290,7 @@ pub const Value = struct {
.anyframe_type => unreachable,
.error_union_type => |error_union_type| {
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
- if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ if ((@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
const alignment = vi.alignment(isel);
const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
@@ -10395,7 +10397,7 @@ pub const Value = struct {
}
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
if (loaded_struct.field_types.len > Value.max_parts and
- (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
const alignment = vi.alignment(isel);
const Part = struct { offset: u64, size: u64, signedness: ?std.lang.Signedness, is_vector: bool };
@@ -10456,7 +10458,7 @@ pub const Value = struct {
.tuple_type => |tuple_type| {
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
if (tuple_type.types.len > Value.max_parts and
- (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ (@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
const alignment = vi.alignment(isel);
const Part = struct { offset: u64, size: u64, is_vector: bool };
@@ -10511,7 +10513,7 @@ pub const Value = struct {
} },
}
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
- if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ if ((@divCeil(size, @as(u64, 1) << min_part_log2_stride)) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
const alignment = vi.alignment(isel);
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index b814fe55c16b3c96ab7f6caee7b4e1119ae73257..3cf7d9c2fbc2921c3f7745c8f020520f464e380e 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
try airBinBuiltinCall(f, inst, "fmod", .none);
},
.div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
+ .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none),
.mod => try airBinBuiltinCall(f, inst, "mod", .none),
.abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),
@@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
+ .div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig
index bad4b1461b1c01274d481c984a752bb6704d4615..602277a91188d72994ac83b025ecf5c580f5ce2b 100644
--- a/src/codegen/llvm/FuncGen.zig
+++ b/src/codegen/llvm/FuncGen.zig
@@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
.div_float => try self.airDivFloat(inst, .normal),
.div_trunc => try self.airDivTrunc(inst, .normal),
.div_floor => try self.airDivFloor(inst, .normal),
+ .div_ceil => try self.airDivCeil(inst, .normal),
.div_exact => try self.airDivExact(inst, .normal),
.rem => try self.airRem(inst, .normal),
.mod => try self.airMod(inst, .normal),
@@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
.div_float_optimized => try self.airDivFloat(inst, .fast),
.div_trunc_optimized => try self.airDivTrunc(inst, .fast),
.div_floor_optimized => try self.airDivFloor(inst, .fast),
+ .div_ceil_optimized => try self.airDivCeil(inst, .fast),
.div_exact_optimized => try self.airDivExact(inst, .fast),
.rem_optimized => try self.airRem(inst, .fast),
.mod_optimized => try self.airMod(inst, .fast),
@@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind)
return self.wip.bin(.udiv, lhs, rhs, "");
}
+fn airDivCeil(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
+ const o = self.object;
+ const zcu = o.zcu;
+ const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.typeOfIndex(inst);
+ const scalar_ty = inst_ty.scalarType(zcu);
+
+ if (scalar_ty.isRuntimeFloat()) {
+ const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs });
+ return self.buildFloatOp(.ceil, fast, inst_ty, 1, .{result});
+ }
+ if (scalar_ty.isSignedInt(zcu)) {
+ const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
+ const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
+
+ const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb;
+ var bfa_buf: ExpectedContents = undefined;
+ var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa);
+ const allocator = bfa.allocator();
+
+ const scalar_bits = scalar_ty.intInfo(zcu).bits;
+ var smin_big_int: std.math.big.int.Mutable = .{
+ .limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcTwosCompLimbCount(scalar_bits),
+ ),
+ .len = undefined,
+ .positive = undefined,
+ };
+ defer allocator.free(smin_big_int.limbs);
+ smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits);
+ const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst(
+ scalar_llvm_ty,
+ smin_big_int.toConst(),
+ ));
+
+ const zero = try o.builder.splatValue(
+ inst_llvm_ty,
+ try o.builder.intConst(scalar_llvm_ty, 0),
+ );
+
+ const div = try self.wip.bin(.sdiv, lhs, rhs, "divCeil.div");
+ const rem = try self.wip.bin(.srem, lhs, rhs, "divCeil.rem");
+
+ const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "divCeil.rhs_sign");
+ const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "divCeil.rem_xor_rhs_sign");
+
+ const need_correction = try self.wip.icmp(.sgt, rem_xor_rhs_sign, zero, "divCeil.need_correction");
+
+ const correction = try self.wip.cast(.zext, need_correction, inst_llvm_ty, "divCeil.correction");
+ return self.wip.bin(.@"add nsw", div, correction, "divCeil");
+ } else {
+ const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value);
+ const inst_llvm_ty = try o.lowerType(inst_ty, .by_value);
+
+ const zero = try o.builder.splatValue(
+ inst_llvm_ty,
+ try o.builder.intConst(scalar_llvm_ty, 0),
+ );
+
+ const div = try self.wip.bin(.udiv, lhs, rhs, "divCeil.div");
+ const rem = try self.wip.bin(.urem, lhs, rhs, "divCeil.rem");
+
+ const rem_nonzero = try self.wip.icmp(.ne, rem, zero, "divCeil.rem_nonzero");
+ const correction = try self.wip.cast(.zext, rem_nonzero, inst_llvm_ty, "divCeil.correction");
+
+ return self.wip.bin(.@"add nuw", div, correction, "divCeil");
+ }
+}
+
fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value {
const zcu = self.object.zcu;
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
@@ -6874,7 +6948,7 @@ const ParamTypeIterator = struct {
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => {
const size = ty.abiSize(zcu);
- assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);
+ assert(@divCeil(size, 8) == types_index);
if (size % 8 > 0) {
it.types_buffer[types_index - 1] =
try it.object.builder.intType(@intCast(size % 8 * 8));
@@ -7119,7 +7193,7 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E
switch (ip.indexToKey(ret_ty.toIntern())) {
.struct_type => {
const size = ret_ty.abiSize(zcu);
- assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index);
+ assert(@divCeil(size, 8) == types_index);
if (size % 8 > 0) {
types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8));
}
diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig
index 4bd30651254a7582d1d49a82562526a9c3e44735..b11b898556c0de6479e0aeaf2fe25d47d31e8445 100644
--- a/src/codegen/riscv64/CodeGen.zig
+++ b/src/codegen/riscv64/CodeGen.zig
@@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.mod,
.div_float,
.div_floor,
+ .div_ceil,
=> return func.fail("TODO: {s}", .{@tagName(tag)}),
.sqrt,
@@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.div_trunc_optimized,
.div_floor_optimized,
.div_exact_optimized,
+ .div_ceil_optimized,
.rem_optimized,
.mod_optimized,
.neg_optimized,
@@ -2215,8 +2217,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void {
};
const dst_mcv = if (dst_int_info.bits <= src_storage_bits and
- math.divCeil(u16, dst_int_info.bits, 64) catch unreachable ==
- math.divCeil(u32, src_storage_bits, 64) catch unreachable and
+ @divCeil(dst_int_info.bits, 64) == @divCeil(src_storage_bits, 64) and
func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true);
try func.genCopy(min_ty, dst_mcv, src_mcv);
diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig
index f0c2d95f9c424d6c1952266834a05e81e1c18377..2d67e7cc54576394bd88d2dcbacab8330b94b21f 100644
--- a/src/codegen/sparc64/CodeGen.zig
+++ b/src/codegen/sparc64/CodeGen.zig
@@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.mul_with_overflow => try self.airMulWithOverflow(inst),
.shl_with_overflow => try self.airShlWithOverflow(inst),
- .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
+ .div_float, .div_trunc, .div_floor, .div_ceil, .div_exact => try self.airDiv(inst),
.cmp_lt => try self.airCmp(inst, .lt),
.cmp_lte => try self.airCmp(inst, .lte),
@@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
+ .div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
diff --git a/src/codegen/spirv/Assembler.zig b/src/codegen/spirv/Assembler.zig
index f18f9028212b4916ed70a89d080e28a3bad75f3f..e748f4789b0325f2a4d0309d0fcad4ea3b66771f 100644
--- a/src/codegen/spirv/Assembler.zig
+++ b/src/codegen/spirv/Assembler.zig
@@ -375,7 +375,7 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {
},
.string => |offset| {
const text = std.mem.sliceTo(ass.inst.string_bytes.items[offset..], 0);
- const size = std.math.divCeil(usize, text.len + 1, @sizeOf(Word)) catch unreachable;
+ const size = @divCeil(text.len + 1, @sizeOf(Word));
try section.ensureUnusedCapacity(cg.gpa, size);
section.writeOperand(spec.LiteralString, text);
},
diff --git a/src/codegen/spirv/Section.zig b/src/codegen/spirv/Section.zig
index c3194a5456370a24f9b9e850e6f5753c1333f332..db338f77db57347d662a20d4c525f61f1b2c18ab 100644
--- a/src/codegen/spirv/Section.zig
+++ b/src/codegen/spirv/Section.zig
@@ -232,7 +232,7 @@ fn operandSize(comptime Operand: type, operand: Operand) usize {
return switch (Operand) {
spec.LiteralSpecConstantOpInteger => unreachable,
spec.Id, spec.LiteralInteger, spec.LiteralExtInstInteger => 1,
- spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable,
+ spec.LiteralString => @divCeil(operand.len + 1, @sizeOf(Word)),
spec.LiteralContextDependentNumber => switch (operand) {
.int32, .uint32, .float32 => 1,
.int64, .uint64, .float64 => 2,
diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig
index ad4b9e4b6d308c6f645785c570dc7eec5c9fe986..6d42d8d1ff69c9f5270106b8c9c311de3f309586 100644
--- a/src/codegen/wasm/CodeGen.zig
+++ b/src/codegen/wasm/CodeGen.zig
@@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
.scalarize_div_trunc_optimized,
.scalarize_div_floor,
.scalarize_div_floor_optimized,
+ .scalarize_div_ceil,
+ .scalarize_div_ceil_optimized,
.scalarize_div_exact,
.scalarize_div_exact_optimized,
.scalarize_rem,
@@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.div_exact,
.div_trunc,
.div_floor,
+ .div_ceil,
=> |tag| {
const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const lhs = try cg.resolveInst(bin_op.lhs);
@@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.div_exact => try cg.floatDiv(float_ty, lhs, rhs),
.div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),
.div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),
+ .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs),
else => unreachable,
};
@@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.div_exact => try cg.intDiv(int_ty, lhs, rhs),
.div_trunc => try cg.intDiv(int_ty, lhs, rhs),
.div_floor => try cg.intDivFloor(int_ty, lhs, rhs),
+ .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs),
else => unreachable,
};
@@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
+ .div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
@@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
}
}
+fn intDivCeil(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
+ switch (ty.bits) {
+ 0 => unreachable,
+ 1...32 => {
+ var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i32);
+ defer q.free(cg);
+
+ const zero: WValue = .{ .imm32 = 0 };
+
+ const r = try cg.intRem(ty, lhs, rhs);
+ var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
+ defer r_nonzero.free(cg);
+
+ if (!ty.is_signed) {
+ try cg.emitWValue(q);
+ try cg.emitWValue(r_nonzero);
+ try cg.addTag(.i32_add);
+ return .stack;
+ }
+
+ const sign_xor = try cg.intXor(ty, lhs, rhs);
+ var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
+ defer same_sign.free(cg);
+
+ try cg.emitWValue(q);
+ const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
+ try cg.emitWValue(need_adjust);
+ try cg.addTag(.i32_add);
+ return .stack;
+ },
+ 33...64 => {
+ var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i64);
+ defer q.free(cg);
+
+ const zero: WValue = .{ .imm64 = 0 };
+
+ const r = try cg.intRem(ty, lhs, rhs);
+ var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32);
+ defer r_nonzero.free(cg);
+
+ if (!ty.is_signed) {
+ try cg.emitWValue(q);
+ try cg.emitWValue(r_nonzero);
+ try cg.addTag(.i64_extend_i32_u);
+ try cg.addTag(.i64_add);
+ return .stack;
+ }
+
+ const sign_xor = try cg.intXor(ty, lhs, rhs);
+ var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32);
+ defer same_sign.free(cg);
+
+ try cg.emitWValue(q);
+ const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign);
+ try cg.emitWValue(need_adjust);
+ try cg.addTag(.i64_extend_i32_u);
+ try cg.addTag(.i64_add);
+ return .stack;
+ },
+ else => {
+ var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.usize);
+ defer q.free(cg);
+
+ const zero = try cg.intZeroValue(ty);
+
+ const r = try cg.intRem(ty, lhs, rhs);
+ var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.u32);
+ defer r_nonzero.free(cg);
+
+ if (!ty.is_signed) {
+ var adjust_bigint = try (try cg.intCast(ty, .u32, r_nonzero)).toLocal(cg, Type.usize);
+ defer adjust_bigint.free(cg);
+
+ return try cg.intAdd(ty, q, adjust_bigint);
+ }
+
+ const sign_xor = try cg.intXor(ty, lhs, rhs);
+ var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.u32);
+ defer same_sign.free(cg);
+
+ var adjust = try (try cg.intAnd(.u32, r_nonzero, same_sign)).toLocal(cg, Type.u32);
+ defer adjust.free(cg);
+
+ var adjust_bigint = try (try cg.intCast(ty, .u32, adjust)).toLocal(cg, Type.usize);
+ defer adjust_bigint.free(cg);
+
+ return try cg.intAdd(ty, q, adjust_bigint);
+ },
+ }
+}
+
fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
switch (ty.bits) {
0 => unreachable,
@@ -3581,7 +3678,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
const result = try cg.allocInt(ty);
- const used_len = (math.divCeil(u16, ty.bits, 64) catch unreachable) * 8;
+ const used_len = @divCeil(ty.bits, 64) * 8;
if (ty.bits % 64 != 0) {
try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 });
@@ -3647,7 +3744,7 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
} else {
const result = try cg.allocInt(int_ty);
const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
- const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8;
+ const used_len = @divCeil(int_ty.bits, 64) * 8;
try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF });
@@ -3681,7 +3778,7 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
} else {
const result = try cg.allocInt(int_ty);
const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
- const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8;
+ const used_len = @divCeil(int_ty.bits, 64) * 8;
try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 });
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
return cg.floatFloor(ty, div_result);
}
+// div_ceil(a, b) = ceil(a / b)
+fn floatDivCeil(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
+ const div_result = try cg.floatDiv(ty, lhs, rhs);
+ return cg.floatCeil(ty, div_result);
+}
+
// mod(a, b) = fmod(fmod(a, b) + b, b)
fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue {
const r = try cg.floatRem(ty, lhs, rhs);
diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig
index 74fb9b4f87725574506a14178d989a66b5ae5731..dbb942eaed207109e57a2f5a39bf7d5d4f21c0d7 100644
--- a/src/codegen/x86_64/CodeGen.zig
+++ b/src/codegen/x86_64/CodeGen.zig
@@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
.expand_sub_safe,
.expand_mul_safe,
+ .expand_div_ceil,
+ .expand_div_ceil_optimized,
+
.expand_packed_load,
.expand_packed_store,
.expand_packed_agg_field_val,
@@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
for (ops) |op| try op.die(cg);
},
+ .div_ceil, .div_ceil_optimized => unreachable,
+
// No soft-float `Legalize` features are enabled, so this instruction never appears.
.legalize_compiler_rt_call => unreachable,
@@ -174781,7 +174786,7 @@ fn genShiftBinOpMir(
try self.spillEflagsIfOccupied();
if (abi_size > 16) {
- const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable;
+ const limbs_len = @divCeil(abi_size, 8);
assert(shift_abi_size >= 1 and shift_abi_size <= 2);
const rcx_lock: ?RegisterLock = switch (rhs_mcv) {
@@ -179593,7 +179598,7 @@ fn genSetReg(
if (pack_alias != sign_alias) try cg.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, sign_alias);
try cg.asmRegisterRegister(.{ .p_b, .ackssw }, pack_alias, pack_alias);
}
- mask_size = std.math.divCeil(u32, mask_size, 2) catch unreachable;
+ mask_size = @divCeil(mask_size, 2);
break :pack_reg pack_reg;
},
};
@@ -180157,7 +180162,7 @@ fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void {
const bit_size = dst_ty.bitSize(zcu);
if (abi_size * 8 <= bit_size) break :result dst_mcv;
- const dst_limbs_len = std.math.divCeil(u31, @intCast(bit_size), 64) catch unreachable;
+ const dst_limbs_len: u31 = @intCast(@divCeil(bit_size, 64));
const high_mcv: MCValue = switch (dst_mcv) {
.register => |dst_reg| .{ .register = dst_reg },
.register_pair => |dst_regs| .{ .register = dst_regs[1] },
@@ -183550,7 +183555,7 @@ const Temp = struct {
const part_ty: Type = if (src_regs.len == 1)
src_ty
else if (cg.intInfo(src_ty)) |int_info| part_ty: {
- assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable);
+ assert(src_regs.len == @divCeil(int_info.bits, 64));
break :part_ty .u64;
} else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {
else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
@@ -183560,7 +183565,7 @@ const Temp = struct {
break :part_ty .usize;
},
.array_type => {
- assert(src_regs.len - part_index == std.math.divCeil(u32, src_size, 8) catch unreachable);
+ assert(src_regs.len - part_index == @divCeil(src_size, 8));
break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_size, 8));
},
.vector_type => |vector_type| switch (@divExact(vector_type.len, src_regs.len)) {
@@ -183580,7 +183585,7 @@ const Temp = struct {
},
},
.struct_type, .union_type => {
- assert(src_regs.len - part_index == std.math.divCeil(u32, src_size, 8) catch unreachable);
+ assert(src_regs.len - part_index == @divCeil(src_size, 8));
break :part_ty switch (src_size) {
0, 3, 5...7 => unreachable,
1 => .u8,
diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig
index 1e8e60ffb665fc70709a5291040a32a1bebcb5af..d5ec79ce22a8f650992ae77567b3d9f303dd2b82 100644
--- a/src/codegen/x86_64/Emit.zig
+++ b/src/codegen/x86_64/Emit.zig
@@ -772,7 +772,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
.rip_inst => 4,
else => unreachable,
- } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);
+ } else @intCast(@divCeil(op.immBitSize(), 8));
reloc_offset -= enc_length;
if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
assert(!is_mem);
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index d51c59ed23941283b2de8258db0ad865adf8aa33..b6ff6c754bc97b0efc78522b30dcf302d9ad072d 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -2137,7 +2137,7 @@ pub const WipNav = struct {
.signed => DW.FORM.sdata,
.unsigned => DW.FORM.udata,
}));
- try wip_nav.debug_info.ensureUnusedCapacity(std.math.divCeil(usize, bits, 7) catch unreachable);
+ try wip_nav.debug_info.ensureUnusedCapacity(@divCeil(bits, 7));
var bit: usize = 0;
var carry: u1 = 1;
while (bit < bits) {
@@ -2158,7 +2158,7 @@ pub const WipNav = struct {
}
} else {
try diw.writeUleb128(DW.FORM.block);
- const bytes = @max(ty.abiSize(zcu), std.math.divCeil(usize, bits, 8) catch unreachable);
+ const bytes = @max(ty.abiSize(zcu), @divCeil(bits, 8));
try diw.writeUleb128(bytes);
try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes));
big_int.writeTwosComplement(
@@ -4275,7 +4275,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
try wip_nav.abbrevCode(.aggregate_undefined_comptime_value);
try wip_nav.refType(.fromInterned(error_union.ty));
var err_buf: [4]u8 = undefined;
- const err_bytes = err_buf[0 .. std.math.divCeil(u17, zcu.errorSetBits(), 8) catch unreachable];
+ const err_bytes = err_buf[0..@divCeil(zcu.errorSetBits(), 8)];
dwarf.writeInt(err_bytes, switch (error_union.val) {
.err_name => |err_name| try pt.getErrorValue(err_name),
.payload => 0,
diff --git a/src/main.zig b/src/main.zig
index 8f569d2a856e039466416c5bd3e810a1a0d15610..91d1a8cee30b57880bfb3092ca05576dd71abe30 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -162,8 +162,7 @@ const use_safe_allocator = build_options.debug_gpa or
.ReleaseFast, .ReleaseSmall => false,
});
-// TODO: The `align(@alignOf(std.heap.SafeAllocator))` can be removed the next time zig1.wasm is updated
-var safe_allocator: std.heap.SafeAllocator align(@alignOf(std.heap.SafeAllocator)) = .init(std.heap.page_allocator, .{
+var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{
.stack_trace_frames = build_options.mem_leak_frames,
});
diff --git a/src/print_zir.zig b/src/print_zir.zig
index 64703fbd60052209854fc733570fa008ec5b201e..74aaf0bda3e6406fefa28610ed092dce5bc583c8 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -392,6 +392,7 @@ const Writer = struct {
.truncate,
.div_exact,
.div_floor,
+ .div_ceil,
.div_trunc,
.mod,
.rem,
diff --git a/stage1/zig.h b/stage1/zig.h
index db94ac20445b942586741451f4e02bc7c579aae3..fbc924ca334e99eb12d2f37e3ebffa970ced7b9d 100644
--- a/stage1/zig.h
+++ b/stage1/zig.h
@@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t;
static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
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)); \
} \
+\
+ static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
+ return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
+ } \
+\
+ static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
+ return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
+ ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
+ } \
\
zig_basic_operator(uint##w##_t, mod_u##w, %) \
\
@@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
}
+static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
+ zig_u128 rem = zig_rem_u128(lhs, rhs);
+ uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
+ ? UINT64_C(1) : UINT64_C(0);
+ return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
+}
+
+static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
+ zig_i128 rem = zig_rem_i128(lhs, rhs);
+ int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
+ ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
+ : INT64_C(0);
+ return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
+}
+
#define zig_mod_u128 zig_rem_u128
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
zig_trap();
}
+static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
+ zig_trap();
+}
+
zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
if (!is_signed) {
@@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
} \
+\
+ static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
+ return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
+ } \
\
static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm
index 1a985c43006c7abfd1e82ead3ac9b656a5b753ab..6a222c0c6138663c1a36630a48e164f351fd17b7 100644
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
diff --git a/test/behavior/int128.zig b/test/behavior/int128.zig
index 9687e3171497df8fb41e9c7fd3277829e81069e2..ce69d80522516c71cc586b84bb3784f0ba9c75e6 100644
--- a/test/behavior/int128.zig
+++ b/test/behavior/int128.zig
@@ -59,6 +59,7 @@ test "int128" {
const a: i128 = -170141183460469231731687303715884105728;
const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000;
try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885);
+ try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884);
try expect(a == b);
}
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
index 705aae28dbcfe45cadf9421b4ae6268b0f6599a9..22b91be640579e1e8fe460f4652f998c5c1b9b6d 100644
--- a/test/behavior/math.zig
+++ b/test/behavior/math.zig
@@ -488,6 +488,36 @@ fn testIntDivision() !void {
try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);
try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);
+ try expect(divCeil(i32, 5, 3) == 2);
+ try expect(divCeil(i32, -5, 3) == -1);
+ try expect(divCeil(i32, -0x80000000, -2) == 0x40000000);
+ try expect(divCeil(i32, 0, -0x80000000) == 0);
+ try expect(divCeil(i32, -0x40000001, 0x40000000) == -1);
+ try expect(divCeil(i32, -0x80000000, 1) == -0x80000000);
+ try expect(divCeil(i32, 10, 12) == 1);
+ try expect(divCeil(i32, -14, 12) == -1);
+ try expect(divCeil(i32, -2, 12) == 0);
+
+ try expect(divCeil(u32, 5, 3) == 2);
+ try expect(divCeil(u32, 16, 4) == 4);
+ try expect(divCeil(u32, 0, 100) == 0);
+ try expect(divCeil(u32, maxInt(u32) - 1, 100) == 42949673);
+
+ try expect(divCeil(i64, 5, 3) == 2);
+ try expect(divCeil(i64, -5, 3) == -1);
+ try expect(divCeil(i64, -0x80000000, -2) == 0x40000000);
+ try expect(divCeil(i64, 0, -0x80000000) == 0);
+ try expect(divCeil(i64, -0x40000001, 0x40000000) == -1);
+ try expect(divCeil(i64, -0x80000000, 1) == -0x80000000);
+ try expect(divCeil(i64, 10, 12) == 1);
+ try expect(divCeil(i64, -14, 12) == -1);
+ try expect(divCeil(i64, -2, 12) == 0);
+
+ try expect(divCeil(u64, 5, 3) == 2);
+ try expect(divCeil(u64, 16, 4) == 4);
+ try expect(divCeil(u64, 0, 100) == 0);
+ try expect(divCeil(u64, maxInt(u64) - 1, 10000) == 1844674407370956);
+
try expect(divTrunc(i32, 5, 3) == 1);
try expect(divTrunc(i32, -5, 3) == -1);
try expect(divTrunc(i32, 9, -10) == 0);
@@ -531,6 +561,24 @@ fn testIntDivision() !void {
try expect(
1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
);
+ try expect(
+ @divFloor(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -3,
+ );
+ try expect(
+ @divFloor(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -3,
+ );
+ try expect(
+ @divFloor(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
+ );
+ try expect(
+ @divCeil(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
+ );
+ try expect(
+ @divCeil(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
+ );
+ try expect(
+ @divCeil(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 3,
+ );
try expect(
@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
);
@@ -559,6 +607,13 @@ fn testFloatDivision() !void {
try expect(divFloor(f16, -43.0, 12.0) == -4.0);
try expect(divFloor(f64, -90.0, -9.0) == 10.0);
+ try expect(divCeil(f32, 5.0, 3.0) == 2.0);
+ try expect(divCeil(f32, -5.0, 3.0) == -1.0);
+ try expect(divCeil(f32, 56.0, 9.0) == 7.0);
+ try expect(divCeil(f32, 1053.0, -41.0) == -25.0);
+ try expect(divCeil(f16, -43.0, 12.0) == -3.0);
+ try expect(divCeil(f64, -90.0, -9.0) == 10.0);
+
try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
@@ -607,6 +662,8 @@ fn testDivisionFP16() !void {
try expect(divFloor(f16, 5.0, 3.0) == 1.0);
try expect(divFloor(f16, -5.0, 3.0) == -2.0);
+ try expect(divCeil(f16, 5.0, 3.0) == 2.0);
+ try expect(divCeil(f16, -5.0, 3.0) == -1.0);
try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
@@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T {
fn divFloor(comptime T: type, a: T, b: T) T {
return @divFloor(a, b);
}
+fn divCeil(comptime T: type, a: T, b: T) T {
+ return @divCeil(a, b);
+}
fn divTrunc(comptime T: type, a: T, b: T) T {
return @divTrunc(a, b);
}
@@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" {
try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
}
+fn testDivCeil(comptime T: type, numerator: T, denominator: T, expected: T) !void {
+ try expect(@divCeil(numerator, denominator) == expected);
+}
+
+test "@divCeil > 128 bits" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+
+ try testDivCeil(u140, 0, maxInt(u140), 0);
+ try testDivCeil(u140, maxInt(u140), maxInt(u140), 1);
+ try testDivCeil(u140, maxInt(u140), 2, maxInt(u140) / 2 + 1);
+ try testDivCeil(u140, (1 << 139) + 5, 1 << 70, (1 << 69) + 1);
+ try testDivCeil(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 2);
+ try testDivCeil(u200, 123, 1 << 100, 1);
+ try testDivCeil(u200, 1 << 120, 1 << 60, 1 << 60);
+ try testDivCeil(u200, maxInt(u200), 1 << 100, 1 << 100);
+
+ try testDivCeil(i140, 0, maxInt(i140), 0);
+ try testDivCeil(i140, maxInt(i140), maxInt(i140), 1);
+ try testDivCeil(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50));
+ try testDivCeil(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50));
+ try testDivCeil(i140, -((1 << 100) + 1), -(1 << 50), (1 << 50) + 1);
+ try testDivCeil(i200, -3, 2, -1);
+ try testDivCeil(i200, minInt(i200), 1, minInt(i200));
+ try testDivCeil(i200, minInt(i200), -2, 1 << 198);
+ try testDivCeil(i200, maxInt(i200), 2, 1 << 198);
+}
+
fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
try expect(@divTrunc(numerator, denominator) == expected);
}
diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig
index 06d5677fe7e392c5c8c12b50a17adcd30950c675..7d63b136fda14fca1358123c1546820a42f0dcf9 100644
--- a/test/behavior/vector.zig
+++ b/test/behavior/vector.zig
@@ -510,8 +510,37 @@ test "vector division operators" {
inline for (@as([4]T, d2), 0..) |v, i| {
try expect(@divFloor(x[i], y[i]) == v);
}
- const d3 = @divTrunc(x, y);
+ const d3 = @divCeil(x, y);
inline for (@as([4]T, d3), 0..) |v, i| {
+ try expect(@divCeil(x[i], y[i]) == v);
+ }
+ const d4 = @divTrunc(x, y);
+ inline for (@as([4]T, d4), 0..) |v, i| {
+ try expect(@divTrunc(x[i], y[i]) == v);
+ }
+ }
+
+ fn doTheTestDivNoExact(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {
+ const is_signed_int = switch (@typeInfo(T)) {
+ .int => |info| info.signedness == .signed,
+ else => false,
+ };
+ if (!is_signed_int) {
+ const d0 = x / y;
+ inline for (@as([4]T, d0), 0..) |v, i| {
+ try expect(x[i] / y[i] == v);
+ }
+ }
+ const d2 = @divFloor(x, y);
+ inline for (@as([4]T, d2), 0..) |v, i| {
+ try expect(@divFloor(x[i], y[i]) == v);
+ }
+ const d3 = @divCeil(x, y);
+ inline for (@as([4]T, d3), 0..) |v, i| {
+ try expect(@divCeil(x[i], y[i]) == v);
+ }
+ const d4 = @divTrunc(x, y);
+ inline for (@as([4]T, d4), 0..) |v, i| {
try expect(@divTrunc(x[i], y[i]) == v);
}
}
@@ -566,6 +595,9 @@ test "vector division operators" {
try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
+
+ try doTheTestDivNoExact(u64, [4]u64{ 4, 5, 6, 7 }, [4]u64{ 4, 4, 4, 4 });
+ try doTheTestDivNoExact(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 3, 3, -3, -3 });
}
};
@@ -1318,11 +1350,13 @@ test "zero divisor" {
const v2 = @divExact(zeros, ones);
const v3 = @divTrunc(zeros, ones);
const v4 = @divFloor(zeros, ones);
+ const v5 = @divCeil(zeros, ones);
_ = v1[0];
_ = v2[0];
_ = v3[0];
_ = v4[0];
+ _ = v5[0];
}
test "zero multiplicand" {
diff --git a/test/behavior/x86_64/binary.zig b/test/behavior/x86_64/binary.zig
index 45965adffb800aa0d84bcbc95b579a8e0df2a757..a1e827cbb58411013cd95873ff4f8abf713c4322 100644
--- a/test/behavior/x86_64/binary.zig
+++ b/test/behavior/x86_64/binary.zig
@@ -5279,6 +5279,27 @@ test divFloorOptimized {
try test_div_floor_optimized.testFloatVectors();
}
+inline fn divCeilUnoptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
+ return @divCeil(lhs, rhs);
+}
+test divCeilUnoptimized {
+ const test_div_ceil_unoptimized = binary(divCeilUnoptimized, .{ .compare = .approx_int });
+ try test_div_ceil_unoptimized.testInts();
+ try test_div_ceil_unoptimized.testIntVectors();
+ try test_div_ceil_unoptimized.testFloats();
+ try test_div_ceil_unoptimized.testFloatVectors();
+}
+
+inline fn divCeilOptimized(comptime Type: type, lhs: Type, rhs: Type) Type {
+ @setFloatMode(.optimized);
+ return @divCeil(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0)));
+}
+test divCeilOptimized {
+ const test_div_ceil_optimized = binary(divCeilOptimized, .{ .compare = .approx_int });
+ try test_div_ceil_optimized.testFloats();
+ try test_div_ceil_optimized.testFloatVectors();
+}
+
inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type {
return @rem(lhs, rhs);
}
diff --git a/test/cases/compile_errors/signed_integer_division.zig b/test/cases/compile_errors/signed_integer_division.zig
index 9e55835adfd2a67fdab96114c6c5423902fe5f69..02c386632bab31c2233fb7a4a6e85a7798b0d90b 100644
--- a/test/cases/compile_errors/signed_integer_division.zig
+++ b/test/cases/compile_errors/signed_integer_division.zig
@@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 {
// error
//
-// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact
+// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact