| ... | @@ -1,58 +1,32 @@ | ... | @@ -1,58 +1,32 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const mem = std.mem; | | |
| 4 | const expectEqual = std.testing.expectEqual; | | |
| 5 | const Vector = std.meta.Vector; | | |
| 6 | const minInt = std.math.minInt; | 3 | const minInt = std.math.minInt; |
| 7 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 8 | | 5 | const expect = std.testing.expect; |
| 9 | const Op = enum { add, sub, mul, shl }; | | |
| 10 | fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void { | | |
| 11 | const a = test_data[0]; | | |
| 12 | const b = test_data[1]; | | |
| 13 | const expected = test_data[2]; | | |
| 14 | { | | |
| 15 | const actual = switch (op) { | | |
| 16 | .add => a +| b, | | |
| 17 | .sub => a -| b, | | |
| 18 | .mul => a *| b, | | |
| 19 | .shl => a <<| b, | | |
| 20 | }; | | |
| 21 | try expectEqual(expected, actual); | | |
| 22 | } | | |
| 23 | { | | |
| 24 | var actual = a; | | |
| 25 | switch (op) { | | |
| 26 | .add => actual +|= b, | | |
| 27 | .sub => actual -|= b, | | |
| 28 | .mul => actual *|= b, | | |
| 29 | .shl => actual <<|= b, | | |
| 30 | } | | |
| 31 | try expectEqual(expected, actual); | | |
| 32 | } | | |
| 33 | } | | |
| 34 | | 6 | |
| 35 | test "saturating add" { | 7 | test "saturating add" { |
| 36 | const S = struct { | 8 | const S = struct { |
| 37 | fn doTheTest() !void { | 9 | fn doTheTest() !void { |
| 38 | // .{a, b, expected a+b} | 10 | try testSatAdd(i8, -3, 10, 7); |
| 39 | try testSaturatingOp(.add, i8, .{ -3, 10, 7 }); | 11 | try testSatAdd(i8, -128, -128, -128); |
| 40 | try testSaturatingOp(.add, i8, .{ -128, -128, -128 }); | 12 | try testSatAdd(i2, 1, 1, 1); |
| 41 | try testSaturatingOp(.add, i2, .{ 1, 1, 1 }); | 13 | try testSatAdd(i64, maxInt(i64), 1, maxInt(i64)); |
| 42 | try testSaturatingOp(.add, i64, .{ maxInt(i64), 1, maxInt(i64) }); | 14 | try testSatAdd(i128, maxInt(i128), -maxInt(i128), 0); |
| 43 | try testSaturatingOp(.add, i128, .{ maxInt(i128), -maxInt(i128), 0 }); | 15 | try testSatAdd(i128, minInt(i128), maxInt(i128), -1); |
| 44 | try testSaturatingOp(.add, i128, .{ minInt(i128), maxInt(i128), -1 }); | 16 | try testSatAdd(i8, 127, 127, 127); |
| 45 | try testSaturatingOp(.add, i8, .{ 127, 127, 127 }); | 17 | try testSatAdd(u8, 3, 10, 13); |
| 46 | try testSaturatingOp(.add, u8, .{ 3, 10, 13 }); | 18 | try testSatAdd(u8, 255, 255, 255); |
| 47 | try testSaturatingOp(.add, u8, .{ 255, 255, 255 }); | 19 | try testSatAdd(u2, 3, 2, 3); |
| 48 | try testSaturatingOp(.add, u2, .{ 3, 2, 3 }); | 20 | try testSatAdd(u3, 7, 1, 7); |
| 49 | try testSaturatingOp(.add, u3, .{ 7, 1, 7 }); | 21 | try testSatAdd(u128, maxInt(u128), 1, maxInt(u128)); |
| 50 | try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) }); | 22 | } |
| | 23 | |
| | 24 | fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| | 25 | try expect((lhs +| rhs) == expected); |
| 51 | | 26 | |
| 52 | const u8x3 = std.meta.Vector(3, u8); | 27 | var x = lhs; |
| 53 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 })); | 28 | x +|= rhs; |
| 54 | const i8x3 = std.meta.Vector(3, i8); | 29 | try expect(x == expected); |
| 55 | try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 })); | | |
| 56 | } | 30 | } |
| 57 | }; | 31 | }; |
| 58 | try S.doTheTest(); | 32 | try S.doTheTest(); |
| ... | @@ -62,20 +36,24 @@ test "saturating add" { | ... | @@ -62,20 +36,24 @@ test "saturating add" { |
| 62 | test "saturating subtraction" { | 36 | test "saturating subtraction" { |
| 63 | const S = struct { | 37 | const S = struct { |
| 64 | fn doTheTest() !void { | 38 | fn doTheTest() !void { |
| 65 | // .{a, b, expected a-b} | 39 | try testSatSub(i8, -3, 10, -13); |
| 66 | try testSaturatingOp(.sub, i8, .{ -3, 10, -13 }); | 40 | try testSatSub(i8, -128, -128, 0); |
| 67 | try testSaturatingOp(.sub, i8, .{ -128, -128, 0 }); | 41 | try testSatSub(i8, -1, 127, -128); |
| 68 | try testSaturatingOp(.sub, i8, .{ -1, 127, -128 }); | 42 | try testSatSub(i64, minInt(i64), 1, minInt(i64)); |
| 69 | try testSaturatingOp(.sub, i64, .{ minInt(i64), 1, minInt(i64) }); | 43 | try testSatSub(i128, maxInt(i128), -1, maxInt(i128)); |
| 70 | try testSaturatingOp(.sub, i128, .{ maxInt(i128), -1, maxInt(i128) }); | 44 | try testSatSub(i128, minInt(i128), -maxInt(i128), -1); |
| 71 | try testSaturatingOp(.sub, i128, .{ minInt(i128), -maxInt(i128), -1 }); | 45 | try testSatSub(u8, 10, 3, 7); |
| 72 | try testSaturatingOp(.sub, u8, .{ 10, 3, 7 }); | 46 | try testSatSub(u8, 0, 255, 0); |
| 73 | try testSaturatingOp(.sub, u8, .{ 0, 255, 0 }); | 47 | try testSatSub(u5, 0, 31, 0); |
| 74 | try testSaturatingOp(.sub, u5, .{ 0, 31, 0 }); | 48 | try testSatSub(u128, 0, maxInt(u128), 0); |
| 75 | try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 }); | 49 | } |
| | 50 | |
| | 51 | fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| | 52 | try expect((lhs -| rhs) == expected); |
| 76 | | 53 | |
| 77 | const u8x3 = std.meta.Vector(3, u8); | 54 | var x = lhs; |
| 78 | try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 })); | 55 | x -|= rhs; |
| | 56 | try expect(x == expected); |
| 79 | } | 57 | } |
| 80 | }; | 58 | }; |
| 81 | try S.doTheTest(); | 59 | try S.doTheTest(); |
| ... | @@ -84,26 +62,29 @@ test "saturating subtraction" { | ... | @@ -84,26 +62,29 @@ test "saturating subtraction" { |
| 84 | | 62 | |
| 85 | test "saturating multiplication" { | 63 | test "saturating multiplication" { |
| 86 | // TODO: once #9660 has been solved, remove this line | 64 | // TODO: once #9660 has been solved, remove this line |
| 87 | if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest; | 65 | if (builtin.stage2_arch == .wasm32) return error.SkipZigTest; |
| 88 | | 66 | |
| 89 | const S = struct { | 67 | const S = struct { |
| 90 | fn doTheTest() !void { | 68 | fn doTheTest() !void { |
| 91 | // .{a, b, expected a*b} | 69 | try testSatMul(i8, -3, 10, -30); |
| 92 | try testSaturatingOp(.mul, i8, .{ -3, 10, -30 }); | 70 | try testSatMul(i4, 2, 4, 7); |
| 93 | try testSaturatingOp(.mul, i4, .{ 2, 4, 7 }); | 71 | try testSatMul(i8, 2, 127, 127); |
| 94 | try testSaturatingOp(.mul, i8, .{ 2, 127, 127 }); | 72 | try testSatMul(i8, -128, -128, 127); |
| 95 | // TODO: uncomment these after #9643 has been solved - this should happen at 0.9.0/llvm-13 release | 73 | try testSatMul(i8, maxInt(i8), maxInt(i8), maxInt(i8)); |
| 96 | // try testSaturatingOp(.mul, i8, .{ -128, -128, 127 }); | 74 | try testSatMul(i16, maxInt(i16), -1, minInt(i16) + 1); |
| 97 | // try testSaturatingOp(.mul, i8, .{ maxInt(i8), maxInt(i8), maxInt(i8) }); | 75 | try testSatMul(i128, maxInt(i128), -1, minInt(i128) + 1); |
| 98 | try testSaturatingOp(.mul, i16, .{ maxInt(i16), -1, minInt(i16) + 1 }); | 76 | try testSatMul(i128, minInt(i128), -1, maxInt(i128)); |
| 99 | try testSaturatingOp(.mul, i128, .{ maxInt(i128), -1, minInt(i128) + 1 }); | 77 | try testSatMul(u8, 10, 3, 30); |
| 100 | try testSaturatingOp(.mul, i128, .{ minInt(i128), -1, maxInt(i128) }); | 78 | try testSatMul(u8, 2, 255, 255); |
| 101 | try testSaturatingOp(.mul, u8, .{ 10, 3, 30 }); | 79 | try testSatMul(u128, maxInt(u128), maxInt(u128), maxInt(u128)); |
| 102 | try testSaturatingOp(.mul, u8, .{ 2, 255, 255 }); | 80 | } |
| 103 | try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) }); | | |
| 104 | | 81 | |
| 105 | const u8x3 = std.meta.Vector(3, u8); | 82 | fn testSatMul(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| 106 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 })); | 83 | try expect((lhs *| rhs) == expected); |
| | 84 | |
| | 85 | var x = lhs; |
| | 86 | x *|= rhs; |
| | 87 | try expect(x == expected); |
| 107 | } | 88 | } |
| 108 | }; | 89 | }; |
| 109 | | 90 | |
| ... | @@ -114,21 +95,24 @@ test "saturating multiplication" { | ... | @@ -114,21 +95,24 @@ test "saturating multiplication" { |
| 114 | test "saturating shift-left" { | 95 | test "saturating shift-left" { |
| 115 | const S = struct { | 96 | const S = struct { |
| 116 | fn doTheTest() !void { | 97 | fn doTheTest() !void { |
| 117 | // .{a, b, expected a<<b} | 98 | try testSatShl(i8, 1, 2, 4); |
| 118 | try testSaturatingOp(.shl, i8, .{ 1, 2, 4 }); | 99 | try testSatShl(i8, 127, 1, 127); |
| 119 | try testSaturatingOp(.shl, i8, .{ 127, 1, 127 }); | 100 | try testSatShl(i8, -128, 1, -128); |
| 120 | try testSaturatingOp(.shl, i8, .{ -128, 1, -128 }); | | |
| 121 | // TODO: remove this check once #9668 is completed | 101 | // TODO: remove this check once #9668 is completed |
| 122 | if (std.builtin.target.cpu.arch != .wasm32) { | 102 | if (builtin.stage2_arch != .wasm32) { |
| 123 | // skip testing ints > 64 bits on wasm due to miscompilation / wasmtime ci error | 103 | // skip testing ints > 64 bits on wasm due to miscompilation / wasmtime ci error |
| 124 | try testSaturatingOp(.shl, i128, .{ maxInt(i128), 64, maxInt(i128) }); | 104 | try testSatShl(i128, maxInt(i128), 64, maxInt(i128)); |
| 125 | try testSaturatingOp(.shl, u128, .{ maxInt(u128), 64, maxInt(u128) }); | 105 | try testSatShl(u128, maxInt(u128), 64, maxInt(u128)); |
| 126 | } | 106 | } |
| 127 | try testSaturatingOp(.shl, u8, .{ 1, 2, 4 }); | 107 | try testSatShl(u8, 1, 2, 4); |
| 128 | try testSaturatingOp(.shl, u8, .{ 255, 1, 255 }); | 108 | try testSatShl(u8, 255, 1, 255); |
| | 109 | } |
| | 110 | fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| | 111 | try expect((lhs <<| rhs) == expected); |
| 129 | | 112 | |
| 130 | const u8x3 = std.meta.Vector(3, u8); | 113 | var x = lhs; |
| 131 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 })); | 114 | x <<|= rhs; |
| | 115 | try expect(x == expected); |
| 132 | } | 116 | } |
| 133 | }; | 117 | }; |
| 134 | try S.doTheTest(); | 118 | try S.doTheTest(); |