Skip to content

x86_64: finish rewriting scalar overflow and saturate operations #23834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 18, 2025
4 changes: 3 additions & 1 deletion lib/std/debug/Dwarf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ pub const ExceptionFrameHeader = struct {
}
}

if (len == 0) return bad();
if (len == 0) return missing();
fbr.pos = left * entry_size;

// Read past the pc_begin field of the entry
Expand Down Expand Up @@ -460,6 +460,8 @@ pub const ExceptionFrameHeader = struct {
@sizeOf(usize),
native_endian,
);

if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return missing();
}
};

Expand Down
2 changes: 1 addition & 1 deletion lib/std/debug/SelfInfo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,7 @@ pub fn unwindFrameDwarf(
&cie,
&fde,
) catch |err| switch (err) {
error.InvalidDebugInfo => {
error.MissingDebugInfo => {
// `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map`
// and `fde_list`, and fall back to the binary search logic below.
try di.scanCieFdeInfo(allocator, base_address);
Expand Down
32 changes: 21 additions & 11 deletions lib/std/math.zig
Original file line number Diff line number Diff line change
Expand Up @@ -774,18 +774,15 @@ pub fn Log2IntCeil(comptime T: type) type {
/// Returns the smallest integer type that can hold both from and to.
pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type {
assert(from <= to);
if (from == 0 and to == 0) {
return u0;
}
const signedness: std.builtin.Signedness = if (from < 0) .signed else .unsigned;
const largest_positive_integer = @max(if (from < 0) (-from) - 1 else from, to); // two's complement
const base = log2(largest_positive_integer);
const upper = (1 << base) - 1;
var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1;
if (signedness == .signed) {
magnitude_bits += 1;
}
return std.meta.Int(signedness, magnitude_bits);
return @Type(.{ .int = .{
.signedness = signedness,
.bits = @as(u16, @intFromBool(signedness == .signed)) +
switch (if (from < 0) @max(@abs(from) - 1, to) else to) {
0 => 0,
else => |pos_max| 1 + log2(pos_max),
},
} });
}

test IntFittingRange {
Expand Down Expand Up @@ -1267,6 +1264,19 @@ pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
return @as(Log2Int(T), @intCast(@typeInfo(T).int.bits - 1 - @clz(x)));
}

test log2_int {
try testing.expect(log2_int(u32, 1) == 0);
try testing.expect(log2_int(u32, 2) == 1);
try testing.expect(log2_int(u32, 3) == 1);
try testing.expect(log2_int(u32, 4) == 2);
try testing.expect(log2_int(u32, 5) == 2);
try testing.expect(log2_int(u32, 6) == 2);
try testing.expect(log2_int(u32, 7) == 2);
try testing.expect(log2_int(u32, 8) == 3);
try testing.expect(log2_int(u32, 9) == 3);
try testing.expect(log2_int(u32, 10) == 3);
}

/// Return the log base 2 of integer value x, rounding up to the
/// nearest integer.
pub fn log2_int_ceil(comptime T: type, x: T) Log2IntCeil(T) {
Expand Down
8 changes: 4 additions & 4 deletions lib/std/math/big/int.zig
Original file line number Diff line number Diff line change
Expand Up @@ -415,12 +415,12 @@ pub const Mutable = struct {
// in the case that scalar happens to be small in magnitude within its type, but it
// is well worth being able to use the stack and not needing an allocator passed in.
// Note that Mutable.init still sets len to calcLimbLen(scalar) in any case.
const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
const limbs_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
.comptime_int => calcLimbLen(scalar),
.int => |info| calcTwosCompLimbCount(info.bits),
else => @compileError("expected scalar to be an int"),
};
var limbs: [limb_len]Limb = undefined;
var limbs: [limbs_len]Limb = undefined;
const operand = init(&limbs, scalar).toConst();
return add(r, a, operand);
}
Expand Down Expand Up @@ -2454,12 +2454,12 @@ pub const Const = struct {
// in the case that scalar happens to be small in magnitude within its type, but it
// is well worth being able to use the stack and not needing an allocator passed in.
// Note that Mutable.init still sets len to calcLimbLen(scalar) in any case.
const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
const limbs_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
.comptime_int => calcLimbLen(scalar),
.int => |info| calcTwosCompLimbCount(info.bits),
else => @compileError("expected scalar to be an int"),
};
var limbs: [limb_len]Limb = undefined;
var limbs: [limbs_len]Limb = undefined;
const rhs = Mutable.init(&limbs, scalar);
return order(lhs, rhs.toConst());
}
Expand Down
4 changes: 0 additions & 4 deletions lib/std/math/big/int_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2295,8 +2295,6 @@ test "sat shift-left signed simple positive" {
}

test "sat shift-left signed multi positive" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;

var x: SignedDoubleLimb = 1;
_ = &x;

Expand All @@ -2310,8 +2308,6 @@ test "sat shift-left signed multi positive" {
}

test "sat shift-left signed multi negative" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;

var x: SignedDoubleLimb = -1;
_ = &x;

Expand Down
21 changes: 11 additions & 10 deletions lib/std/math/log2.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ const expect = std.testing.expect;
/// - log2(nan) = nan
pub fn log2(x: anytype) @TypeOf(x) {
const T = @TypeOf(x);
switch (@typeInfo(T)) {
.comptime_float => {
return @as(comptime_float, @log2(x));
},
.float => return @log2(x),
return switch (@typeInfo(T)) {
.comptime_float, .float => @log2(x),
.comptime_int => comptime {
std.debug.assert(x > 0);
var x_shifted = x;
// First, calculate floorPowerOfTwo(x)
var shift_amt = 1;
Expand All @@ -34,12 +32,15 @@ pub fn log2(x: anytype) @TypeOf(x) {
}
return result;
},
.int => |IntType| switch (IntType.signedness) {
.signed => @compileError("log2 not implemented for signed integers"),
.unsigned => return math.log2_int(T, x),
},
.int => |int_info| math.log2_int(switch (int_info.signedness) {
.signed => @Type(.{ .int = .{
.signedness = .unsigned,
.bits = int_info.bits -| 1,
} }),
.unsigned => T,
}, @intCast(x)),
else => @compileError("log2 not implemented for " ++ @typeName(T)),
}
};
}

test log2 {
Expand Down
6 changes: 5 additions & 1 deletion lib/std/zig/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2142,7 +2142,7 @@ pub const Inst = struct {
ref_start_index = static_len,
_,

pub const static_len = 97;
pub const static_len = 101;

pub fn toRef(i: Index) Inst.Ref {
return @enumFromInt(@intFromEnum(Index.ref_start_index) + @intFromEnum(i));
Expand Down Expand Up @@ -2225,6 +2225,7 @@ pub const Inst = struct {
single_const_pointer_to_comptime_int_type,
slice_const_u8_type,
slice_const_u8_sentinel_0_type,
vector_8_i8_type,
vector_16_i8_type,
vector_32_i8_type,
vector_1_u8_type,
Expand All @@ -2233,8 +2234,10 @@ pub const Inst = struct {
vector_8_u8_type,
vector_16_u8_type,
vector_32_u8_type,
vector_4_i16_type,
vector_8_i16_type,
vector_16_i16_type,
vector_4_u16_type,
vector_8_u16_type,
vector_16_u16_type,
vector_4_i32_type,
Expand All @@ -2245,6 +2248,7 @@ pub const Inst = struct {
vector_4_i64_type,
vector_2_u64_type,
vector_4_u64_type,
vector_2_u128_type,
vector_4_f16_type,
vector_8_f16_type,
vector_2_f32_type,
Expand Down
31 changes: 20 additions & 11 deletions lib/zig.h
Original file line number Diff line number Diff line change
Expand Up @@ -1115,14 +1115,15 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t
\
static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
uint##w##_t res; \
if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \
return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \
if (rhs < bits && !zig_shlo_u##w(&res, lhs, rhs, bits)) return res; \
return lhs == INT##w##_C(0) ? INT##w##_C(0) : zig_maxInt_u(w, bits); \
} \
\
static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
static inline int##w##_t zig_shls_i##w(int##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
int##w##_t res; \
if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \
return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
if (rhs < bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \
return lhs == INT##w##_C(0) ? INT##w##_C(0) : \
lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
} \
\
static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
Expand Down Expand Up @@ -1851,15 +1852,23 @@ static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8

static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
zig_u128 res;
if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0))
return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs;
return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res;
if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits)) return res;
switch (zig_cmp_u128(lhs, zig_make_u128(0, 0))) {
case 0: return zig_make_u128(0, 0);
case 1: return zig_maxInt_u(128, bits);
default: zig_unreachable();
}
}

static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_u128 rhs, uint8_t bits) {
zig_i128 res;
if (zig_cmp_u128(zig_bitCast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res;
return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits)) return res;
switch (zig_cmp_i128(lhs, zig_make_i128(0, 0))) {
case -1: return zig_minInt_i(128, bits);
case 0: return zig_make_i128(0, 0);
case 1: return zig_maxInt_i(128, bits);
default: zig_unreachable();
}
}

static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
Expand Down
8 changes: 7 additions & 1 deletion src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ pub const Inst = struct {
/// it shifts out any bits that disagree with the resultant sign bit.
/// Uses the `bin_op` field.
shl_exact,
/// Saturating integer shift left. `<<|`
/// Saturating integer shift left. `<<|`. The result is the same type as the `lhs`.
/// The `rhs` must have the same vector shape as the `lhs`, but with any unsigned
/// integer as the scalar type.
/// Uses the `bin_op` field.
shl_sat,
/// Bitwise XOR. `^`
Expand Down Expand Up @@ -995,6 +997,7 @@ pub const Inst = struct {
single_const_pointer_to_comptime_int_type = @intFromEnum(InternPool.Index.single_const_pointer_to_comptime_int_type),
slice_const_u8_type = @intFromEnum(InternPool.Index.slice_const_u8_type),
slice_const_u8_sentinel_0_type = @intFromEnum(InternPool.Index.slice_const_u8_sentinel_0_type),
vector_8_i8_type = @intFromEnum(InternPool.Index.vector_8_i8_type),
vector_16_i8_type = @intFromEnum(InternPool.Index.vector_16_i8_type),
vector_32_i8_type = @intFromEnum(InternPool.Index.vector_32_i8_type),
vector_1_u8_type = @intFromEnum(InternPool.Index.vector_1_u8_type),
Expand All @@ -1003,8 +1006,10 @@ pub const Inst = struct {
vector_8_u8_type = @intFromEnum(InternPool.Index.vector_8_u8_type),
vector_16_u8_type = @intFromEnum(InternPool.Index.vector_16_u8_type),
vector_32_u8_type = @intFromEnum(InternPool.Index.vector_32_u8_type),
vector_4_i16_type = @intFromEnum(InternPool.Index.vector_4_i16_type),
vector_8_i16_type = @intFromEnum(InternPool.Index.vector_8_i16_type),
vector_16_i16_type = @intFromEnum(InternPool.Index.vector_16_i16_type),
vector_4_u16_type = @intFromEnum(InternPool.Index.vector_4_u16_type),
vector_8_u16_type = @intFromEnum(InternPool.Index.vector_8_u16_type),
vector_16_u16_type = @intFromEnum(InternPool.Index.vector_16_u16_type),
vector_4_i32_type = @intFromEnum(InternPool.Index.vector_4_i32_type),
Expand All @@ -1015,6 +1020,7 @@ pub const Inst = struct {
vector_4_i64_type = @intFromEnum(InternPool.Index.vector_4_i64_type),
vector_2_u64_type = @intFromEnum(InternPool.Index.vector_2_u64_type),
vector_4_u64_type = @intFromEnum(InternPool.Index.vector_4_u64_type),
vector_2_u128_type = @intFromEnum(InternPool.Index.vector_2_u128_type),
vector_4_f16_type = @intFromEnum(InternPool.Index.vector_4_f16_type),
vector_8_f16_type = @intFromEnum(InternPool.Index.vector_8_f16_type),
vector_2_f32_type = @intFromEnum(InternPool.Index.vector_2_f32_type),
Expand Down
20 changes: 20 additions & 0 deletions src/InternPool.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4572,6 +4572,7 @@ pub const Index = enum(u32) {
slice_const_u8_type,
slice_const_u8_sentinel_0_type,

vector_8_i8_type,
vector_16_i8_type,
vector_32_i8_type,
vector_1_u8_type,
Expand All @@ -4580,8 +4581,10 @@ pub const Index = enum(u32) {
vector_8_u8_type,
vector_16_u8_type,
vector_32_u8_type,
vector_4_i16_type,
vector_8_i16_type,
vector_16_i16_type,
vector_4_u16_type,
vector_8_u16_type,
vector_16_u16_type,
vector_4_i32_type,
Expand All @@ -4592,6 +4595,7 @@ pub const Index = enum(u32) {
vector_4_i64_type,
vector_2_u64_type,
vector_4_u64_type,
vector_2_u128_type,
vector_4_f16_type,
vector_8_f16_type,
vector_2_f32_type,
Expand Down Expand Up @@ -5090,6 +5094,8 @@ pub const static_keys = [_]Key{
},
} },

// @Vector(8, i8)
.{ .vector_type = .{ .len = 8, .child = .i8_type } },
// @Vector(16, i8)
.{ .vector_type = .{ .len = 16, .child = .i8_type } },
// @Vector(32, i8)
Expand All @@ -5106,10 +5112,14 @@ pub const static_keys = [_]Key{
.{ .vector_type = .{ .len = 16, .child = .u8_type } },
// @Vector(32, u8)
.{ .vector_type = .{ .len = 32, .child = .u8_type } },
// @Vector(4, i16)
.{ .vector_type = .{ .len = 4, .child = .i16_type } },
// @Vector(8, i16)
.{ .vector_type = .{ .len = 8, .child = .i16_type } },
// @Vector(16, i16)
.{ .vector_type = .{ .len = 16, .child = .i16_type } },
// @Vector(4, u16)
.{ .vector_type = .{ .len = 4, .child = .u16_type } },
// @Vector(8, u16)
.{ .vector_type = .{ .len = 8, .child = .u16_type } },
// @Vector(16, u16)
Expand All @@ -5130,6 +5140,8 @@ pub const static_keys = [_]Key{
.{ .vector_type = .{ .len = 2, .child = .u64_type } },
// @Vector(8, u64)
.{ .vector_type = .{ .len = 4, .child = .u64_type } },
// @Vector(2, u128)
.{ .vector_type = .{ .len = 2, .child = .u128_type } },
// @Vector(4, f16)
.{ .vector_type = .{ .len = 4, .child = .f16_type } },
// @Vector(8, f16)
Expand Down Expand Up @@ -11777,6 +11789,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.single_const_pointer_to_comptime_int_type,
.slice_const_u8_type,
.slice_const_u8_sentinel_0_type,
.vector_8_i8_type,
.vector_16_i8_type,
.vector_32_i8_type,
.vector_1_u8_type,
Expand All @@ -11785,8 +11798,10 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.vector_8_u8_type,
.vector_16_u8_type,
.vector_32_u8_type,
.vector_4_i16_type,
.vector_8_i16_type,
.vector_16_i16_type,
.vector_4_u16_type,
.vector_8_u16_type,
.vector_16_u16_type,
.vector_4_i32_type,
Expand All @@ -11797,6 +11812,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.vector_4_i64_type,
.vector_2_u64_type,
.vector_4_u64_type,
.vector_2_u128_type,
.vector_4_f16_type,
.vector_8_f16_type,
.vector_2_f32_type,
Expand Down Expand Up @@ -12121,6 +12137,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.slice_const_u8_sentinel_0_type,
=> .pointer,

.vector_8_i8_type,
.vector_16_i8_type,
.vector_32_i8_type,
.vector_1_u8_type,
Expand All @@ -12129,8 +12146,10 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.vector_8_u8_type,
.vector_16_u8_type,
.vector_32_u8_type,
.vector_4_i16_type,
.vector_8_i16_type,
.vector_16_i16_type,
.vector_4_u16_type,
.vector_8_u16_type,
.vector_16_u16_type,
.vector_4_i32_type,
Expand All @@ -12141,6 +12160,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.vector_4_i64_type,
.vector_2_u64_type,
.vector_4_u64_type,
.vector_2_u128_type,
.vector_4_f16_type,
.vector_8_f16_type,
.vector_2_f32_type,
Expand Down
Loading
Loading