authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-20 13:21:46+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-20 13:21:46+02:00
logf5dd6fb71a61b47c05089e81220d0c16b0d0c0fb
treecd73be129dffda6520a687fcde3bbe8d0c0ff57f
parent56d535dd24a16bcd827b54d151d6858a2c3ec3b5

stage2-wasm: @mulWithOverflow fixes + 128 bit signed


2 files changed, 185 insertions(+), 445 deletions(-)

src/arch/wasm/CodeGen.zig+116-157
...@@ -2681,41 +2681,41 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner...@@ -2681,41 +2681,41 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner
2681 .@"and", .@"or", .xor => {2681 .@"and", .@"or", .xor => {
2682 const result = try func.allocStack(ty);2682 const result = try func.allocStack(ty);
2683 try func.emitWValue(result);2683 try func.emitWValue(result);
2684 const lhs_low_bit = try func.load(lhs, Type.u64, 0);2684 const lhs_lsb = try func.load(lhs, Type.u64, 0);
2685 const rhs_low_bit = try func.load(rhs, Type.u64, 0);2685 const rhs_lsb = try func.load(rhs, Type.u64, 0);
2686 const op_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);2686 const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2687 try func.store(.stack, op_low_bit, Type.u64, result.offset());2687 try func.store(.stack, op_lsb, Type.u64, result.offset());
26882688
2689 try func.emitWValue(result);2689 try func.emitWValue(result);
2690 const lhs_high_bit = try func.load(lhs, Type.u64, 8);2690 const lhs_msb = try func.load(lhs, Type.u64, 8);
2691 const rhs_high_bit = try func.load(rhs, Type.u64, 8);2691 const rhs_msb = try func.load(rhs, Type.u64, 8);
2692 const op_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);2692 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
2693 try func.store(.stack, op_high_bit, Type.u64, result.offset() + 8);2693 try func.store(.stack, op_msb, Type.u64, result.offset() + 8);
2694 return result;2694 return result;
2695 },2695 },
2696 .add, .sub => {2696 .add, .sub => {
2697 const result = try func.allocStack(ty);2697 const result = try func.allocStack(ty);
2698 var lhs_low_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);2698 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2699 defer lhs_low_bit.free(func);2699 defer lhs_lsb.free(func);
2700 var rhs_low_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);2700 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2701 defer rhs_low_bit.free(func);2701 defer rhs_lsb.free(func);
2702 var low_op_res = try (try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(func, Type.u64);2702 var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64);
2703 defer low_op_res.free(func);2703 defer op_lsb.free(func);
27042704
2705 const lhs_high_bit = try func.load(lhs, Type.u64, 8);2705 const lhs_msb = try func.load(lhs, Type.u64, 8);
2706 const rhs_high_bit = try func.load(rhs, Type.u64, 8);2706 const rhs_msb = try func.load(rhs, Type.u64, 8);
2707 const high_op_res = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);2707 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
27082708
2709 const lt = if (op == .add) blk: {2709 const lt = if (op == .add) blk: {
2710 break :blk try func.cmp(low_op_res, rhs_low_bit, Type.u64, .lt);2710 break :blk try func.cmp(op_lsb, rhs_lsb, Type.u64, .lt);
2711 } else if (op == .sub) blk: {2711 } else if (op == .sub) blk: {
2712 break :blk try func.cmp(lhs_low_bit, rhs_low_bit, Type.u64, .lt);2712 break :blk try func.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt);
2713 } else unreachable;2713 } else unreachable;
2714 const tmp = try func.intcast(lt, Type.u32, Type.u64);2714 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2715 var tmp_op = try (try func.binOp(high_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);2715 var tmp_op = try (try func.binOp(op_msb, tmp, Type.u64, op)).toLocal(func, Type.u64);
2716 defer tmp_op.free(func);2716 defer tmp_op.free(func);
27172717
2718 try func.store(result, low_op_res, Type.u64, 0);2718 try func.store(result, op_lsb, Type.u64, 0);
2719 try func.store(result, tmp_op, Type.u64, 8);2719 try func.store(result, tmp_op, Type.u64, 8);
2720 return result;2720 return result;
2721 },2721 },
...@@ -4419,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4419,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4419 break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty);4419 break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty);
4420 } else operand;4420 } else operand;
44214421
4422 // store msb first4422 // store lsb first
4423 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());4423 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());
44244424
4425 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value4425 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value
4426 if (wanted.isSignedInt(mod)) {4426 if (wanted.isSignedInt(mod)) {
4427 try func.emitWValue(stack_ptr);4427 try func.emitWValue(stack_ptr);
4428 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);4428 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4429 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());4429 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
4430 } else {4430 } else {
4431 // Ensure memory of lsb is zero'd4431 // Ensure memory of msb is zero'd
4432 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);4432 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
4433 }4433 }
4434 return stack_ptr;4434 return stack_ptr;
...@@ -5529,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -5529,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
5529 return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(pt)});5529 return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(pt)});
5530 }5530 }
55315531
5532 var lhs_high_bit = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);5532 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5533 defer lhs_high_bit.free(func);5533 defer lhs_msb.free(func);
5534 var rhs_high_bit = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);5534 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5535 defer rhs_high_bit.free(func);5535 defer rhs_msb.free(func);
55365536
5537 switch (op) {5537 switch (op) {
5538 .eq, .neq => {5538 .eq, .neq => {
5539 const xor_high = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);5539 const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor);
5540 const lhs_low_bit = try func.load(lhs, Type.u64, 0);5540 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5541 const rhs_low_bit = try func.load(rhs, Type.u64, 0);5541 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5542 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);5542 const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);
5543 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");5543 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");
55445544
5545 switch (op) {5545 switch (op) {
...@@ -5551,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -5551,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
5551 else => {5551 else => {
5552 const ty = if (operand_ty.isSignedInt(mod)) Type.i64 else Type.u64;5552 const ty = if (operand_ty.isSignedInt(mod)) Type.i64 else Type.u64;
5553 // leave those value on top of the stack for '.select'5553 // leave those value on top of the stack for '.select'
5554 const lhs_low_bit = try func.load(lhs, Type.u64, 0);5554 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5555 const rhs_low_bit = try func.load(rhs, Type.u64, 0);5555 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5556 _ = try func.cmp(lhs_low_bit, rhs_low_bit, Type.u64, op);5556 _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5557 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, op);5557 _ = try func.cmp(lhs_msb, rhs_msb, ty, op);
5558 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);5558 _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq);
5559 try func.addTag(.select);5559 try func.addTag(.select);
5560 },5560 },
5561 }5561 }
...@@ -6106,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6106,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61066106
6107 const lhs = try func.resolveInst(extra.lhs);6107 const lhs = try func.resolveInst(extra.lhs);
6108 const rhs = try func.resolveInst(extra.rhs);6108 const rhs = try func.resolveInst(extra.rhs);
6109 const lhs_ty = func.typeOf(extra.lhs);6109 const ty = func.typeOf(extra.lhs);
6110 const pt = func.pt;6110 const pt = func.pt;
6111 const mod = pt.zcu;6111 const mod = pt.zcu;
61126112
6113 if (lhs_ty.zigTypeTag(mod) == .Vector) {6113 if (ty.zigTypeTag(mod) == .Vector) {
6114 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6114 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
6115 }6115 }
61166116
...@@ -6119,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6119,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6119 var overflow_bit = try func.ensureAllocLocal(Type.u1);6119 var overflow_bit = try func.ensureAllocLocal(Type.u1);
6120 defer overflow_bit.free(func);6120 defer overflow_bit.free(func);
61216121
6122 const int_info = lhs_ty.intInfo(mod);6122 const int_info = ty.intInfo(mod);
6123 const wasm_bits = toWasmBits(int_info.bits) orelse {6123 const wasm_bits = toWasmBits(int_info.bits) orelse {
6124 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});6124 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
6125 };6125 };
...@@ -6131,147 +6131,106 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6131,147 +6131,106 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6131 };6131 };
61326132
6133 // for 32 bit integers we upcast it to a 64bit integer6133 // for 32 bit integers we upcast it to a 64bit integer
6134 const bin_op = if (int_info.bits == 32) blk: {6134 const mul = if (wasm_bits == 32) blk: {
6135 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;6135 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
6136 const lhs_upcast = try func.intcast(lhs, lhs_ty, new_ty);6136 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6137 const rhs_upcast = try func.intcast(rhs, lhs_ty, new_ty);6137 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6138 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);6138 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6139 if (int_info.signedness == .unsigned) {6139 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6140 const shr = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);6140 const res_upcast = try func.intcast(res, ty, new_ty);
6141 const wrap = try func.intcast(shr, new_ty, lhs_ty);6141 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
6142 _ = try func.cmp(wrap, zero, lhs_ty, .neq);
6143 try func.addLabel(.local_set, overflow_bit.local.value);
6144 break :blk try func.intcast(bin_op, new_ty, lhs_ty);
6145 } else {
6146 const down_cast = try (try func.intcast(bin_op, new_ty, lhs_ty)).toLocal(func, lhs_ty);
6147 var shr = try (try func.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(func, lhs_ty);
6148 defer shr.free(func);
6149
6150 const shr_res = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
6151 const down_shr_res = try func.intcast(shr_res, new_ty, lhs_ty);
6152 _ = try func.cmp(down_shr_res, shr, lhs_ty, .neq);
6153 try func.addLabel(.local_set, overflow_bit.local.value);
6154 break :blk down_cast;
6155 }
6156 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
6157 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
6158 const mul_abs = try func.wrapOperand(bin_op, lhs_ty);
6159 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
6160 try func.addLabel(.local_set, overflow_bit.local.value);6142 try func.addLabel(.local_set, overflow_bit.local.value);
6161 break :blk try func.wrapOperand(bin_op, lhs_ty);6143 break :blk res;
6162 } else if (wasm_bits == 32) blk: {6144 } else if (wasm_bits == 64) blk: {
6163 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);6145 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;
6164 defer bin_op.free(func);6146 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6165 const shift_imm: WValue = if (wasm_bits == 32)6147 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6166 .{ .imm32 = int_info.bits }6148 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6167 else6149 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6168 .{ .imm64 = int_info.bits };6150 const res_upcast = try func.intcast(res, ty, new_ty);
6169 const shr = try func.binOp(bin_op, shift_imm, lhs_ty, .shr);6151 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
6170 _ = try func.cmp(shr, zero, lhs_ty, .neq);
6171 try func.addLabel(.local_set, overflow_bit.local.value);
6172 break :blk try func.wrapOperand(bin_op, lhs_ty);
6173 } else if (int_info.bits == 64 and int_info.signedness == .unsigned) blk: {
6174 const new_ty = Type.u128;
6175 var lhs_upcast = try (try func.intcast(lhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
6176 defer lhs_upcast.free(func);
6177 var rhs_upcast = try (try func.intcast(rhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
6178 defer rhs_upcast.free(func);
6179 const bin_op = try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul);
6180 const lsb = try func.load(bin_op, lhs_ty, 8);
6181 _ = try func.cmp(lsb, zero, lhs_ty, .neq);
6182 try func.addLabel(.local_set, overflow_bit.local.value);
6183
6184 break :blk try func.load(bin_op, lhs_ty, 0);
6185 } else if (int_info.bits == 64 and int_info.signedness == .signed) blk: {
6186 const shift_val: WValue = .{ .imm64 = 63 };
6187 var lhs_shifted = try (try func.binOp(lhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
6188 defer lhs_shifted.free(func);
6189 var rhs_shifted = try (try func.binOp(rhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
6190 defer rhs_shifted.free(func);
6191
6192 const bin_op = try func.callIntrinsic(
6193 "__multi3",
6194 &[_]InternPool.Index{.i64_type} ** 4,
6195 Type.i128,
6196 &.{ lhs, lhs_shifted, rhs, rhs_shifted },
6197 );
6198 const res = try func.allocLocal(lhs_ty);
6199 const msb = try func.load(bin_op, lhs_ty, 0);
6200 try func.addLabel(.local_tee, res.local.value);
6201 const msb_shifted = try func.binOp(msb, shift_val, lhs_ty, .shr);
6202 const lsb = try func.load(bin_op, lhs_ty, 8);
6203 _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq);
6204 try func.addLabel(.local_set, overflow_bit.local.value);6152 try func.addLabel(.local_set, overflow_bit.local.value);
6205 break :blk res;6153 break :blk res;
6206 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {6154 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
6207 var lhs_msb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);6155 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
6208 defer lhs_msb.free(func);
6209 var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6210 defer lhs_lsb.free(func);6156 defer lhs_lsb.free(func);
6211 var rhs_msb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);6157 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6212 defer rhs_msb.free(func);6158 defer lhs_msb.free(func);
6213 var rhs_lsb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);6159 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
6214 defer rhs_lsb.free(func);6160 defer rhs_lsb.free(func);
6161 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
6162 defer rhs_msb.free(func);
62156163
6216 const mul1 = try func.callIntrinsic(6164 const cross_1 = try func.callIntrinsic(
6217 "__multi3",6165 "__multi3",
6218 &[_]InternPool.Index{.i64_type} ** 4,6166 &[_]InternPool.Index{.i64_type} ** 4,
6219 Type.i128,6167 Type.i128,
6220 &.{ lhs_lsb, zero, rhs_msb, zero },6168 &.{ lhs_msb, zero, rhs_lsb, zero },
6221 );6169 );
6222 const mul2 = try func.callIntrinsic(6170 const cross_2 = try func.callIntrinsic(
6223 "__multi3",6171 "__multi3",
6224 &[_]InternPool.Index{.i64_type} ** 4,6172 &[_]InternPool.Index{.i64_type} ** 4,
6225 Type.i128,6173 Type.i128,
6226 &.{ rhs_lsb, zero, lhs_msb, zero },6174 &.{ rhs_msb, zero, lhs_lsb, zero },
6227 );6175 );
6228 const mul3 = try func.callIntrinsic(6176 const mul_lsb = try func.callIntrinsic(
6229 "__multi3",6177 "__multi3",
6230 &[_]InternPool.Index{.i64_type} ** 4,6178 &[_]InternPool.Index{.i64_type} ** 4,
6231 Type.i128,6179 Type.i128,
6232 &.{ lhs_msb, zero, rhs_msb, zero },6180 &.{ rhs_lsb, zero, lhs_lsb, zero },
6233 );6181 );
62346182
6235 const rhs_lsb_not_zero = try func.cmp(rhs_lsb, zero, Type.u64, .neq);6183 const rhs_msb_not_zero = try func.cmp(rhs_msb, zero, Type.u64, .neq);
6236 const lhs_lsb_not_zero = try func.cmp(lhs_lsb, zero, Type.u64, .neq);6184 const lhs_msb_not_zero = try func.cmp(lhs_msb, zero, Type.u64, .neq);
6237 const lsb_and = try func.binOp(rhs_lsb_not_zero, lhs_lsb_not_zero, Type.bool, .@"and");6185 const both_msb_not_zero = try func.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and");
6238 const mul1_lsb = try func.load(mul1, Type.u64, 8);6186 const cross_1_msb = try func.load(cross_1, Type.u64, 8);
6239 const mul1_lsb_not_zero = try func.cmp(mul1_lsb, zero, Type.u64, .neq);6187 const cross_1_msb_not_zero = try func.cmp(cross_1_msb, zero, Type.u64, .neq);
6240 const lsb_or1 = try func.binOp(lsb_and, mul1_lsb_not_zero, Type.bool, .@"or");6188 const cond_1 = try func.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or");
6241 const mul2_lsb = try func.load(mul2, Type.u64, 8);6189 const cross_2_msb = try func.load(cross_2, Type.u64, 8);
6242 const mul2_lsb_not_zero = try func.cmp(mul2_lsb, zero, Type.u64, .neq);6190 const cross_2_msb_not_zero = try func.cmp(cross_2_msb, zero, Type.u64, .neq);
6243 const lsb_or = try func.binOp(lsb_or1, mul2_lsb_not_zero, Type.bool, .@"or");6191 const cond_2 = try func.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or");
62446192
6245 const mul1_msb = try func.load(mul1, Type.u64, 0);6193 const cross_1_lsb = try func.load(cross_1, Type.u64, 0);
6246 const mul2_msb = try func.load(mul2, Type.u64, 0);6194 const cross_2_lsb = try func.load(cross_2, Type.u64, 0);
6247 const mul_add1 = try func.binOp(mul1_msb, mul2_msb, Type.u64, .add);6195 const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);
62486196
6249 var mul3_lsb = try (try func.load(mul3, Type.u64, 8)).toLocal(func, Type.u64);6197 var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64);
6250 defer mul3_lsb.free(func);6198 defer mul_lsb_msb.free(func);
6251 var mul_add2 = try (try func.binOp(mul_add1, mul3_lsb, Type.u64, .add)).toLocal(func, Type.u64);6199 var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64);
6252 defer mul_add2.free(func);6200 defer all_add.free(func);
6253 const mul_add_lt = try func.cmp(mul_add2, mul3_lsb, Type.u64, .lt);6201 const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt);
62546202
6255 // result for overflow bit6203 // result for overflow bit
6256 _ = try func.binOp(lsb_or, mul_add_lt, Type.bool, .@"or");6204 _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or");
6257 try func.addLabel(.local_set, overflow_bit.local.value);6205 try func.addLabel(.local_set, overflow_bit.local.value);
62586206
6259 const tmp_result = try func.allocStack(Type.u128);6207 const tmp_result = try func.allocStack(Type.u128);
6260 try func.emitWValue(tmp_result);6208 try func.emitWValue(tmp_result);
6261 const mul3_msb = try func.load(mul3, Type.u64, 0);6209 const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0);
6262 try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset());6210 try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6263 try func.store(tmp_result, mul_add2, Type.u64, 8);6211 try func.store(tmp_result, all_add, Type.u64, 8);
6264 break :blk tmp_result;6212 break :blk tmp_result;
6265 } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{});6213 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {
6266 var bin_op_local = try bin_op.toLocal(func, lhs_ty);6214 const overflow_ret = try func.allocStack(Type.i32);
6215 const res = try func.callIntrinsic(
6216 "__muloti4",
6217 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
6218 Type.i128,
6219 &.{ lhs, rhs, overflow_ret },
6220 );
6221 _ = try func.load(overflow_ret, Type.i32, 0);
6222 try func.addLabel(.local_set, overflow_bit.local.value);
6223 break :blk res;
6224 } else return func.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});
6225 var bin_op_local = try mul.toLocal(func, ty);
6267 defer bin_op_local.free(func);6226 defer bin_op_local.free(func);
62686227
6269 const result_ptr = try func.allocStack(func.typeOfIndex(inst));6228 const result = try func.allocStack(func.typeOfIndex(inst));
6270 try func.store(result_ptr, bin_op_local, lhs_ty, 0);6229 const offset: u32 = @intCast(ty.abiSize(pt));
6271 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));6230 try func.store(result, bin_op_local, ty, 0);
6272 try func.store(result_ptr, overflow_bit, Type.u1, offset);6231 try func.store(result, overflow_bit, Type.u1, offset);
62736232
6274 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });6233 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6275}6234}
62766235
6277fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6236fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
...@@ -6378,16 +6337,16 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6378,16 +6337,16 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6378 try func.addTag(.i32_wrap_i64);6337 try func.addTag(.i32_wrap_i64);
6379 },6338 },
6380 128 => {6339 128 => {
6381 var lsb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);6340 var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);
6382 defer lsb.free(func);6341 defer msb.free(func);
63836342
6384 try func.emitWValue(lsb);6343 try func.emitWValue(msb);
6385 try func.addTag(.i64_clz);6344 try func.addTag(.i64_clz);
6386 _ = try func.load(operand, Type.u64, 0);6345 _ = try func.load(operand, Type.u64, 0);
6387 try func.addTag(.i64_clz);6346 try func.addTag(.i64_clz);
6388 try func.emitWValue(.{ .imm64 = 64 });6347 try func.emitWValue(.{ .imm64 = 64 });
6389 try func.addTag(.i64_add);6348 try func.addTag(.i64_add);
6390 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);6349 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
6391 try func.addTag(.select);6350 try func.addTag(.select);
6392 try func.addTag(.i32_wrap_i64);6351 try func.addTag(.i32_wrap_i64);
6393 },6352 },
...@@ -6438,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6438,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6438 try func.addTag(.i32_wrap_i64);6397 try func.addTag(.i32_wrap_i64);
6439 },6398 },
6440 128 => {6399 128 => {
6441 var msb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);6400 var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
6442 defer msb.free(func);6401 defer lsb.free(func);
64436402
6444 try func.emitWValue(msb);6403 try func.emitWValue(lsb);
6445 try func.addTag(.i64_ctz);6404 try func.addTag(.i64_ctz);
6446 _ = try func.load(operand, Type.u64, 8);6405 _ = try func.load(operand, Type.u64, 8);
6447 if (wasm_bits != int_info.bits) {6406 if (wasm_bits != int_info.bits) {
...@@ -6455,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6455,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6455 } else {6414 } else {
6456 try func.addTag(.i64_add);6415 try func.addTag(.i64_add);
6457 }6416 }
6458 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);6417 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6459 try func.addTag(.select);6418 try func.addTag(.select);
6460 try func.addTag(.i32_wrap_i64);6419 try func.addTag(.i32_wrap_i64);
6461 },6420 },
test/behavior/math.zig+69-288
...@@ -918,37 +918,22 @@ test "small int addition" {...@@ -918,37 +918,22 @@ test "small int addition" {
918 try expect(ov[1] == 1);918 try expect(ov[1] == 1);
919}919}
920920
921fn testMulWithOverflow(comptime T: type, a: T, b: T, mul: T, bit: u1) !void {
922 const ov = @mulWithOverflow(a, b);
923 try expect(ov[0] == mul);
924 try expect(ov[1] == bit);
925}
926
921test "basic @mulWithOverflow" {927test "basic @mulWithOverflow" {
922 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO928 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
923 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO929 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
924 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;930 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
925931
926 {932 try testMulWithOverflow(u8, 86, 3, 2, 1);
927 var a: u8 = 86;933 try testMulWithOverflow(u8, 85, 3, 255, 0);
928 _ = &a;
929 const ov = @mulWithOverflow(a, 3);
930 try expect(ov[0] == 2);
931 try expect(ov[1] == 1);
932 }
933 {
934 var a: u8 = 85;
935 _ = &a;
936 const ov = @mulWithOverflow(a, 3);
937 try expect(ov[0] == 255);
938 try expect(ov[1] == 0);
939 }
940
941 var a: u8 = 123;
942 _ = &a;
943 var b: u8 = 2;
944 var ov = @mulWithOverflow(a, b);
945 try expect(ov[0] == 246);
946 try expect(ov[1] == 0);
947934
948 b = 4;935 try testMulWithOverflow(u8, 123, 2, 246, 0);
949 ov = @mulWithOverflow(a, b);936 try testMulWithOverflow(u8, 123, 4, 236, 1);
950 try expect(ov[0] == 236);
951 try expect(ov[1] == 1);
952}937}
953938
954test "extensive @mulWithOverflow" {939test "extensive @mulWithOverflow" {
...@@ -956,173 +941,38 @@ test "extensive @mulWithOverflow" {...@@ -956,173 +941,38 @@ test "extensive @mulWithOverflow" {
956 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO941 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
957 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;942 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
958943
959 {944 try testMulWithOverflow(u5, 3, 10, 30, 0);
960 var a: u5 = 3;945 try testMulWithOverflow(u5, 3, 11, 1, 1);
961 _ = &a;946 try testMulWithOverflow(i5, 3, -5, -15, 0);
962 var b: u5 = 10;947 try testMulWithOverflow(i5, 3, -6, 14, 1);
963 var ov = @mulWithOverflow(a, b);
964 try expect(ov[0] == 30);
965 try expect(ov[1] == 0);
966
967 b = 11;
968 ov = @mulWithOverflow(a, b);
969 try expect(ov[0] == 1);
970 try expect(ov[1] == 1);
971 }
972
973 {
974 var a: i5 = 3;
975 _ = &a;
976 var b: i5 = -5;
977 var ov = @mulWithOverflow(a, b);
978 try expect(ov[0] == -15);
979 try expect(ov[1] == 0);
980
981 b = -6;
982 ov = @mulWithOverflow(a, b);
983 try expect(ov[0] == 14);
984 try expect(ov[1] == 1);
985 }
986
987 {
988 var a: u8 = 3;
989 _ = &a;
990 var b: u8 = 85;
991
992 var ov = @mulWithOverflow(a, b);
993 try expect(ov[0] == 255);
994 try expect(ov[1] == 0);
995
996 b = 86;
997 ov = @mulWithOverflow(a, b);
998 try expect(ov[0] == 2);
999 try expect(ov[1] == 1);
1000 }
1001
1002 {
1003 var a: i8 = 3;
1004 _ = &a;
1005 var b: i8 = -42;
1006 var ov = @mulWithOverflow(a, b);
1007 try expect(ov[0] == -126);
1008 try expect(ov[1] == 0);
1009
1010 b = -43;
1011 ov = @mulWithOverflow(a, b);
1012 try expect(ov[0] == 127);
1013 try expect(ov[1] == 1);
1014 }
1015
1016 {
1017 var a: u14 = 3;
1018 _ = &a;
1019 var b: u14 = 0x1555;
1020 var ov = @mulWithOverflow(a, b);
1021 try expect(ov[0] == 0x3fff);
1022 try expect(ov[1] == 0);
1023
1024 b = 0x1556;
1025 ov = @mulWithOverflow(a, b);
1026 try expect(ov[0] == 2);
1027 try expect(ov[1] == 1);
1028 }
1029
1030 {
1031 var a: i14 = 3;
1032 _ = &a;
1033 var b: i14 = -0xaaa;
1034 var ov = @mulWithOverflow(a, b);
1035 try expect(ov[0] == -0x1ffe);
1036 try expect(ov[1] == 0);
1037948
1038 b = -0xaab;949 try testMulWithOverflow(u8, 3, 85, 255, 0);
1039 ov = @mulWithOverflow(a, b);950 try testMulWithOverflow(u8, 3, 86, 2, 1);
1040 try expect(ov[0] == 0x1fff);951 try testMulWithOverflow(i8, 3, -42, -126, 0);
1041 }952 try testMulWithOverflow(i8, 3, -43, 127, 1);
1042953
1043 {954 try testMulWithOverflow(u14, 3, 0x1555, 0x3fff, 0);
1044 var a: u16 = 3;955 try testMulWithOverflow(u14, 3, 0x1556, 2, 1);
1045 _ = &a;956 try testMulWithOverflow(i14, 3, -0xaaa, -0x1ffe, 0);
1046 var b: u16 = 0x5555;957 try testMulWithOverflow(i14, 3, -0xaab, 0x1fff, 1);
1047 var ov = @mulWithOverflow(a, b);
1048 try expect(ov[0] == 0xffff);
1049 try expect(ov[1] == 0);
1050958
1051 b = 0x5556;959 try testMulWithOverflow(u16, 3, 0x5555, 0xffff, 0);
1052 ov = @mulWithOverflow(a, b);960 try testMulWithOverflow(u16, 3, 0x5556, 2, 1);
1053 try expect(ov[0] == 2);961 try testMulWithOverflow(i16, 3, -0x2aaa, -0x7ffe, 0);
1054 try expect(ov[1] == 1);962 try testMulWithOverflow(i16, 3, -0x2aab, 0x7fff, 1);
1055 }
1056963
1057 {964 try testMulWithOverflow(u30, 3, 0x15555555, 0x3fffffff, 0);
1058 var a: i16 = 3;965 try testMulWithOverflow(u30, 3, 0x15555556, 2, 1);
1059 _ = &a;966 try testMulWithOverflow(i30, 3, -0xaaaaaaa, -0x1ffffffe, 0);
1060 var b: i16 = -0x2aaa;967 try testMulWithOverflow(i30, 3, -0xaaaaaab, 0x1fffffff, 1);
1061 var ov = @mulWithOverflow(a, b);
1062 try expect(ov[0] == -0x7ffe);
1063 try expect(ov[1] == 0);
1064968
1065 b = -0x2aab;969 try testMulWithOverflow(u32, 3, 0x55555555, 0xffffffff, 0);
1066 ov = @mulWithOverflow(a, b);970 try testMulWithOverflow(u32, 3, 0x55555556, 2, 1);
1067 try expect(ov[0] == 0x7fff);971 try testMulWithOverflow(i32, 3, -0x2aaaaaaa, -0x7ffffffe, 0);
1068 try expect(ov[1] == 1);972 try testMulWithOverflow(i32, 3, -0x2aaaaaab, 0x7fffffff, 1);
1069 }
1070973
1071 {974 try testMulWithOverflow(u31, 1 << 30, 1 << 30, 0, 1);
1072 var a: u30 = 3;975 try testMulWithOverflow(i31, minInt(i31), minInt(i31), 0, 1);
1073 _ = &a;
1074 var b: u30 = 0x15555555;
1075 var ov = @mulWithOverflow(a, b);
1076 try expect(ov[0] == 0x3fffffff);
1077 try expect(ov[1] == 0);
1078
1079 b = 0x15555556;
1080 ov = @mulWithOverflow(a, b);
1081 try expect(ov[0] == 2);
1082 try expect(ov[1] == 1);
1083 }
1084
1085 {
1086 var a: i30 = 3;
1087 _ = &a;
1088 var b: i30 = -0xaaaaaaa;
1089 var ov = @mulWithOverflow(a, b);
1090 try expect(ov[0] == -0x1ffffffe);
1091 try expect(ov[1] == 0);
1092
1093 b = -0xaaaaaab;
1094 ov = @mulWithOverflow(a, b);
1095 try expect(ov[0] == 0x1fffffff);
1096 try expect(ov[1] == 1);
1097 }
1098
1099 {
1100 var a: u32 = 3;
1101 _ = &a;
1102 var b: u32 = 0x55555555;
1103 var ov = @mulWithOverflow(a, b);
1104 try expect(ov[0] == 0xffffffff);
1105 try expect(ov[1] == 0);
1106
1107 b = 0x55555556;
1108 ov = @mulWithOverflow(a, b);
1109 try expect(ov[0] == 2);
1110 try expect(ov[1] == 1);
1111 }
1112
1113 {
1114 var a: i32 = 3;
1115 _ = &a;
1116 var b: i32 = -0x2aaaaaaa;
1117 var ov = @mulWithOverflow(a, b);
1118 try expect(ov[0] == -0x7ffffffe);
1119 try expect(ov[1] == 0);
1120
1121 b = -0x2aaaaaab;
1122 ov = @mulWithOverflow(a, b);
1123 try expect(ov[0] == 0x7fffffff);
1124 try expect(ov[1] == 1);
1125 }
1126}976}
1127977
1128test "@mulWithOverflow bitsize > 32" {978test "@mulWithOverflow bitsize > 32" {
...@@ -1131,118 +981,49 @@ test "@mulWithOverflow bitsize > 32" {...@@ -1131,118 +981,49 @@ test "@mulWithOverflow bitsize > 32" {
1131 // aarch64 fails on a release build of the compiler.981 // aarch64 fails on a release build of the compiler.
1132 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO982 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO983 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1134 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1135 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO984 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1136985
1137 {986 try testMulWithOverflow(u40, 3, 0x55_5555_5555, 0xff_ffff_ffff, 0);
1138 var a: u40 = 3;987 try testMulWithOverflow(u40, 3, 0x55_5555_5556, 2, 1);
1139 var b: u40 = 0x55_5555_5555;988 try testMulWithOverflow(u40, 0x10_0000_0000, 0x10_0000_0000, 0, 1);
1140 var ov = @mulWithOverflow(a, b);
1141989
1142 try expect(ov[0] == 0xff_ffff_ffff);990 try testMulWithOverflow(i40, 3, -0x2a_aaaa_aaaa, -0x7f_ffff_fffe, 0);
1143 try expect(ov[1] == 0);991 try testMulWithOverflow(i40, 3, -0x2a_aaaa_aaab, 0x7f_ffff_ffff, 1);
992 try testMulWithOverflow(i40, 6, -0x2a_aaaa_aaab, -2, 1);
993 try testMulWithOverflow(i40, 0x08_0000_0000, -0x08_0000_0001, -0x8_0000_0000, 1);
1144994
1145 // Check that overflow bits in the low-word of wide-multiplications are checked too.995 try testMulWithOverflow(u62, 3, 0x1555555555555555, 0x3fffffffffffffff, 0);
1146 // Intermediate result is less than 2**64996 try testMulWithOverflow(u62, 3, 0x1555555555555556, 2, 1);
1147 b = 0x55_5555_5556;997 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaaa, -0x1ffffffffffffffe, 0);
1148 ov = @mulWithOverflow(a, b);998 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaab, 0x1fffffffffffffff, 1);
1149 try expect(ov[0] == 2);
1150 try expect(ov[1] == 1);
1151
1152 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1153 // Intermediate result is more than 2**64 and bits 40..64 are not set.
1154 a = 0x10_0000_0000;
1155 b = 0x10_0000_0000;
1156 ov = @mulWithOverflow(a, b);
1157 try expect(ov[0] == 0);
1158 try expect(ov[1] == 1);
1159 }
1160999
1161 {1000 try testMulWithOverflow(u64, 3, 0x5555555555555555, 0xffffffffffffffff, 0);
1162 var a: i40 = 3;1001 try testMulWithOverflow(u64, 3, 0x5555555555555556, 2, 1);
1163 var b: i40 = -0x2a_aaaa_aaaa;1002 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaaa, -0x7ffffffffffffffe, 0);
1164 var ov = @mulWithOverflow(a, b);1003 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaab, 0x7fffffffffffffff, 1);
11651004
1166 try expect(ov[0] == -0x7f_ffff_fffe);1005 try testMulWithOverflow(u63, 1 << 62, 1 << 62, 0, 1);
1167 try expect(ov[1] == 0);1006 try testMulWithOverflow(i63, minInt(i63), minInt(i63), 0, 1);
11681007}
1169 // Check that the sign bit is properly checked
1170 b = -0x2a_aaaa_aaab;
1171 ov = @mulWithOverflow(a, b);
1172 try expect(ov[0] == 0x7f_ffff_ffff);
1173 try expect(ov[1] == 1);
1174
1175 // Check that the low-order bits above the sign are checked.
1176 a = 6;
1177 ov = @mulWithOverflow(a, b);
1178 try expect(ov[0] == -2);
1179 try expect(ov[1] == 1);
1180
1181 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1182 // high parts and sign of low-order bits are all 1.
1183 a = 0x08_0000_0000;
1184 b = -0x08_0000_0001;
1185 ov = @mulWithOverflow(a, b);
1186
1187 try expect(ov[0] == -0x8_0000_0000);
1188 try expect(ov[1] == 1);
1189 }
1190
1191 {
1192 var a: u62 = 3;
1193 _ = &a;
1194 var b: u62 = 0x1555555555555555;
1195 var ov = @mulWithOverflow(a, b);
1196 try expect(ov[0] == 0x3fffffffffffffff);
1197 try expect(ov[1] == 0);
1198
1199 b = 0x1555555555555556;
1200 ov = @mulWithOverflow(a, b);
1201 try expect(ov[0] == 2);
1202 try expect(ov[1] == 1);
1203 }
1204
1205 {
1206 var a: i62 = 3;
1207 _ = &a;
1208 var b: i62 = -0xaaaaaaaaaaaaaaa;
1209 var ov = @mulWithOverflow(a, b);
1210 try expect(ov[0] == -0x1ffffffffffffffe);
1211 try expect(ov[1] == 0);
1212
1213 b = -0xaaaaaaaaaaaaaab;
1214 ov = @mulWithOverflow(a, b);
1215 try expect(ov[0] == 0x1fffffffffffffff);
1216 try expect(ov[1] == 1);
1217 }
12181008
1219 {1009test "@mulWithOverflow bitsize 128 bits" {
1220 var a: u64 = 3;1010 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1221 _ = &a;1011 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1222 var b: u64 = 0x5555555555555555;1012 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1223 var ov = @mulWithOverflow(a, b);1013 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1224 try expect(ov[0] == 0xffffffffffffffff);
1225 try expect(ov[1] == 0);
12261014
1227 b = 0x5555555555555556;1015 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555555, 0xffffffffffffffff_ffffffffffffffff, 0);
1228 ov = @mulWithOverflow(a, b);1016 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555556, 2, 1);
1229 try expect(ov[0] == 2);
1230 try expect(ov[1] == 1);
1231 }
12321017
1233 {1018 try testMulWithOverflow(u128, 1 << 100, 1 << 27, 1 << 127, 0);
1234 var a: i64 = 3;1019 try testMulWithOverflow(u128, maxInt(u128), maxInt(u128), 1, 1);
1235 _ = &a;1020 try testMulWithOverflow(u128, 1 << 100, 1 << 28, 0, 1);
1236 var b: i64 = -0x2aaaaaaaaaaaaaaa;1021 try testMulWithOverflow(u128, 1 << 127, 1 << 127, 0, 1);
1237 var ov = @mulWithOverflow(a, b);
1238 try expect(ov[0] == -0x7ffffffffffffffe);
1239 try expect(ov[1] == 0);
12401022
1241 b = -0x2aaaaaaaaaaaaaab;1023 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaaa, -0x7fffffffffffffff_fffffffffffffffe, 0);
1242 ov = @mulWithOverflow(a, b);1024 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaab, 0x7fffffffffffffff_ffffffffffffffff, 1);
1243 try expect(ov[0] == 0x7fffffffffffffff);1025 try testMulWithOverflow(i128, -1, -1, 1, 0);
1244 try expect(ov[1] == 1);1026 try testMulWithOverflow(i128, minInt(i128), minInt(i128), 0, 1);
1245 }
1246}1027}
12471028
1248test "@mulWithOverflow u256" {1029test "@mulWithOverflow u256" {