authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-18 17:54:44+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-18 17:54:44+02:00
logdc3176d6287955d2ecdaaa26f46f5577d5316d36
tree3bc1d7db6f6cc4555f076e2d506a3165c2f19ee9
parentd1bd9518f97abc9ab80795962b8e0dfd8e4d8768

stage2-wasm: enhance add/subWithOverflow

Added behavior tests to verify implementation

2 files changed, 157 insertions(+), 189 deletions(-)

src/arch/wasm/CodeGen.zig+45-105
...@@ -5980,6 +5980,26 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE...@@ -5980,6 +5980,26 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE
5980 return func.finishAir(inst, result, &.{ty_op.operand});5980 return func.finishAir(inst, result, &.{ty_op.operand});
5981}5981}
59825982
5983/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
5984fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {
5985 const mod = func.bin_file.base.comp.module.?;
5986 const int_info = ty.intInfo(mod);
5987 const wasm_bits = toWasmBits(int_info.bits) orelse {
5988 return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});
5989 };
5990 switch (wasm_bits) {
5991 32 => return .{ .imm32 = 0 },
5992 64 => return .{ .imm64 = 0 },
5993 128 => {
5994 const result = try func.allocStack(ty);
5995 try func.store(result, .{ .imm64 = 0 }, Type.u64, 0);
5996 try func.store(result, .{ .imm64 = 0 }, Type.u64, 8);
5997 return result;
5998 },
5999 else => unreachable,
6000 }
6001}
6002
5983fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6003fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
5984 assert(op == .add or op == .sub);6004 assert(op == .add or op == .sub);
5985 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6005 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -5987,124 +6007,44 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -5987,124 +6007,44 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
59876007
5988 const lhs = try func.resolveInst(extra.lhs);6008 const lhs = try func.resolveInst(extra.lhs);
5989 const rhs = try func.resolveInst(extra.rhs);6009 const rhs = try func.resolveInst(extra.rhs);
5990 const lhs_ty = func.typeOf(extra.lhs);6010 const ty = func.typeOf(extra.lhs);
5991 const pt = func.pt;6011 const pt = func.pt;
5992 const mod = pt.zcu;6012 const mod = pt.zcu;
59936013
5994 if (lhs_ty.zigTypeTag(mod) == .Vector) {6014 if (ty.zigTypeTag(mod) == .Vector) {
5995 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6015 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
5996 }6016 }
59976017
5998 const int_info = lhs_ty.intInfo(mod);6018 const int_info = ty.intInfo(mod);
5999 const is_signed = int_info.signedness == .signed;6019 const is_signed = int_info.signedness == .signed;
6000 const wasm_bits = toWasmBits(int_info.bits) orelse {6020 if (int_info.bits > 128) {
6001 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});6021 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
6002 };
6003
6004 if (wasm_bits == 128) {
6005 const result = try func.addSubWithOverflowBigInt(lhs, rhs, lhs_ty, func.typeOfIndex(inst), op);
6006 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6007 }6022 }
60086023
6009 const zero: WValue = switch (wasm_bits) {6024 const op_result = try func.wrapBinOp(lhs, rhs, ty, op);
6010 32 => .{ .imm32 = 0 },6025 var op_tmp = try op_result.toLocal(func, ty);
6011 64 => .{ .imm64 = 0 },6026 defer op_tmp.free(func);
6027
6028 const cmp_op: std.math.CompareOperator = switch (op) {
6029 .add => .lt,
6030 .sub => .gt,
6012 else => unreachable,6031 else => unreachable,
6013 };6032 };
6014
6015 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
6016 var result = if (wasm_bits != int_info.bits) blk: {
6017 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
6018 } else bin_op;
6019 defer result.free(func);
6020
6021 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
6022 const overflow_bit: WValue = if (is_signed) blk: {
6023 if (wasm_bits == int_info.bits) {
6024 const cmp_zero = try func.cmp(rhs, zero, lhs_ty, cmp_op);
6025 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
6026 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
6027 }
6028 break :blk try func.cmp(bin_op, bin_op, lhs_ty, .neq);
6029 } else if (wasm_bits == int_info.bits)
6030 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
6031 else
6032 try func.cmp(bin_op, result, lhs_ty, .neq);
6033 var overflow_local = try overflow_bit.toLocal(func, Type.u32);
6034 defer overflow_local.free(func);
6035
6036 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
6037 try func.store(result_ptr, result, lhs_ty, 0);
6038 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));
6039 try func.store(result_ptr, overflow_local, Type.u1, offset);
6040
6041 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
6042}
6043
6044fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
6045 const pt = func.pt;
6046 const mod = pt.zcu;
6047 assert(op == .add or op == .sub);
6048 const int_info = ty.intInfo(mod);
6049 const is_signed = int_info.signedness == .signed;
6050 if (int_info.bits != 128) {
6051 return func.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
6052 }
6053
6054 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
6055 defer lhs_high_bit.free(func);
6056 var lhs_low_bit = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6057 defer lhs_low_bit.free(func);
6058 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
6059 defer rhs_high_bit.free(func);
6060 var rhs_low_bit = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
6061 defer rhs_low_bit.free(func);
6062
6063 var low_op_res = try (try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(func, Type.u64);
6064 defer low_op_res.free(func);
6065 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
6066 defer high_op_res.free(func);
6067
6068 var lt = if (op == .add) blk: {
6069 break :blk try (try func.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
6070 } else if (op == .sub) blk: {
6071 break :blk try (try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
6072 } else unreachable;
6073 defer lt.free(func);
6074 var tmp = try (try func.intcast(lt, Type.u32, Type.u64)).toLocal(func, Type.u64);
6075 defer tmp.free(func);
6076 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
6077 defer tmp_op.free(func);
6078
6079 const overflow_bit = if (is_signed) blk: {6033 const overflow_bit = if (is_signed) blk: {
6080 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);6034 const zero = try intZeroValue(func, ty);
6081 const to_wrap = if (op == .add) wrap: {6035 const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt);
6082 break :wrap try func.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);6036 const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op);
6083 } else xor_low;6037 break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
6084 const xor_op = try func.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);6038 } else try func.cmp(op_tmp, lhs, ty, cmp_op);
6085 const wrap = try func.binOp(to_wrap, xor_op, Type.u64, .@"and");6039 var bit_tmp = try overflow_bit.toLocal(func, Type.u1);
6086 break :blk try func.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed6040 defer bit_tmp.free(func);
6087 } else blk: {6041
6088 const first_arg = if (op == .sub) arg: {6042 const result = try func.allocStack(func.typeOfIndex(inst));
6089 break :arg try func.cmp(high_op_res, lhs_high_bit, Type.u64, .gt);6043 const offset: u32 = @intCast(ty.abiSize(pt));
6090 } else lt;6044 try func.store(result, op_tmp, ty, 0);
60916045 try func.store(result, bit_tmp, Type.u1, offset);
6092 try func.emitWValue(first_arg);6046
6093 _ = try func.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);6047 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6094 _ = try func.cmp(tmp_op, lhs_low_bit, Type.u64, .eq);
6095 try func.addTag(.select);
6096
6097 break :blk .stack;
6098 };
6099 var overflow_local = try overflow_bit.toLocal(func, Type.u1);
6100 defer overflow_local.free(func);
6101
6102 const result_ptr = try func.allocStack(result_ty);
6103 try func.store(result_ptr, high_op_res, Type.u64, 0);
6104 try func.store(result_ptr, tmp_op, Type.u64, 8);
6105 try func.store(result_ptr, overflow_local, Type.u1, 16);
6106
6107 return result_ptr;
6108}6048}
61096049
6110fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6050fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
test/behavior/math.zig+112-84
...@@ -828,56 +828,72 @@ test "128-bit multiplication" {...@@ -828,56 +828,72 @@ test "128-bit multiplication" {
828 }828 }
829}829}
830830
831fn testAddWithOverflow(comptime T: type, a: T, b: T, add: T, bit: u1) !void {
832 const ov = @addWithOverflow(a, b);
833 try expect(ov[0] == add);
834 try expect(ov[1] == bit);
835}
836
831test "@addWithOverflow" {837test "@addWithOverflow" {
832 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO838 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
833 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO839 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
834 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO840 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
835 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;841 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
836842
837 {843 try testAddWithOverflow(u8, 250, 100, 94, 1);
838 var a: u8 = 250;844 try testAddWithOverflow(u8, 100, 150, 250, 0);
839 _ = &a;
840 const ov = @addWithOverflow(a, 100);
841 try expect(ov[0] == 94);
842 try expect(ov[1] == 1);
843 }
844 {
845 var a: u8 = 100;
846 _ = &a;
847 const ov = @addWithOverflow(a, 150);
848 try expect(ov[0] == 250);
849 try expect(ov[1] == 0);
850 }
851 {
852 var a: u8 = 200;
853 _ = &a;
854 var b: u8 = 99;
855 var ov = @addWithOverflow(a, b);
856 try expect(ov[0] == 43);
857 try expect(ov[1] == 1);
858 b = 55;
859 ov = @addWithOverflow(a, b);
860 try expect(ov[0] == 255);
861 try expect(ov[1] == 0);
862 }
863845
864 {846 try testAddWithOverflow(u8, 200, 99, 43, 1);
865 var a: usize = 6;847 try testAddWithOverflow(u8, 200, 55, 255, 0);
866 var b: usize = 6;
867 _ = .{ &a, &b };
868 const ov = @addWithOverflow(a, b);
869 try expect(ov[0] == 12);
870 try expect(ov[1] == 0);
871 }
872848
873 {849 try testAddWithOverflow(usize, 6, 6, 12, 0);
874 var a: isize = -6;850 try testAddWithOverflow(usize, maxInt(usize), 6, 5, 1);
875 var b: isize = -6;851
876 _ = .{ &a, &b };852 try testAddWithOverflow(isize, -6, -6, -12, 0);
877 const ov = @addWithOverflow(a, b);853 try testAddWithOverflow(isize, minInt(isize), -6, maxInt(isize) - 5, 1);
878 try expect(ov[0] == -12);854}
879 try expect(ov[1] == 0);855
880 }856test "@addWithOverflow > 64 bits" {
857 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
858 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
859 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
860 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
861
862 try testAddWithOverflow(u65, 4, 105, 109, 0);
863 try testAddWithOverflow(u65, 1000, 100, 1100, 0);
864 try testAddWithOverflow(u65, 100, maxInt(u65) - 99, 0, 1);
865 try testAddWithOverflow(u65, maxInt(u65), maxInt(u65), maxInt(u65) - 1, 1);
866 try testAddWithOverflow(u65, maxInt(u65) - 1, maxInt(u65), maxInt(u65) - 2, 1);
867 try testAddWithOverflow(u65, maxInt(u65), maxInt(u65) - 1, maxInt(u65) - 2, 1);
868
869 try testAddWithOverflow(u128, 4, 105, 109, 0);
870 try testAddWithOverflow(u128, 1000, 100, 1100, 0);
871 try testAddWithOverflow(u128, 100, maxInt(u128) - 99, 0, 1);
872 try testAddWithOverflow(u128, maxInt(u128), maxInt(u128), maxInt(u128) - 1, 1);
873 try testAddWithOverflow(u128, maxInt(u128) - 1, maxInt(u128), maxInt(u128) - 2, 1);
874 try testAddWithOverflow(u128, maxInt(u128), maxInt(u128) - 1, maxInt(u128) - 2, 1);
875
876 try testAddWithOverflow(i65, 4, -105, -101, 0);
877 try testAddWithOverflow(i65, 1000, 100, 1100, 0);
878 try testAddWithOverflow(i65, minInt(i65), 1, minInt(i65) + 1, 0);
879 try testAddWithOverflow(i65, maxInt(i65), minInt(i65), -1, 0);
880 try testAddWithOverflow(i65, minInt(i65), maxInt(i65), -1, 0);
881 try testAddWithOverflow(i65, maxInt(i65), -2, maxInt(i65) - 2, 0);
882 try testAddWithOverflow(i65, maxInt(i65), maxInt(i65), -2, 1);
883 try testAddWithOverflow(i65, minInt(i65), minInt(i65), 0, 1);
884 try testAddWithOverflow(i65, maxInt(i65) - 1, maxInt(i65), -3, 1);
885 try testAddWithOverflow(i65, maxInt(i65), maxInt(i65) - 1, -3, 1);
886
887 try testAddWithOverflow(i128, 4, -105, -101, 0);
888 try testAddWithOverflow(i128, 1000, 100, 1100, 0);
889 try testAddWithOverflow(i128, minInt(i128), 1, minInt(i128) + 1, 0);
890 try testAddWithOverflow(i128, maxInt(i128), minInt(i128), -1, 0);
891 try testAddWithOverflow(i128, minInt(i128), maxInt(i128), -1, 0);
892 try testAddWithOverflow(i128, maxInt(i128), -2, maxInt(i128) - 2, 0);
893 try testAddWithOverflow(i128, maxInt(i128), maxInt(i128), -2, 1);
894 try testAddWithOverflow(i128, minInt(i128), minInt(i128), 0, 1);
895 try testAddWithOverflow(i128, maxInt(i128) - 1, maxInt(i128), -3, 1);
896 try testAddWithOverflow(i128, maxInt(i128), maxInt(i128) - 1, -3, 1);
881}897}
882898
883test "small int addition" {899test "small int addition" {
...@@ -1265,56 +1281,68 @@ test "@mulWithOverflow u256" {...@@ -1265,56 +1281,68 @@ test "@mulWithOverflow u256" {
1265 }1281 }
1266}1282}
12671283
1284fn testSubWithOverflow(comptime T: type, a: T, b: T, sub: T, bit: u1) !void {
1285 const ov = @subWithOverflow(a, b);
1286 try expect(ov[0] == sub);
1287 try expect(ov[1] == bit);
1288}
1289
1268test "@subWithOverflow" {1290test "@subWithOverflow" {
1269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1291 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1270 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1292 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1271 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1293 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12721294
1273 {1295 try testSubWithOverflow(u8, 1, 2, 255, 1);
1274 var a: u8 = 1;1296 try testSubWithOverflow(u8, 1, 1, 0, 0);
1275 _ = &a;
1276 const ov = @subWithOverflow(a, 2);
1277 try expect(ov[0] == 255);
1278 try expect(ov[1] == 1);
1279 }
1280 {
1281 var a: u8 = 1;
1282 _ = &a;
1283 const ov = @subWithOverflow(a, 1);
1284 try expect(ov[0] == 0);
1285 try expect(ov[1] == 0);
1286 }
12871297
1288 {1298 try testSubWithOverflow(u16, 10000, 10002, 65534, 1);
1289 var a: u8 = 1;1299 try testSubWithOverflow(u16, 10000, 9999, 1, 0);
1290 _ = &a;
1291 var b: u8 = 2;
1292 var ov = @subWithOverflow(a, b);
1293 try expect(ov[0] == 255);
1294 try expect(ov[1] == 1);
1295 b = 1;
1296 ov = @subWithOverflow(a, b);
1297 try expect(ov[0] == 0);
1298 try expect(ov[1] == 0);
1299 }
13001300
1301 {1301 try testSubWithOverflow(usize, 6, 6, 0, 0);
1302 var a: usize = 6;1302 try testSubWithOverflow(usize, 6, 7, maxInt(usize), 1);
1303 var b: usize = 6;1303 try testSubWithOverflow(isize, -6, -6, 0, 0);
1304 _ = .{ &a, &b };1304 try testSubWithOverflow(isize, minInt(isize), 6, maxInt(isize) - 5, 1);
1305 const ov = @subWithOverflow(a, b);1305}
1306 try expect(ov[0] == 0);
1307 try expect(ov[1] == 0);
1308 }
13091306
1310 {1307test "@subWithOverflow > 64 bits" {
1311 var a: isize = -6;1308 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1312 var b: isize = -6;1309 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1313 _ = .{ &a, &b };1310 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1314 const ov = @subWithOverflow(a, b);1311 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1315 try expect(ov[0] == 0);1312
1316 try expect(ov[1] == 0);1313 try testSubWithOverflow(u65, 4, 105, maxInt(u65) - 100, 1);
1317 }1314 try testSubWithOverflow(u65, 1000, 100, 900, 0);
1315 try testSubWithOverflow(u65, maxInt(u65), maxInt(u65), 0, 0);
1316 try testSubWithOverflow(u65, maxInt(u65) - 1, maxInt(u65), maxInt(u65), 1);
1317 try testSubWithOverflow(u65, maxInt(u65), maxInt(u65) - 1, 1, 0);
1318
1319 try testSubWithOverflow(u128, 4, 105, maxInt(u128) - 100, 1);
1320 try testSubWithOverflow(u128, 1000, 100, 900, 0);
1321 try testSubWithOverflow(u128, maxInt(u128), maxInt(u128), 0, 0);
1322 try testSubWithOverflow(u128, maxInt(u128) - 1, maxInt(u128), maxInt(u128), 1);
1323 try testSubWithOverflow(u128, maxInt(u128), maxInt(u128) - 1, 1, 0);
1324
1325 try testSubWithOverflow(i65, 4, 105, -101, 0);
1326 try testSubWithOverflow(i65, 1000, 100, 900, 0);
1327 try testSubWithOverflow(i65, maxInt(i65), maxInt(i65), 0, 0);
1328 try testSubWithOverflow(i65, minInt(i65), minInt(i65), 0, 0);
1329 try testSubWithOverflow(i65, maxInt(i65) - 1, maxInt(i65), -1, 0);
1330 try testSubWithOverflow(i65, maxInt(i65), maxInt(i65) - 1, 1, 0);
1331 try testSubWithOverflow(i65, minInt(i65), 1, maxInt(i65), 1);
1332 try testSubWithOverflow(i65, maxInt(i65), minInt(i65), -1, 1);
1333 try testSubWithOverflow(i65, minInt(i65), maxInt(i65), 1, 1);
1334 try testSubWithOverflow(i65, maxInt(i65), -2, minInt(i65) + 1, 1);
1335
1336 try testSubWithOverflow(i128, 4, 105, -101, 0);
1337 try testSubWithOverflow(i128, 1000, 100, 900, 0);
1338 try testSubWithOverflow(i128, maxInt(i128), maxInt(i128), 0, 0);
1339 try testSubWithOverflow(i128, minInt(i128), minInt(i128), 0, 0);
1340 try testSubWithOverflow(i128, maxInt(i128) - 1, maxInt(i128), -1, 0);
1341 try testSubWithOverflow(i128, maxInt(i128), maxInt(i128) - 1, 1, 0);
1342 try testSubWithOverflow(i128, minInt(i128), 1, maxInt(i128), 1);
1343 try testSubWithOverflow(i128, maxInt(i128), minInt(i128), -1, 1);
1344 try testSubWithOverflow(i128, minInt(i128), maxInt(i128), 1, 1);
1345 try testSubWithOverflow(i128, maxInt(i128), -2, minInt(i128) + 1, 1);
1318}1346}
13191347
1320test "@shlWithOverflow" {1348test "@shlWithOverflow" {