authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-06-14 23:20:56+03:30
committergravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-06-18 13:38:58+02:00
log7140d08334de7d3c0c1342a0c2eb8a8db638b5df
treec031195d03ff3ce11e65a7342eab968a64767d3f
parentf4c4daec863cf7394e141aefc5ad52e9c34f5979

spirv: int/bool fixes

- use logical ops for boolean bitwise instructions - normalize strange-int results in airArithOp, airReduce, and airIntCast Co-authored-by: Quint Daenen <quint@daenen.email>

10 files changed, 62 insertions(+), 45 deletions(-)

src/codegen/spirv/CodeGen.zig+51-20
...@@ -3016,9 +3016,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -3016,9 +3016,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
3016 .ptr_add => try cg.airPtrAdd(inst),3016 .ptr_add => try cg.airPtrAdd(inst),
3017 .ptr_sub => try cg.airPtrSub(inst),3017 .ptr_sub => try cg.airPtrSub(inst),
30183018
3019 .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd),3019 .bit_and => try cg.airBitwiseOp(inst, .bit_and),
3020 .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr),3020 .bit_or => try cg.airBitwiseOp(inst, .bit_or),
3021 .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor),3021 .xor => try cg.airBitwiseOp(inst, .xor),
30223022
3023 .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),3023 .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),
3024 .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),3024 .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),
...@@ -3145,6 +3145,34 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id {...@@ -3145,6 +3145,34 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id {
3145 return try result.materialize(cg);3145 return try result.materialize(cg);
3146}3146}
31473147
3148const BitwiseOp = enum { bit_and, bit_or, xor };
3149
3150fn airBitwiseOp(cg: *CodeGen, inst: Air.Inst.Index, op: BitwiseOp) !?Id {
3151 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3152 const lhs = try cg.temporary(bin_op.lhs);
3153 const rhs = try cg.temporary(bin_op.rhs);
3154 const info = cg.arithmeticTypeInfo(lhs.ty);
3155
3156 // SPIR-V requires logical opcodes for booleans, bitwise opcodes for integers.
3157 const opcode: Opcode = switch (info.class) {
3158 .bool => switch (op) {
3159 .bit_and => .OpLogicalAnd,
3160 .bit_or => .OpLogicalOr,
3161 .xor => .OpLogicalNotEqual,
3162 },
3163 .integer, .strange_integer => switch (op) {
3164 .bit_and => .OpBitwiseAnd,
3165 .bit_or => .OpBitwiseOr,
3166 .xor => .OpBitwiseXor,
3167 },
3168 .float => unreachable,
3169 .composite_integer => unreachable, // TODO
3170 };
3171
3172 const result = try cg.buildBinary(opcode, lhs, rhs);
3173 return try result.materialize(cg);
3174}
3175
3148fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id {3176fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id {
3149 const zcu = cg.module.zcu;3177 const zcu = cg.module.zcu;
3150 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3178 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -3389,9 +3417,12 @@ fn airArithOp(...@@ -3389,9 +3417,12 @@ fn airArithOp(
3389 const info = cg.arithmeticTypeInfo(lhs.ty);3417 const info = cg.arithmeticTypeInfo(lhs.ty);
3390 const result = switch (info.class) {3418 const result = switch (info.class) {
3391 .composite_integer => unreachable, // TODO3419 .composite_integer => unreachable, // TODO
3392 .integer, .strange_integer => switch (info.signedness) {3420 .integer, .strange_integer => res: {
3393 .signed => try cg.buildBinary(sop, lhs, rhs),3421 const raw = switch (info.signedness) {
3394 .unsigned => try cg.buildBinary(uop, lhs, rhs),3422 .signed => try cg.buildBinary(sop, lhs, rhs),
3423 .unsigned => try cg.buildBinary(uop, lhs, rhs),
3424 };
3425 break :res try cg.normalize(raw, info);
3395 },3426 },
3396 .float => try cg.buildBinary(fop, lhs, rhs),3427 .float => try cg.buildBinary(fop, lhs, rhs),
3397 .bool => unreachable,3428 .bool => unreachable,
...@@ -3766,7 +3797,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3766,7 +3797,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3766 const operand = try cg.resolve(reduce.operand);3797 const operand = try cg.resolve(reduce.operand);
3767 const operand_ty = cg.typeOf(reduce.operand);3798 const operand_ty = cg.typeOf(reduce.operand);
3768 const scalar_ty = operand_ty.scalarType(zcu);3799 const scalar_ty = operand_ty.scalarType(zcu);
3769 const scalar_ty_id = try cg.resolveType(scalar_ty, .direct);
3770 const info = cg.arithmeticTypeInfo(operand_ty);3800 const info = cg.arithmeticTypeInfo(operand_ty);
3771 const len = operand_ty.vectorLen(zcu);3801 const len = operand_ty.vectorLen(zcu);
3772 const first = try cg.extractVectorComponent(scalar_ty, operand, 0);3802 const first = try cg.extractVectorComponent(scalar_ty, operand, 0);
...@@ -3792,8 +3822,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3792,8 +3822,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3792 else => {},3822 else => {},
3793 }3823 }
37943824
3795 var result_id = first;
3796
3797 const opcode: Opcode = switch (info.class) {3825 const opcode: Opcode = switch (info.class) {
3798 .bool => switch (reduce.operation) {3826 .bool => switch (reduce.operation) {
3799 .And => .OpLogicalAnd,3827 .And => .OpLogicalAnd,
...@@ -3817,19 +3845,18 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3817,19 +3845,18 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3817 .composite_integer => unreachable, // TODO3845 .composite_integer => unreachable, // TODO
3818 };3846 };
38193847
3820 for (1..len) |i| {3848 const needs_normalize = info.class == .strange_integer and
3821 const lhs = result_id;3849 (reduce.operation == .Add or reduce.operation == .Mul);
3822 const rhs = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i));
3823 result_id = cg.module.allocId();
38243850
3825 try cg.body.emitRaw(cg.module.gpa, opcode, 4);3851 var result: Temporary = .init(scalar_ty, first);
3826 cg.body.writeOperand(Id, scalar_ty_id);3852 for (1..len) |i| {
3827 cg.body.writeOperand(Id, result_id);3853 const rhs_id = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i));
3828 cg.body.writeOperand(Id, lhs);3854 const rhs: Temporary = .init(scalar_ty, rhs_id);
3829 cg.body.writeOperand(Id, rhs);3855 const stepped = try cg.buildBinary(opcode, result, rhs);
3856 result = if (needs_normalize) try cg.normalize(stepped, info) else stepped;
3830 }3857 }
38313858
3832 return result_id;3859 return try result.materialize(cg);
3833}3860}
38343861
3835fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) !?Id {3862fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
...@@ -4314,7 +4341,11 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4314,7 +4341,11 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4314 const dst_info = cg.arithmeticTypeInfo(dst_ty);4341 const dst_info = cg.arithmeticTypeInfo(dst_ty);
43154342
4316 if (src_info.backing_bits == dst_info.backing_bits) {4343 if (src_info.backing_bits == dst_info.backing_bits) {
4317 return try src.materialize(cg);4344 const result = if (dst_info.bits < src_info.bits)
4345 try cg.normalize(src.pun(dst_ty), dst_info)
4346 else
4347 src.pun(dst_ty);
4348 return try result.materialize(cg);
4318 }4349 }
43194350
4320 const converted = try cg.buildConvert(dst_ty, src);4351 const converted = try cg.buildConvert(dst_ty, src);
test/behavior/bool.zig-1
...@@ -10,7 +10,6 @@ test "bool literals" {...@@ -10,7 +10,6 @@ test "bool literals" {
1010
11test "cast bool to int" {11test "cast bool to int" {
12 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;12 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1413
15 const t = true;14 const t = true;
16 const f = false;15 const f = false;
test/behavior/cast_int.zig-2
...@@ -19,7 +19,6 @@ test "@intCast i32 to u7" {...@@ -19,7 +19,6 @@ test "@intCast i32 to u7" {
19}19}
2020
21test "coerce i8 to i32 and @intCast back" {21test "coerce i8 to i32 and @intCast back" {
22 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO22 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2524
...@@ -35,7 +34,6 @@ test "coerce i8 to i32 and @intCast back" {...@@ -35,7 +34,6 @@ test "coerce i8 to i32 and @intCast back" {
35}34}
3635
37test "coerce non byte-sized integers accross 32bits boundary" {36test "coerce non byte-sized integers accross 32bits boundary" {
38 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO37 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
4038
41 {39 {
test/behavior/duplicated_test_names.zig-1
...@@ -15,7 +15,6 @@ comptime {...@@ -15,7 +15,6 @@ comptime {
15test "thingy" {}15test "thingy" {}
1616
17test thingy {17test thingy {
18 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
19 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;18 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2019
21 if (thingy(1, 2) != 3) unreachable;20 if (thingy(1, 2) != 3) unreachable;
test/behavior/hasdecl.zig-2
...@@ -13,7 +13,6 @@ const Bar = struct {...@@ -13,7 +13,6 @@ const Bar = struct {
1313
14test "@hasDecl" {14test "@hasDecl" {
15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1716
18 try expect(@hasDecl(Foo, "public_thing"));17 try expect(@hasDecl(Foo, "public_thing"));
19 try expect(!@hasDecl(Foo, "private_thing"));18 try expect(!@hasDecl(Foo, "private_thing"));
...@@ -26,7 +25,6 @@ test "@hasDecl" {...@@ -26,7 +25,6 @@ test "@hasDecl" {
2625
27test "@hasDecl using a sliced string literal" {26test "@hasDecl using a sliced string literal" {
28 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;27 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
29 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
3028
31 try expect(@hasDecl(@This(), "std") == true);29 try expect(@hasDecl(@This(), "std") == true);
32 try expect(@hasDecl(@This(), "std"[0..0]) == false);30 try expect(@hasDecl(@This(), "std"[0..0]) == false);
test/behavior/import.zig-3
...@@ -5,21 +5,18 @@ const expectEqual = std.testing.expectEqual;...@@ -5,21 +5,18 @@ const expectEqual = std.testing.expectEqual;
5const a_namespace = @import("import/a_namespace.zig");5const a_namespace = @import("import/a_namespace.zig");
66
7test "call fn via namespace lookup" {7test "call fn via namespace lookup" {
8 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
109
11 try expect(@as(i32, 1234) == a_namespace.foo());10 try expect(@as(i32, 1234) == a_namespace.foo());
12}11}
1312
14test "importing the same thing gives the same import" {13test "importing the same thing gives the same import" {
15 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1715
18 try expect(@import("std") == @import("std"));16 try expect(@import("std") == @import("std"));
19}17}
2018
21test "import empty file" {19test "import empty file" {
22 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;20 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2421
25 _ = @import("import/empty.zig");22 _ = @import("import/empty.zig");
test/behavior/ir_block_deps.zig+1-1
...@@ -19,8 +19,8 @@ fn getErrInt() anyerror!i32 {...@@ -19,8 +19,8 @@ fn getErrInt() anyerror!i32 {
1919
20test "ir block deps" {20test "ir block deps" {
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2424
25 try expect((foo(1) catch unreachable) == 0);25 try expect((foo(1) catch unreachable) == 0);
26 try expect((foo(2) catch unreachable) == 0);26 try expect((foo(2) catch unreachable) == 0);
test/behavior/namespace_depends_on_compile_var.zig-1
...@@ -3,7 +3,6 @@ const builtin = @import("builtin");...@@ -3,7 +3,6 @@ const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
44
5test "namespace depends on compile var" {5test "namespace depends on compile var" {
6 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
87
9 if (some_namespace.a_bool) {8 if (some_namespace.a_bool) {
test/behavior/pub_enum.zig-2
...@@ -3,7 +3,6 @@ const other = @import("pub_enum/other.zig");...@@ -3,7 +3,6 @@ const other = @import("pub_enum/other.zig");
3const expect = @import("std").testing.expect;3const expect = @import("std").testing.expect;
44
5test "pub enum" {5test "pub enum" {
6 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
87
9 try pubEnumTest(other.APubEnum.Two);8 try pubEnumTest(other.APubEnum.Two);
...@@ -13,7 +12,6 @@ fn pubEnumTest(foo: other.APubEnum) !void {...@@ -13,7 +12,6 @@ fn pubEnumTest(foo: other.APubEnum) !void {
13}12}
1413
15test "cast with imported symbol" {14test "cast with imported symbol" {
16 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1816
19 try expect(@as(other.size_t, 42) == 42);17 try expect(@as(other.size_t, 42) == 42);
test/behavior/wrapping_arithmetic.zig+10-12
...@@ -3,9 +3,9 @@ const builtin = @import("builtin");...@@ -3,9 +3,9 @@ const builtin = @import("builtin");
3const minInt = std.math.minInt;3const minInt = std.math.minInt;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const expect = std.testing.expect;5const expect = std.testing.expect;
6const skip128 = builtin.zig_backend == .stage2_spirv;
67
7test "wrapping add" {8test "wrapping add" {
8 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;9 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1010
11 const S = struct {11 const S = struct {
...@@ -14,14 +14,14 @@ test "wrapping add" {...@@ -14,14 +14,14 @@ test "wrapping add" {
14 try testWrapAdd(i8, -128, -128, 0);14 try testWrapAdd(i8, -128, -128, 0);
15 try testWrapAdd(i2, 1, 1, -2);15 try testWrapAdd(i2, 1, 1, -2);
16 try testWrapAdd(i64, maxInt(i64), 1, minInt(i64));16 try testWrapAdd(i64, maxInt(i64), 1, minInt(i64));
17 try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0);17 if (!skip128) try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0);
18 try testWrapAdd(i128, minInt(i128), maxInt(i128), -1);18 if (!skip128) try testWrapAdd(i128, minInt(i128), maxInt(i128), -1);
19 try testWrapAdd(i8, 127, 127, -2);19 try testWrapAdd(i8, 127, 127, -2);
20 try testWrapAdd(u8, 3, 10, 13);20 try testWrapAdd(u8, 3, 10, 13);
21 try testWrapAdd(u8, 255, 255, 254);21 try testWrapAdd(u8, 255, 255, 254);
22 try testWrapAdd(u2, 3, 2, 1);22 try testWrapAdd(u2, 3, 2, 1);
23 try testWrapAdd(u3, 7, 1, 0);23 try testWrapAdd(u3, 7, 1, 0);
24 try testWrapAdd(u128, maxInt(u128), 1, minInt(u128));24 if (!skip128) try testWrapAdd(u128, maxInt(u128), 1, minInt(u128));
25 }25 }
2626
27 fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {27 fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
...@@ -43,7 +43,6 @@ test "wrapping add" {...@@ -43,7 +43,6 @@ test "wrapping add" {
43}43}
4444
45test "wrapping subtraction" {45test "wrapping subtraction" {
46 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
47 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;46 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
4847
49 const S = struct {48 const S = struct {
...@@ -52,12 +51,12 @@ test "wrapping subtraction" {...@@ -52,12 +51,12 @@ test "wrapping subtraction" {
52 try testWrapSub(i8, -128, -128, 0);51 try testWrapSub(i8, -128, -128, 0);
53 try testWrapSub(i8, -1, 127, -128);52 try testWrapSub(i8, -1, 127, -128);
54 try testWrapSub(i64, minInt(i64), 1, maxInt(i64));53 try testWrapSub(i64, minInt(i64), 1, maxInt(i64));
55 try testWrapSub(i128, maxInt(i128), -1, minInt(i128));54 if (!skip128) try testWrapSub(i128, maxInt(i128), -1, minInt(i128));
56 try testWrapSub(i128, minInt(i128), -maxInt(i128), -1);55 if (!skip128) try testWrapSub(i128, minInt(i128), -maxInt(i128), -1);
57 try testWrapSub(u8, 10, 3, 7);56 try testWrapSub(u8, 10, 3, 7);
58 try testWrapSub(u8, 0, 255, 1);57 try testWrapSub(u8, 0, 255, 1);
59 try testWrapSub(u5, 0, 31, 1);58 try testWrapSub(u5, 0, 31, 1);
60 try testWrapSub(u128, 0, maxInt(u128), 1);59 if (!skip128) try testWrapSub(u128, 0, maxInt(u128), 1);
61 }60 }
6261
63 fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {62 fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
...@@ -79,7 +78,6 @@ test "wrapping subtraction" {...@@ -79,7 +78,6 @@ test "wrapping subtraction" {
79}78}
8079
81test "wrapping multiplication" {80test "wrapping multiplication" {
82 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;81 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
8482
85 const S = struct {83 const S = struct {
...@@ -90,11 +88,11 @@ test "wrapping multiplication" {...@@ -90,11 +88,11 @@ test "wrapping multiplication" {
90 try testWrapMul(i8, -128, -128, 0);88 try testWrapMul(i8, -128, -128, 0);
91 try testWrapMul(i8, maxInt(i8), maxInt(i8), 1);89 try testWrapMul(i8, maxInt(i8), maxInt(i8), 1);
92 try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1);90 try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1);
93 try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1);91 if (!skip128) try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1);
94 try testWrapMul(i128, minInt(i128), -1, minInt(i128));92 if (!skip128) try testWrapMul(i128, minInt(i128), -1, minInt(i128));
95 try testWrapMul(u8, 10, 3, 30);93 try testWrapMul(u8, 10, 3, 30);
96 try testWrapMul(u8, 2, 255, 254);94 try testWrapMul(u8, 2, 255, 254);
97 try testWrapMul(u128, maxInt(u128), maxInt(u128), 1);95 if (!skip128) try testWrapMul(u128, maxInt(u128), maxInt(u128), 1);
98 }96 }
9997
100 fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {98 fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {