authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-08 03:47:24+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-09 00:14:09+02:00
logf2a842db5caa7a91faade2636106327cc6707ad3
tree239acaad481e37a6c630006b4e4c7c99055b2ea6
parentaa7874657b3439134eb4cd8b65271fb9cc38fdad

stage2-wasm: sat ops


2 files changed, 121 insertions(+), 121 deletions(-)

src/codegen/wasm/CodeGen.zig+21-58
...@@ -3484,20 +3484,20 @@ fn intAddSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3484,20 +3484,20 @@ fn intAddSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3484 defer rhs_is_neg.free(cg);3484 defer rhs_is_neg.free(cg);
3485 const min_val = try cg.intMinValue(int_ty);3485 const min_val = try cg.intMinValue(int_ty);
34863486
3487 try cg.emitWValue(min_val);3487 try cg.lowerToStack(min_val);
3488 try cg.emitWValue(max_val);3488 try cg.lowerToStack(max_val);
3489 try cg.emitWValue(rhs_is_neg);3489 try cg.emitWValue(rhs_is_neg);
3490 try cg.addTag(.select);3490 try cg.addTag(.select);
34913491
3492 try cg.emitWValue(op_val);3492 try cg.lowerToStack(op_val);
3493 const overflow_cmp = try cg.intCmp(int_ty, .lt, op_val, lhs);3493 const overflow_cmp = try cg.intCmp(int_ty, .lt, op_val, lhs);
3494 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);3494 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);
3495 try cg.emitWValue(is_overflow);3495 try cg.emitWValue(is_overflow);
3496 try cg.addTag(.select);3496 try cg.addTag(.select);
3497 return .stack;3497 return .stack;
3498 } else {3498 } else {
3499 try cg.emitWValue(max_val);3499 try cg.lowerToStack(max_val);
3500 try cg.emitWValue(op_val);3500 try cg.lowerToStack(op_val);
35013501
3502 const is_overflow = try cg.intCmp(int_ty, .lt, op_val, lhs);3502 const is_overflow = try cg.intCmp(int_ty, .lt, op_val, lhs);
3503 try cg.emitWValue(is_overflow);3503 try cg.emitWValue(is_overflow);
...@@ -3518,12 +3518,12 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3518,12 +3518,12 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3518 const max_val = try cg.intMaxValue(int_ty);3518 const max_val = try cg.intMaxValue(int_ty);
3519 const min_val = try cg.intMinValue(int_ty);3519 const min_val = try cg.intMinValue(int_ty);
35203520
3521 try cg.emitWValue(max_val);3521 try cg.lowerToStack(max_val);
3522 try cg.emitWValue(min_val);3522 try cg.lowerToStack(min_val);
3523 try cg.emitWValue(rhs_is_neg);3523 try cg.emitWValue(rhs_is_neg);
3524 try cg.addTag(.select);3524 try cg.addTag(.select);
35253525
3526 try cg.emitWValue(op_val);3526 try cg.lowerToStack(op_val);
3527 const overflow_cmp = try cg.intCmp(int_ty, .gt, op_val, lhs);3527 const overflow_cmp = try cg.intCmp(int_ty, .gt, op_val, lhs);
3528 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);3528 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);
3529 try cg.emitWValue(is_overflow);3529 try cg.emitWValue(is_overflow);
...@@ -3532,8 +3532,8 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3532,8 +3532,8 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3532 } else {3532 } else {
3533 const zero = try cg.intZeroValue(int_ty);3533 const zero = try cg.intZeroValue(int_ty);
35343534
3535 try cg.emitWValue(zero);3535 try cg.lowerToStack(zero);
3536 try cg.emitWValue(op_val);3536 try cg.lowerToStack(op_val);
3537 const is_overflow = try cg.intCmp(int_ty, .lt, lhs, rhs);3537 const is_overflow = try cg.intCmp(int_ty, .lt, lhs, rhs);
3538 try cg.emitWValue(is_overflow);3538 try cg.emitWValue(is_overflow);
3539 try cg.addTag(.select);3539 try cg.addTag(.select);
...@@ -3542,43 +3542,6 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3542,43 +3542,6 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3542}3542}
35433543
3544fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {3544fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
3545 // Remove when > 128 int ops will be implemented in backend
3546 if (int_ty.bits == 128) {
3547 if (!int_ty.is_signed) {
3548 return cg.fail("TODO: mul_sat for unsigned 128-bit integers", .{});
3549 }
3550
3551 const overflow_ret = try cg.allocStack(Type.i32);
3552 const ret = try cg.callIntrinsic(
3553 .__muloti4,
3554 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
3555 Type.i128,
3556 &.{ lhs, rhs, overflow_ret },
3557 );
3558 try cg.lowerToStack(ret);
3559
3560 const xor = try cg.intXor(int_ty, lhs, rhs);
3561 const sign_v = try cg.intShr(int_ty, xor, .{ .imm32 = 127 });
3562
3563 // xor ~@as(u127, 0)
3564 try cg.emitWValue(sign_v);
3565 const lsb = try cg.load(sign_v, Type.u64, 0);
3566 _ = try cg.intXor(.u64, lsb, .{ .imm64 = ~@as(u64, 0) });
3567 try cg.store(.stack, .stack, Type.u64, sign_v.offset());
3568
3569 try cg.emitWValue(sign_v);
3570 const msb = try cg.load(sign_v, Type.u64, 8);
3571 _ = try cg.intXor(.u64, msb, .{ .imm64 = ~@as(u64, 0) >> 1 });
3572 try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8);
3573
3574 try cg.lowerToStack(sign_v);
3575 _ = try cg.load(overflow_ret, Type.i32, 0);
3576 try cg.addTag(.i32_eqz);
3577 try cg.addTag(.select);
3578
3579 return .stack;
3580 }
3581
3582 const ext_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = int_ty.bits * 2 };3545 const ext_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = int_ty.bits * 2 };
35833546
3584 const lhs_ext = try cg.intCast(ext_ty, int_ty, lhs);3547 const lhs_ext = try cg.intCast(ext_ty, int_ty, lhs);
...@@ -3594,10 +3557,10 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3594,10 +3557,10 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3594 if (int_ty.is_signed) {3557 if (int_ty.is_signed) {
3595 const min_val = try cg.intMinValue(int_ty);3558 const min_val = try cg.intMinValue(int_ty);
35963559
3597 try cg.emitWValue(min_val);3560 try cg.lowerToStack(min_val);
35983561
3599 try cg.emitWValue(max_val);3562 try cg.lowerToStack(max_val);
3600 try cg.emitWValue(op_val);3563 try cg.lowerToStack(op_val);
3601 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);3564 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);
3602 const ov_pos = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);3565 const ov_pos = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);
3603 try cg.emitWValue(ov_pos);3566 try cg.emitWValue(ov_pos);
...@@ -3605,12 +3568,12 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3605,12 +3568,12 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
36053568
3606 const min_ext = try cg.intCast(ext_ty, int_ty, min_val);3569 const min_ext = try cg.intCast(ext_ty, int_ty, min_val);
3607 const ov_neg = try cg.intCmp(ext_ty, .gt, min_ext, mul_ext);3570 const ov_neg = try cg.intCmp(ext_ty, .gt, min_ext, mul_ext);
3608 try cg.emitWValue(ov_neg);3571 try cg.lowerToStack(ov_neg);
3609 try cg.addTag(.select);3572 try cg.addTag(.select);
3610 return .stack;3573 return .stack;
3611 } else {3574 } else {
3612 try cg.emitWValue(max_val);3575 try cg.lowerToStack(max_val);
3613 try cg.emitWValue(op_val);3576 try cg.lowerToStack(op_val);
3614 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);3577 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);
3615 const is_overflow = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);3578 const is_overflow = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);
3616 try cg.emitWValue(is_overflow);3579 try cg.emitWValue(is_overflow);
...@@ -3633,20 +3596,20 @@ fn intShlSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError...@@ -3633,20 +3596,20 @@ fn intShlSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
3633 const zero = try cg.intZeroValue(int_ty);3596 const zero = try cg.intZeroValue(int_ty);
3634 const min_val = try cg.intMinValue(int_ty);3597 const min_val = try cg.intMinValue(int_ty);
36353598
3636 try cg.emitWValue(min_val);3599 try cg.lowerToStack(min_val);
3637 try cg.emitWValue(max_val);3600 try cg.lowerToStack(max_val);
3638 const lhs_is_neg = try cg.intCmp(int_ty, .lt, lhs, zero);3601 const lhs_is_neg = try cg.intCmp(int_ty, .lt, lhs, zero);
3639 try cg.emitWValue(lhs_is_neg);3602 try cg.emitWValue(lhs_is_neg);
3640 try cg.addTag(.select);3603 try cg.addTag(.select);
36413604
3642 try cg.emitWValue(op_val);3605 try cg.lowerToStack(op_val);
3643 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);3606 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);
3644 try cg.emitWValue(is_overflow);3607 try cg.emitWValue(is_overflow);
3645 try cg.addTag(.select);3608 try cg.addTag(.select);
3646 return .stack;3609 return .stack;
3647 } else {3610 } else {
3648 try cg.emitWValue(max_val);3611 try cg.lowerToStack(max_val);
3649 try cg.emitWValue(op_val);3612 try cg.lowerToStack(op_val);
3650 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);3613 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);
3651 try cg.emitWValue(is_overflow);3614 try cg.emitWValue(is_overflow);
3652 try cg.addTag(.select);3615 try cg.addTag(.select);
test/behavior/saturating_arithmetic.zig+100-63
...@@ -4,6 +4,14 @@ const minInt = std.math.minInt;...@@ -4,6 +4,14 @@ const minInt = std.math.minInt;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const expect = std.testing.expect;5const expect = std.testing.expect;
66
7fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
8 try expect((lhs +| rhs) == expected);
9
10 var x = lhs;
11 x +|= rhs;
12 try expect(x == expected);
13}
14
7test "saturating add" {15test "saturating add" {
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;16 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO17 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -28,32 +36,23 @@ test "saturating add" {...@@ -28,32 +36,23 @@ test "saturating add" {
28 try testSatAdd(u2, 3, 2, 3);36 try testSatAdd(u2, 3, 2, 3);
29 try testSatAdd(u3, 7, 1, 7);37 try testSatAdd(u3, 7, 1, 7);
30 }38 }
31
32 fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
33 try expect((lhs +| rhs) == expected);
34
35 var x = lhs;
36 x +|= rhs;
37 try expect(x == expected);
38 }
39 };39 };
4040
41 try S.doTheTest();41 try S.doTheTest();
42 try comptime S.doTheTest();42 try comptime S.doTheTest();
4343
44 try comptime S.testSatAdd(comptime_int, 0, 0, 0);44 try comptime testSatAdd(comptime_int, 0, 0, 0);
45 try comptime S.testSatAdd(comptime_int, -1, 1, 0);45 try comptime testSatAdd(comptime_int, -1, 1, 0);
46 try comptime S.testSatAdd(comptime_int, 3, 2, 5);46 try comptime testSatAdd(comptime_int, 3, 2, 5);
47 try comptime S.testSatAdd(comptime_int, -3, -2, -5);47 try comptime testSatAdd(comptime_int, -3, -2, -5);
48 try comptime S.testSatAdd(comptime_int, 3, -2, 1);48 try comptime testSatAdd(comptime_int, 3, -2, 1);
49 try comptime S.testSatAdd(comptime_int, -3, 2, -1);49 try comptime testSatAdd(comptime_int, -3, 2, -1);
50 try comptime S.testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512);50 try comptime testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512);
51 try comptime S.testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501);51 try comptime testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501);
52}52}
5353
54test "saturating add 128bit" {54test "saturating add 128bit" {
55 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;55 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
56 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO56 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO57 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;58 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
...@@ -65,19 +64,20 @@ test "saturating add 128bit" {...@@ -65,19 +64,20 @@ test "saturating add 128bit" {
65 try testSatAdd(i128, minInt(i128), maxInt(i128), -1);64 try testSatAdd(i128, minInt(i128), maxInt(i128), -1);
66 try testSatAdd(u128, maxInt(u128), 1, maxInt(u128));65 try testSatAdd(u128, maxInt(u128), 1, maxInt(u128));
67 }66 }
68 fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
69 try expect((lhs +| rhs) == expected);
70
71 var x = lhs;
72 x +|= rhs;
73 try expect(x == expected);
74 }
75 };67 };
7668
77 try S.doTheTest();69 try S.doTheTest();
78 try comptime S.doTheTest();70 try comptime S.doTheTest();
79}71}
8072
73fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
74 try expect((lhs -| rhs) == expected);
75
76 var x = lhs;
77 x -|= rhs;
78 try expect(x == expected);
79}
80
81test "saturating subtraction" {81test "saturating subtraction" {
82 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;82 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO83 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -101,32 +101,23 @@ test "saturating subtraction" {...@@ -101,32 +101,23 @@ test "saturating subtraction" {
101 try testSatSub(u8, 10, 3, 7);101 try testSatSub(u8, 10, 3, 7);
102 try testSatSub(u8, 0, 255, 0);102 try testSatSub(u8, 0, 255, 0);
103 }103 }
104
105 fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
106 try expect((lhs -| rhs) == expected);
107
108 var x = lhs;
109 x -|= rhs;
110 try expect(x == expected);
111 }
112 };104 };
113105
114 try S.doTheTest();106 try S.doTheTest();
115 try comptime S.doTheTest();107 try comptime S.doTheTest();
116108
117 try comptime S.testSatSub(comptime_int, 0, 0, 0);109 try comptime testSatSub(comptime_int, 0, 0, 0);
118 try comptime S.testSatSub(comptime_int, 1, 1, 0);110 try comptime testSatSub(comptime_int, 1, 1, 0);
119 try comptime S.testSatSub(comptime_int, 3, 2, 1);111 try comptime testSatSub(comptime_int, 3, 2, 1);
120 try comptime S.testSatSub(comptime_int, -3, -2, -1);112 try comptime testSatSub(comptime_int, -3, -2, -1);
121 try comptime S.testSatSub(comptime_int, 3, -2, 5);113 try comptime testSatSub(comptime_int, 3, -2, 5);
122 try comptime S.testSatSub(comptime_int, -3, 2, -5);114 try comptime testSatSub(comptime_int, -3, 2, -5);
123 try comptime S.testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602);115 try comptime testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602);
124 try comptime S.testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515);116 try comptime testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515);
125}117}
126118
127test "saturating subtraction 128bit" {119test "saturating subtraction 128bit" {
128 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;120 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
129 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
130 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO121 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
131 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO122 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
132 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;123 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
...@@ -138,14 +129,6 @@ test "saturating subtraction 128bit" {...@@ -138,14 +129,6 @@ test "saturating subtraction 128bit" {
138 try testSatSub(i128, minInt(i128), -maxInt(i128), -1);129 try testSatSub(i128, minInt(i128), -maxInt(i128), -1);
139 try testSatSub(u128, 0, maxInt(u128), 0);130 try testSatSub(u128, 0, maxInt(u128), 0);
140 }131 }
141
142 fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
143 try expect((lhs -| rhs) == expected);
144
145 var x = lhs;
146 x -|= rhs;
147 try expect(x == expected);
148 }
149 };132 };
150133
151 try S.doTheTest();134 try S.doTheTest();
...@@ -257,7 +240,6 @@ test "saturating mul i64, i128" {...@@ -257,7 +240,6 @@ test "saturating mul i64, i128" {
257240
258test "saturating multiplication" {241test "saturating multiplication" {
259 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;242 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
260 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
261 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
262 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO244 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
263 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;245 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
...@@ -294,6 +276,14 @@ test "saturating multiplication" {...@@ -294,6 +276,14 @@ test "saturating multiplication" {
294 try comptime testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);276 try comptime testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);
295}277}
296278
279fn testSatShl(comptime Lhs: type, lhs: Lhs, comptime Rhs: type, rhs: Rhs, expected: Lhs) !void {
280 try expect((lhs <<| rhs) == expected);
281
282 var x = lhs;
283 x <<|= rhs;
284 try expect(x == expected);
285}
286
297test "saturating shift-left" {287test "saturating shift-left" {
298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;288 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
299 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO289 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -320,23 +310,15 @@ test "saturating shift-left" {...@@ -320,23 +310,15 @@ test "saturating shift-left" {
320 try testSatShl(u8, 0, u4, 8, 0);310 try testSatShl(u8, 0, u4, 8, 0);
321 try testSatShl(u8, 3, u4, 8, maxInt(u8));311 try testSatShl(u8, 3, u4, 8, maxInt(u8));
322 }312 }
323
324 fn testSatShl(comptime Lhs: type, lhs: Lhs, comptime Rhs: type, rhs: Rhs, expected: Lhs) !void {
325 try expect((lhs <<| rhs) == expected);
326
327 var x = lhs;
328 x <<|= rhs;
329 try expect(x == expected);
330 }
331 };313 };
332314
333 try S.doTheTest();315 try S.doTheTest();
334 try comptime S.doTheTest();316 try comptime S.doTheTest();
335317
336 try comptime S.testSatShl(comptime_int, 0, comptime_int, 0, 0);318 try comptime testSatShl(comptime_int, 0, comptime_int, 0, 0);
337 try comptime S.testSatShl(comptime_int, 1, comptime_int, 2, 4);319 try comptime testSatShl(comptime_int, 1, comptime_int, 2, 4);
338 try comptime S.testSatShl(comptime_int, 13, comptime_int, 150, 18554220005177478453757717602843436772975706112);320 try comptime testSatShl(comptime_int, 13, comptime_int, 150, 18554220005177478453757717602843436772975706112);
339 try comptime S.testSatShl(comptime_int, -582769, comptime_int, 180, -893090893854873184096635538665358532628308979495815656505344);321 try comptime testSatShl(comptime_int, -582769, comptime_int, 180, -893090893854873184096635538665358532628308979495815656505344);
340}322}
341323
342test "saturating shift-left large rhs" {324test "saturating shift-left large rhs" {
...@@ -344,7 +326,6 @@ test "saturating shift-left large rhs" {...@@ -344,7 +326,6 @@ test "saturating shift-left large rhs" {
344 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;326 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
345 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;327 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
346 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;328 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
347 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
348329
349 {330 {
350 var lhs: u8 = undefined;331 var lhs: u8 = undefined;
...@@ -386,3 +367,59 @@ test "saturating shl uses the LHS type" {...@@ -386,3 +367,59 @@ test "saturating shl uses the LHS type" {
386367
387 try expect((1 <<| @as(u8, 200)) == 1606938044258990275541962092341162602522202993782792835301376);368 try expect((1 <<| @as(u8, 200)) == 1606938044258990275541962092341162602522202993782792835301376);
388}369}
370
371test "sat add > 128 bits" {
372 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
373
374 try testSatAdd(u140, 0, 0, 0);
375 try testSatAdd(u140, maxInt(u140), 1, maxInt(u140));
376 try testSatAdd(u200, 1 << 150, 1 << 20, (1 << 150) + (1 << 20));
377 try testSatAdd(u200, maxInt(u200), maxInt(u200), maxInt(u200));
378
379 try testSatAdd(i140, minInt(i140), -1, minInt(i140));
380 try testSatAdd(i140, maxInt(i140), 1, maxInt(i140));
381 try testSatAdd(i200, -1 << 150, 1 << 149, -1 << 149);
382 try testSatAdd(i200, maxInt(i200), maxInt(i200), maxInt(i200));
383}
384
385test "sat sub > 128 bits" {
386 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
387
388 try testSatSub(u140, 0, 1, 0);
389 try testSatSub(u140, maxInt(u140), maxInt(u140), 0);
390 try testSatSub(u200, 1 << 150, 1 << 20, (1 << 150) - (1 << 20));
391 try testSatSub(u200, maxInt(u200), 0, maxInt(u200));
392
393 try testSatSub(i140, minInt(i140), 1, minInt(i140));
394 try testSatSub(i140, maxInt(i140), -1, maxInt(i140));
395 try testSatSub(i200, -1 << 150, 1 << 149, -3 << 149);
396 try testSatSub(i200, 0, minInt(i200), maxInt(i200));
397}
398
399test "sat mul > 128 bits" {
400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
401
402 try testSatMul(u140, 0, maxInt(u140), 0);
403 try testSatMul(u140, 1 << 70, 1 << 69, 1 << 139);
404 try testSatMul(u200, maxInt(u200), 2, maxInt(u200));
405 try testSatMul(u200, maxInt(u200) - 1, 1, maxInt(u200) - 1);
406
407 try testSatMul(i140, -1, maxInt(i140), -maxInt(i140));
408 try testSatMul(i140, minInt(i140), -1, maxInt(i140));
409 try testSatMul(i200, 1 << 100, 1 << 99, maxInt(i200));
410 try testSatMul(i200, -1 << 150, 1 << 30, -1 << 180);
411}
412
413test "sat shl > 128 bits" {
414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
415
416 try testSatShl(u140, 0, u8, 17, 0);
417 try testSatShl(u140, 1 << 100, u8, 20, 1 << 120);
418 try testSatShl(u200, maxInt(u200), u8, 1, maxInt(u200));
419 try testSatShl(u200, 1 << 199, u8, 1, maxInt(u200));
420
421 try testSatShl(i140, 0, u8, 17, 0);
422 try testSatShl(i140, 1 << 100, u8, 38, 1 << 138);
423 try testSatShl(i140, 1 << 100, u8, 39, maxInt(i140));
424 try testSatShl(i200, minInt(i200) + 1, u8, 1, minInt(i200));
425}