Merge pull request #25201 from jacobly0/x86_64-addsat

x86_64: fix strictness edge cases in `+|`
This commit is contained in:
Andrew Kelley
2025-09-17 21:15:03 -07:00
committed by Alex Rønne Petersen
parent b8bf2780a0
commit 35de86906d
3 changed files with 498 additions and 95 deletions

View File

@@ -607,26 +607,212 @@ test "@intCast on vector" {
const S = struct {
fn doTheTest() !void {
// Upcast (implicit, equivalent to @intCast)
var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa };
_ = &up0;
const up1: @Vector(2, u16) = up0;
const up2: @Vector(2, u32) = up0;
const up3: @Vector(2, u64) = up0;
// Downcast (safety-checked)
var down0 = up3;
_ = &down0;
const down1: @Vector(2, u32) = @intCast(down0);
const down2: @Vector(2, u16) = @intCast(down0);
const down3: @Vector(2, u8) = @intCast(down0);
{
// Upcast (implicit, equivalent to @intCast)
var up0: @Vector(2, u8) = .{ 0x55, 0xaa };
_ = &up0;
const up1: @Vector(2, u16) = up0;
const up2: @Vector(2, u32) = up0;
const up3: @Vector(2, u64) = up0;
try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
try expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa }));
try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
try expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa }));
try expect(mem.eql(u32, &@as([2]u32, down1), &[2]u32{ 0x55, 0xaa }));
try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));
try expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa }));
{
// Downcast (safety-checked)
const down2: @Vector(2, u32) = @intCast(up3);
const down1: @Vector(2, u16) = @intCast(up3);
const down0: @Vector(2, u8) = @intCast(up3);
try expect(mem.eql(u32, &@as([2]u32, down2), &[2]u32{ 0x55, 0xaa }));
try expect(mem.eql(u16, &@as([2]u16, down1), &[2]u16{ 0x55, 0xaa }));
try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa }));
}
{
// Downcast (safety-checked)
const down1: @Vector(2, u16) = @intCast(up2);
const down0: @Vector(2, u8) = @intCast(up2);
try expect(mem.eql(u16, &@as([2]u16, down1), &[2]u16{ 0x55, 0xaa }));
try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa }));
}
{
// Downcast (safety-checked)
const down0: @Vector(2, u8) = @intCast(up1);
try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa }));
}
}
{
// Upcast (implicit, equivalent to @intCast)
var up0: @Vector(4, u8) = .{ 0x00, 0x55, 0xaa, 0xff };
_ = &up0;
const up1: @Vector(4, u16) = up0;
const up2: @Vector(4, u32) = up0;
const up3: @Vector(4, u64) = up0;
try expect(mem.eql(u16, &@as([4]u16, up1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff }));
try expect(mem.eql(u32, &@as([4]u32, up2), &[4]u32{ 0x00, 0x55, 0xaa, 0xff }));
try expect(mem.eql(u64, &@as([4]u64, up3), &[4]u64{ 0x00, 0x55, 0xaa, 0xff }));
{
// Downcast (safety-checked)
const down2: @Vector(4, u32) = @intCast(up3);
const down1: @Vector(4, u16) = @intCast(up3);
const down0: @Vector(4, u8) = @intCast(up3);
try expect(mem.eql(u32, &@as([4]u32, down2), &[4]u32{ 0x00, 0x55, 0xaa, 0xff }));
try expect(mem.eql(u16, &@as([4]u16, down1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff }));
try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff }));
}
{
// Downcast (safety-checked)
const down1: @Vector(4, u16) = @intCast(up2);
const down0: @Vector(4, u8) = @intCast(up2);
try expect(mem.eql(u16, &@as([4]u16, down1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff }));
try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff }));
}
{
// Downcast (safety-checked)
const down0: @Vector(4, u8) = @intCast(up1);
try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff }));
}
}
{
// Upcast (implicit, equivalent to @intCast)
var up0: @Vector(8, u8) = .{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
};
_ = &up0;
const up1: @Vector(8, u16) = up0;
const up2: @Vector(8, u32) = up0;
const up3: @Vector(8, u64) = up0;
try expect(mem.eql(u16, &@as([8]u16, up1), &[8]u16{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
try expect(mem.eql(u32, &@as([8]u32, up2), &[8]u32{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
try expect(mem.eql(u64, &@as([8]u64, up3), &[8]u64{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
{
// Downcast (safety-checked)
const down2: @Vector(8, u32) = @intCast(up3);
const down1: @Vector(8, u16) = @intCast(up3);
const down0: @Vector(8, u8) = @intCast(up3);
try expect(mem.eql(u32, &@as([8]u32, down2), &[8]u32{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
try expect(mem.eql(u16, &@as([8]u16, down1), &[8]u16{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
}
{
// Downcast (safety-checked)
const down1: @Vector(8, u16) = @intCast(up2);
const down0: @Vector(8, u8) = @intCast(up2);
try expect(mem.eql(u16, &@as([8]u16, down1), &[8]u16{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
}
{
// Downcast (safety-checked)
const down0: @Vector(8, u8) = @intCast(up1);
try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
}));
}
}
{
// Upcast (implicit, equivalent to @intCast)
var up0: @Vector(16, u8) = .{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
};
_ = &up0;
const up1: @Vector(16, u16) = up0;
const up2: @Vector(16, u32) = up0;
const up3: @Vector(16, u64) = up0;
try expect(mem.eql(u16, &@as([16]u16, up1), &[16]u16{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
try expect(mem.eql(u32, &@as([16]u32, up2), &[16]u32{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
try expect(mem.eql(u64, &@as([16]u64, up3), &[16]u64{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
{
// Downcast (safety-checked)
const down2: @Vector(16, u32) = @intCast(up3);
const down1: @Vector(16, u16) = @intCast(up3);
const down0: @Vector(16, u8) = @intCast(up3);
try expect(mem.eql(u32, &@as([16]u32, down2), &[16]u32{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
try expect(mem.eql(u16, &@as([16]u16, down1), &[16]u16{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
}
{
// Downcast (safety-checked)
const down1: @Vector(16, u16) = @intCast(up2);
const down0: @Vector(16, u8) = @intCast(up2);
try expect(mem.eql(u16, &@as([16]u16, down1), &[16]u16{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
}
{
// Downcast (safety-checked)
const down0: @Vector(16, u8) = @intCast(up1);
try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
}));
}
}
}
};

View File

@@ -968,6 +968,73 @@ test "saturating add" {
const expected = i8x3{ 127, 127, 127 };
try expect(mem.eql(i8, &@as([3]i8, expected), &@as([3]i8, result)));
}
try testElemType(i4);
try testElemType(u4);
try testElemType(i8);
try testElemType(u8);
try testElemType(i12);
try testElemType(u12);
try testElemType(i16);
try testElemType(u16);
try testElemType(i24);
try testElemType(u24);
try testElemType(i32);
try testElemType(u32);
try testElemType(i48);
try testElemType(u48);
try testElemType(i64);
try testElemType(u64);
}
fn testElemType(comptime Elem: type) !void {
const min = std.math.minInt(Elem);
const max = std.math.maxInt(Elem);
var v: @Vector(4, Elem) = .{ 0, 1, 0, 1 };
v +|= .{ 0, 0, 1, 1 };
try expect(v[0] == 0);
try expect(v[1] == 1);
try expect(v[2] == 1);
try expect(v[3] == 2);
v = .{ 0, max, 1, max };
v +|= .{ max, 0, max, 1 };
try expect(v[0] == max);
try expect(v[1] == max);
try expect(v[2] == max);
try expect(v[3] == max);
v = .{ 1, max - 1, max / 2, max };
v +|= .{ max - 1, 1, max / 2, max };
try expect(v[0] == max);
try expect(v[1] == max);
try expect(v[2] == max - 1);
try expect(v[3] == max);
switch (@typeInfo(Elem).int.signedness) {
.signed => {
v = .{ -1, -1, 0, -1 };
v +|= .{ 1, 0, -1, -1 };
try expect(v[0] == 0);
try expect(v[1] == -1);
try expect(v[2] == -1);
try expect(v[3] == -2);
v = .{ 0, min, -1, min };
v +|= .{ min, 0, min, -1 };
try expect(v[0] == min);
try expect(v[1] == min);
try expect(v[2] == min);
try expect(v[3] == min);
v = .{ -1, min + 1, min / 2, min };
v +|= .{ min + 1, -1, min / 2, min };
try expect(v[0] == min);
try expect(v[1] == min);
try expect(v[2] == min);
try expect(v[3] == min);
},
.unsigned => {},
}
}
};
try S.doTheTest();
@@ -984,14 +1051,83 @@ test "saturating subtraction" {
const S = struct {
fn doTheTest() !void {
// Broken out to avoid https://github.com/ziglang/zig/issues/11251
const u8x3 = @Vector(3, u8);
var lhs = u8x3{ 0, 0, 0 };
var rhs = u8x3{ 255, 255, 255 };
_ = .{ &lhs, &rhs };
const result = lhs -| rhs;
const expected = u8x3{ 0, 0, 0 };
try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result)));
{
// Broken out to avoid https://github.com/ziglang/zig/issues/11251
const u8x3 = @Vector(3, u8);
var lhs = u8x3{ 0, 0, 0 };
var rhs = u8x3{ 255, 255, 255 };
_ = .{ &lhs, &rhs };
const result = lhs -| rhs;
const expected = u8x3{ 0, 0, 0 };
try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result)));
}
try testElemType(i4);
try testElemType(u4);
try testElemType(i8);
try testElemType(u8);
try testElemType(i12);
try testElemType(u12);
try testElemType(i16);
try testElemType(u16);
try testElemType(i24);
try testElemType(u24);
try testElemType(i32);
try testElemType(u32);
try testElemType(i48);
try testElemType(u48);
try testElemType(i64);
try testElemType(u64);
}
fn testElemType(comptime Elem: type) !void {
const min = std.math.minInt(Elem);
const max = std.math.maxInt(Elem);
var v: @Vector(4, Elem) = .{ 0, 1, 0, 1 };
v -|= .{ 0, 0, 1, 1 };
try expect(v[0] == 0);
try expect(v[1] == 1);
try expect(v[2] == @max(min, -1));
try expect(v[3] == 0);
v = .{ 0, max, 1, max };
v -|= .{ max, 0, max, 1 };
try expect(v[0] == @min(min + 1, 0));
try expect(v[1] == max);
try expect(v[2] == @min(min + 2, 0));
try expect(v[3] == max - 1);
v = .{ 1, max - 1, max / 2, max };
v -|= .{ max - 1, 1, max / 2, max };
try expect(v[0] == @min(min + 3, 0));
try expect(v[1] == max - 2);
try expect(v[2] == 0);
try expect(v[3] == 0);
switch (@typeInfo(Elem).int.signedness) {
.signed => {
v = .{ -1, -1, 0, -1 };
v -|= .{ -1, 0, 1, 1 };
try expect(v[0] == 0);
try expect(v[1] == -1);
try expect(v[2] == -1);
try expect(v[3] == -2);
v = .{ 0, min, -1, min };
v -|= .{ max, 0, max, 1 };
try expect(v[0] == min + 1);
try expect(v[1] == min);
try expect(v[2] == min);
try expect(v[3] == min);
v = .{ -1, min + 1, min / 2, min };
v -|= .{ max, 1, max / 2, max };
try expect(v[0] == min);
try expect(v[1] == min);
try expect(v[2] == min + 1);
try expect(v[3] == min);
},
.unsigned => {},
}
}
};
try S.doTheTest();