authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-20 23:57:14-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-20 23:57:14-07:00
log93c546c8c9bf20b70b49546f278b4663b5c3f6d7
tree6d4083ab7ac0d3259be51388675c92c3775c6f67
parent5f78e28899f99985fcc3d957b0fac08deffac81c
parenta0795f11dfbe80fcdab96d2dc6d2de96eddf9f5c
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20692 from pavelverigo/stage2-wasm-overflow-ops

stage2-wasm: overflow ops improvement

3 files changed, 474 insertions(+), 743 deletions(-)

src/arch/wasm/CodeGen.zig+182-281
......@@ -2669,47 +2669,53 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner
26692669 .signed => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26702670 .unsigned => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26712671 },
2672 .rem => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2673 .shr => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2672 .rem => switch (int_info.signedness) {
2673 .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2674 .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2675 },
2676 .shr => switch (int_info.signedness) {
2677 .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2678 .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2679 },
26742680 .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
26752681 .@"and", .@"or", .xor => {
26762682 const result = try func.allocStack(ty);
26772683 try func.emitWValue(result);
2678 const lhs_high_bit = try func.load(lhs, Type.u64, 0);
2679 const rhs_high_bit = try func.load(rhs, Type.u64, 0);
2680 const op_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
2681 try func.store(.stack, op_high_bit, Type.u64, result.offset());
2684 const lhs_lsb = try func.load(lhs, Type.u64, 0);
2685 const rhs_lsb = try func.load(rhs, Type.u64, 0);
2686 const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2687 try func.store(.stack, op_lsb, Type.u64, result.offset());
26822688
26832689 try func.emitWValue(result);
2684 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2685 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2686 const op_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2687 try func.store(.stack, op_low_bit, Type.u64, result.offset() + 8);
2690 const lhs_msb = try func.load(lhs, Type.u64, 8);
2691 const rhs_msb = try func.load(rhs, Type.u64, 8);
2692 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
2693 try func.store(.stack, op_msb, Type.u64, result.offset() + 8);
26882694 return result;
26892695 },
26902696 .add, .sub => {
26912697 const result = try func.allocStack(ty);
2692 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2693 defer lhs_high_bit.free(func);
2694 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2695 defer rhs_high_bit.free(func);
2696 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
2697 defer high_op_res.free(func);
2698 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2699 defer lhs_lsb.free(func);
2700 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2701 defer rhs_lsb.free(func);
2702 var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64);
2703 defer op_lsb.free(func);
26982704
2699 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2700 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2701 const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2705 const lhs_msb = try func.load(lhs, Type.u64, 8);
2706 const rhs_msb = try func.load(rhs, Type.u64, 8);
2707 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
27022708
27032709 const lt = if (op == .add) blk: {
2704 break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
2710 break :blk try func.cmp(op_lsb, rhs_lsb, Type.u64, .lt);
27052711 } else if (op == .sub) blk: {
2706 break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2712 break :blk try func.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt);
27072713 } else unreachable;
27082714 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2709 var tmp_op = try (try func.binOp(low_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);
27102716 defer tmp_op.free(func);
27112717
2712 try func.store(result, high_op_res, Type.u64, 0);
2718 try func.store(result, op_lsb, Type.u64, 0);
27132719 try func.store(result, tmp_op, Type.u64, 8);
27142720 return result;
27152721 },
......@@ -4413,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
44134419 break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty);
44144420 } else operand;
44154421
4416 // store msb first
4422 // store lsb first
44174423 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());
44184424
4419 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
4425 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value
44204426 if (wanted.isSignedInt(mod)) {
44214427 try func.emitWValue(stack_ptr);
44224428 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
44234429 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
44244430 } else {
4425 // Ensure memory of lsb is zero'd
4431 // Ensure memory of msb is zero'd
44264432 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
44274433 }
44284434 return stack_ptr;
......@@ -5523,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
55235529 return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(pt)});
55245530 }
55255531
5526 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
5527 defer lhs_high_bit.free(func);
5528 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
5529 defer rhs_high_bit.free(func);
5532 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5533 defer lhs_msb.free(func);
5534 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5535 defer rhs_msb.free(func);
55305536
55315537 switch (op) {
55325538 .eq, .neq => {
5533 const xor_high = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
5534 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
5535 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
5536 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
5539 const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor);
5540 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5541 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5542 const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);
55375543 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");
55385544
55395545 switch (op) {
......@@ -5545,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
55455551 else => {
55465552 const ty = if (operand_ty.isSignedInt(mod)) Type.i64 else Type.u64;
55475553 // leave those value on top of the stack for '.select'
5548 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
5549 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
5550 _ = try func.cmp(lhs_low_bit, rhs_low_bit, ty, op);
5551 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, op);
5552 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
5554 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5555 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5556 _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5557 _ = try func.cmp(lhs_msb, rhs_msb, ty, op);
5558 _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq);
55535559 try func.addTag(.select);
55545560 },
55555561 }
......@@ -5980,6 +5986,26 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE
59805986 return func.finishAir(inst, result, &.{ty_op.operand});
59815987}
59825988
5989/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
5990fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {
5991 const mod = func.bin_file.base.comp.module.?;
5992 const int_info = ty.intInfo(mod);
5993 const wasm_bits = toWasmBits(int_info.bits) orelse {
5994 return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});
5995 };
5996 switch (wasm_bits) {
5997 32 => return .{ .imm32 = 0 },
5998 64 => return .{ .imm64 = 0 },
5999 128 => {
6000 const result = try func.allocStack(ty);
6001 try func.store(result, .{ .imm64 = 0 }, Type.u64, 0);
6002 try func.store(result, .{ .imm64 = 0 }, Type.u64, 8);
6003 return result;
6004 },
6005 else => unreachable,
6006 }
6007}
6008
59836009fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
59846010 assert(op == .add or op == .sub);
59856011 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
......@@ -5987,124 +6013,44 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
59876013
59886014 const lhs = try func.resolveInst(extra.lhs);
59896015 const rhs = try func.resolveInst(extra.rhs);
5990 const lhs_ty = func.typeOf(extra.lhs);
6016 const ty = func.typeOf(extra.lhs);
59916017 const pt = func.pt;
59926018 const mod = pt.zcu;
59936019
5994 if (lhs_ty.zigTypeTag(mod) == .Vector) {
6020 if (ty.zigTypeTag(mod) == .Vector) {
59956021 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
59966022 }
59976023
5998 const int_info = lhs_ty.intInfo(mod);
6024 const int_info = ty.intInfo(mod);
59996025 const is_signed = int_info.signedness == .signed;
6000 const wasm_bits = toWasmBits(int_info.bits) orelse {
6026 if (int_info.bits > 128) {
60016027 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 });
60076028 }
60086029
6009 const zero: WValue = switch (wasm_bits) {
6010 32 => .{ .imm32 = 0 },
6011 64 => .{ .imm64 = 0 },
6030 const op_result = try func.wrapBinOp(lhs, rhs, ty, op);
6031 var op_tmp = try op_result.toLocal(func, ty);
6032 defer op_tmp.free(func);
6033
6034 const cmp_op: std.math.CompareOperator = switch (op) {
6035 .add => .lt,
6036 .sub => .gt,
60126037 else => unreachable,
60136038 };
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
60796039 const overflow_bit = if (is_signed) blk: {
6080 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
6081 const to_wrap = if (op == .add) wrap: {
6082 break :wrap try func.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
6083 } else xor_low;
6084 const xor_op = try func.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
6085 const wrap = try func.binOp(to_wrap, xor_op, Type.u64, .@"and");
6086 break :blk try func.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
6087 } else blk: {
6088 const first_arg = if (op == .sub) arg: {
6089 break :arg try func.cmp(high_op_res, lhs_high_bit, Type.u64, .gt);
6090 } else lt;
6091
6092 try func.emitWValue(first_arg);
6093 _ = try func.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);
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);
6040 const zero = try intZeroValue(func, ty);
6041 const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt);
6042 const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op);
6043 break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
6044 } else try func.cmp(op_tmp, lhs, ty, cmp_op);
6045 var bit_tmp = try overflow_bit.toLocal(func, Type.u1);
6046 defer bit_tmp.free(func);
61016047
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);
6048 const result = try func.allocStack(func.typeOfIndex(inst));
6049 const offset: u32 = @intCast(ty.abiSize(pt));
6050 try func.store(result, op_tmp, ty, 0);
6051 try func.store(result, bit_tmp, Type.u1, offset);
61066052
6107 return result_ptr;
6053 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
61086054}
61096055
61106056fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -6115,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61156061
61166062 const lhs = try func.resolveInst(extra.lhs);
61176063 const rhs = try func.resolveInst(extra.rhs);
6118 const lhs_ty = func.typeOf(extra.lhs);
6064 const ty = func.typeOf(extra.lhs);
61196065 const rhs_ty = func.typeOf(extra.rhs);
61206066
6121 if (lhs_ty.zigTypeTag(mod) == .Vector) {
6067 if (ty.zigTypeTag(mod) == .Vector) {
61226068 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
61236069 }
61246070
6125 const int_info = lhs_ty.intInfo(mod);
6071 const int_info = ty.intInfo(mod);
61266072 const wasm_bits = toWasmBits(int_info.bits) orelse {
61276073 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
61286074 };
......@@ -6130,32 +6076,28 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61306076 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types
61316077 // before we can perform any binary operation.
61326078 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(mod).bits).?;
6133 const rhs_final = if (wasm_bits != rhs_wasm_bits) blk: {
6134 const rhs_casted = try func.intcast(rhs, rhs_ty, lhs_ty);
6135 break :blk try rhs_casted.toLocal(func, lhs_ty);
6079 // If wasm_bits == 128, compiler-rt expects i32 for shift
6080 const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: {
6081 const rhs_casted = try func.intcast(rhs, rhs_ty, ty);
6082 break :blk try rhs_casted.toLocal(func, ty);
61366083 } else rhs;
61376084
6138 var shl = try (try func.binOp(lhs, rhs_final, lhs_ty, .shl)).toLocal(func, lhs_ty);
6085 var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty);
61396086 defer shl.free(func);
6140 var result = if (wasm_bits != int_info.bits) blk: {
6141 break :blk try (try func.wrapOperand(shl, lhs_ty)).toLocal(func, lhs_ty);
6142 } else shl;
6143 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
61446087
61456088 const overflow_bit = blk: {
6146 try func.emitWValue(lhs);
6147 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);
6148 break :blk try func.cmp(.stack, shr, lhs_ty, .neq);
6089 const shr = try func.binOp(shl, rhs_final, ty, .shr);
6090 break :blk try func.cmp(shr, lhs, ty, .neq);
61496091 };
61506092 var overflow_local = try overflow_bit.toLocal(func, Type.u1);
61516093 defer overflow_local.free(func);
61526094
6153 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
6154 try func.store(result_ptr, result, lhs_ty, 0);
6155 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));
6156 try func.store(result_ptr, overflow_local, Type.u1, offset);
6095 const result = try func.allocStack(func.typeOfIndex(inst));
6096 const offset: u32 = @intCast(ty.abiSize(pt));
6097 try func.store(result, shl, ty, 0);
6098 try func.store(result, overflow_local, Type.u1, offset);
61576099
6158 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
6100 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
61596101}
61606102
61616103fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -6164,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61646106
61656107 const lhs = try func.resolveInst(extra.lhs);
61666108 const rhs = try func.resolveInst(extra.rhs);
6167 const lhs_ty = func.typeOf(extra.lhs);
6109 const ty = func.typeOf(extra.lhs);
61686110 const pt = func.pt;
61696111 const mod = pt.zcu;
61706112
6171 if (lhs_ty.zigTypeTag(mod) == .Vector) {
6113 if (ty.zigTypeTag(mod) == .Vector) {
61726114 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
61736115 }
61746116
......@@ -6177,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61776119 var overflow_bit = try func.ensureAllocLocal(Type.u1);
61786120 defer overflow_bit.free(func);
61796121
6180 const int_info = lhs_ty.intInfo(mod);
6122 const int_info = ty.intInfo(mod);
61816123 const wasm_bits = toWasmBits(int_info.bits) orelse {
61826124 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
61836125 };
......@@ -6189,147 +6131,106 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61896131 };
61906132
61916133 // for 32 bit integers we upcast it to a 64bit integer
6192 const bin_op = if (int_info.bits == 32) blk: {
6134 const mul = if (wasm_bits == 32) blk: {
61936135 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
6194 const lhs_upcast = try func.intcast(lhs, lhs_ty, new_ty);
6195 const rhs_upcast = try func.intcast(rhs, lhs_ty, new_ty);
6136 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6137 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
61966138 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6197 if (int_info.signedness == .unsigned) {
6198 const shr = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
6199 const wrap = try func.intcast(shr, new_ty, lhs_ty);
6200 _ = try func.cmp(wrap, zero, lhs_ty, .neq);
6201 try func.addLabel(.local_set, overflow_bit.local.value);
6202 break :blk try func.intcast(bin_op, new_ty, lhs_ty);
6203 } else {
6204 const down_cast = try (try func.intcast(bin_op, new_ty, lhs_ty)).toLocal(func, lhs_ty);
6205 var shr = try (try func.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(func, lhs_ty);
6206 defer shr.free(func);
6207
6208 const shr_res = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
6209 const down_shr_res = try func.intcast(shr_res, new_ty, lhs_ty);
6210 _ = try func.cmp(down_shr_res, shr, lhs_ty, .neq);
6211 try func.addLabel(.local_set, overflow_bit.local.value);
6212 break :blk down_cast;
6213 }
6214 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
6215 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
6216 const mul_abs = try func.wrapOperand(bin_op, lhs_ty);
6217 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
6139 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6140 const res_upcast = try func.intcast(res, ty, new_ty);
6141 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
62186142 try func.addLabel(.local_set, overflow_bit.local.value);
6219 break :blk try func.wrapOperand(bin_op, lhs_ty);
6220 } else if (wasm_bits == 32) blk: {
6221 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
6222 defer bin_op.free(func);
6223 const shift_imm: WValue = if (wasm_bits == 32)
6224 .{ .imm32 = int_info.bits }
6225 else
6226 .{ .imm64 = int_info.bits };
6227 const shr = try func.binOp(bin_op, shift_imm, lhs_ty, .shr);
6228 _ = try func.cmp(shr, zero, lhs_ty, .neq);
6229 try func.addLabel(.local_set, overflow_bit.local.value);
6230 break :blk try func.wrapOperand(bin_op, lhs_ty);
6231 } else if (int_info.bits == 64 and int_info.signedness == .unsigned) blk: {
6232 const new_ty = Type.u128;
6233 var lhs_upcast = try (try func.intcast(lhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
6234 defer lhs_upcast.free(func);
6235 var rhs_upcast = try (try func.intcast(rhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
6236 defer rhs_upcast.free(func);
6237 const bin_op = try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul);
6238 const lsb = try func.load(bin_op, lhs_ty, 8);
6239 _ = try func.cmp(lsb, zero, lhs_ty, .neq);
6240 try func.addLabel(.local_set, overflow_bit.local.value);
6241
6242 break :blk try func.load(bin_op, lhs_ty, 0);
6243 } else if (int_info.bits == 64 and int_info.signedness == .signed) blk: {
6244 const shift_val: WValue = .{ .imm64 = 63 };
6245 var lhs_shifted = try (try func.binOp(lhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
6246 defer lhs_shifted.free(func);
6247 var rhs_shifted = try (try func.binOp(rhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
6248 defer rhs_shifted.free(func);
6249
6250 const bin_op = try func.callIntrinsic(
6251 "__multi3",
6252 &[_]InternPool.Index{.i64_type} ** 4,
6253 Type.i128,
6254 &.{ lhs, lhs_shifted, rhs, rhs_shifted },
6255 );
6256 const res = try func.allocLocal(lhs_ty);
6257 const msb = try func.load(bin_op, lhs_ty, 0);
6258 try func.addLabel(.local_tee, res.local.value);
6259 const msb_shifted = try func.binOp(msb, shift_val, lhs_ty, .shr);
6260 const lsb = try func.load(bin_op, lhs_ty, 8);
6261 _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq);
6143 break :blk res;
6144 } else if (wasm_bits == 64) blk: {
6145 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;
6146 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6147 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6148 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6149 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6150 const res_upcast = try func.intcast(res, ty, new_ty);
6151 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
62626152 try func.addLabel(.local_set, overflow_bit.local.value);
62636153 break :blk res;
62646154 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
6265 var lhs_msb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
6266 defer lhs_msb.free(func);
6267 var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6155 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
62686156 defer lhs_lsb.free(func);
6269 var rhs_msb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
6270 defer rhs_msb.free(func);
6271 var rhs_lsb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
6157 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6158 defer lhs_msb.free(func);
6159 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
62726160 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);
62736163
6274 const mul1 = try func.callIntrinsic(
6164 const cross_1 = try func.callIntrinsic(
62756165 "__multi3",
62766166 &[_]InternPool.Index{.i64_type} ** 4,
62776167 Type.i128,
6278 &.{ lhs_lsb, zero, rhs_msb, zero },
6168 &.{ lhs_msb, zero, rhs_lsb, zero },
62796169 );
6280 const mul2 = try func.callIntrinsic(
6170 const cross_2 = try func.callIntrinsic(
62816171 "__multi3",
62826172 &[_]InternPool.Index{.i64_type} ** 4,
62836173 Type.i128,
6284 &.{ rhs_lsb, zero, lhs_msb, zero },
6174 &.{ rhs_msb, zero, lhs_lsb, zero },
62856175 );
6286 const mul3 = try func.callIntrinsic(
6176 const mul_lsb = try func.callIntrinsic(
62876177 "__multi3",
62886178 &[_]InternPool.Index{.i64_type} ** 4,
62896179 Type.i128,
6290 &.{ lhs_msb, zero, rhs_msb, zero },
6180 &.{ rhs_lsb, zero, lhs_lsb, zero },
62916181 );
62926182
6293 const rhs_lsb_not_zero = try func.cmp(rhs_lsb, zero, Type.u64, .neq);
6294 const lhs_lsb_not_zero = try func.cmp(lhs_lsb, zero, Type.u64, .neq);
6295 const lsb_and = try func.binOp(rhs_lsb_not_zero, lhs_lsb_not_zero, Type.bool, .@"and");
6296 const mul1_lsb = try func.load(mul1, Type.u64, 8);
6297 const mul1_lsb_not_zero = try func.cmp(mul1_lsb, zero, Type.u64, .neq);
6298 const lsb_or1 = try func.binOp(lsb_and, mul1_lsb_not_zero, Type.bool, .@"or");
6299 const mul2_lsb = try func.load(mul2, Type.u64, 8);
6300 const mul2_lsb_not_zero = try func.cmp(mul2_lsb, zero, Type.u64, .neq);
6301 const lsb_or = try func.binOp(lsb_or1, mul2_lsb_not_zero, Type.bool, .@"or");
6302
6303 const mul1_msb = try func.load(mul1, Type.u64, 0);
6304 const mul2_msb = try func.load(mul2, Type.u64, 0);
6305 const mul_add1 = try func.binOp(mul1_msb, mul2_msb, Type.u64, .add);
6306
6307 var mul3_lsb = try (try func.load(mul3, Type.u64, 8)).toLocal(func, Type.u64);
6308 defer mul3_lsb.free(func);
6309 var mul_add2 = try (try func.binOp(mul_add1, mul3_lsb, Type.u64, .add)).toLocal(func, Type.u64);
6310 defer mul_add2.free(func);
6311 const mul_add_lt = try func.cmp(mul_add2, mul3_lsb, Type.u64, .lt);
6183 const rhs_msb_not_zero = try func.cmp(rhs_msb, zero, Type.u64, .neq);
6184 const lhs_msb_not_zero = try func.cmp(lhs_msb, zero, Type.u64, .neq);
6185 const both_msb_not_zero = try func.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and");
6186 const cross_1_msb = try func.load(cross_1, Type.u64, 8);
6187 const cross_1_msb_not_zero = try func.cmp(cross_1_msb, zero, Type.u64, .neq);
6188 const cond_1 = try func.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or");
6189 const cross_2_msb = try func.load(cross_2, Type.u64, 8);
6190 const cross_2_msb_not_zero = try func.cmp(cross_2_msb, zero, Type.u64, .neq);
6191 const cond_2 = try func.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or");
6192
6193 const cross_1_lsb = try func.load(cross_1, Type.u64, 0);
6194 const cross_2_lsb = try func.load(cross_2, Type.u64, 0);
6195 const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);
6196
6197 var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64);
6198 defer mul_lsb_msb.free(func);
6199 var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64);
6200 defer all_add.free(func);
6201 const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt);
63126202
63136203 // result for overflow bit
6314 _ = try func.binOp(lsb_or, mul_add_lt, Type.bool, .@"or");
6204 _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or");
63156205 try func.addLabel(.local_set, overflow_bit.local.value);
63166206
63176207 const tmp_result = try func.allocStack(Type.u128);
63186208 try func.emitWValue(tmp_result);
6319 const mul3_msb = try func.load(mul3, Type.u64, 0);
6320 try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset());
6321 try func.store(tmp_result, mul_add2, Type.u64, 8);
6209 const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0);
6210 try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6211 try func.store(tmp_result, all_add, Type.u64, 8);
63226212 break :blk tmp_result;
6323 } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{});
6324 var bin_op_local = try bin_op.toLocal(func, lhs_ty);
6213 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {
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);
63256226 defer bin_op_local.free(func);
63266227
6327 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
6328 try func.store(result_ptr, bin_op_local, lhs_ty, 0);
6329 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));
6330 try func.store(result_ptr, overflow_bit, Type.u1, offset);
6228 const result = try func.allocStack(func.typeOfIndex(inst));
6229 const offset: u32 = @intCast(ty.abiSize(pt));
6230 try func.store(result, bin_op_local, ty, 0);
6231 try func.store(result, overflow_bit, Type.u1, offset);
63316232
6332 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
6233 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
63336234}
63346235
63356236fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
......@@ -6436,16 +6337,16 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64366337 try func.addTag(.i32_wrap_i64);
64376338 },
64386339 128 => {
6439 var lsb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);
6440 defer lsb.free(func);
6340 var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);
6341 defer msb.free(func);
64416342
6442 try func.emitWValue(lsb);
6343 try func.emitWValue(msb);
64436344 try func.addTag(.i64_clz);
64446345 _ = try func.load(operand, Type.u64, 0);
64456346 try func.addTag(.i64_clz);
64466347 try func.emitWValue(.{ .imm64 = 64 });
64476348 try func.addTag(.i64_add);
6448 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6349 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
64496350 try func.addTag(.select);
64506351 try func.addTag(.i32_wrap_i64);
64516352 },
......@@ -6496,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64966397 try func.addTag(.i32_wrap_i64);
64976398 },
64986399 128 => {
6499 var msb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
6500 defer msb.free(func);
6400 var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
6401 defer lsb.free(func);
65016402
6502 try func.emitWValue(msb);
6403 try func.emitWValue(lsb);
65036404 try func.addTag(.i64_ctz);
65046405 _ = try func.load(operand, Type.u64, 8);
65056406 if (wasm_bits != int_info.bits) {
......@@ -6513,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
65136414 } else {
65146415 try func.addTag(.i64_add);
65156416 }
6516 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
6417 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
65176418 try func.addTag(.select);
65186419 try func.addTag(.i32_wrap_i64);
65196420 },
test/behavior/basic.zig+69-44
......@@ -1134,55 +1134,80 @@ test "pointer to struct literal with runtime field is constant" {
11341134 try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const);
11351135}
11361136
1137test "integer compare" {
1137fn testSignedCmp(comptime T: type) !void {
1138 var z: T = 0;
1139 var p: T = 123;
1140 var n: T = -123;
1141 var min: T = std.math.minInt(T);
1142 var max: T = std.math.maxInt(T);
1143 var half_min: T = std.math.minInt(T) / 2;
1144 var half_max: T = std.math.minInt(T) / 2;
1145 _ = .{ &z, &p, &n, &min, &max, &half_min, &half_max };
1146 try expect(z == z and z != p and z != n);
1147 try expect(p == p and p != n and n == n);
1148 try expect(z > n and z < p and z >= n and z <= p);
1149 try expect(!(z < n or z > p or z <= n or z >= p or z > z or z < z));
1150 try expect(p > n and n < p and p >= n and n <= p and p >= p and p <= p and n >= n and n <= n);
1151 try expect(!(p < n or n > p or p <= n or n >= p or p > p or p < p or n > n or n < n));
1152 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p and 0 != n);
1153 try expect(z > -123 and p > -123 and !(n > 123));
1154 try expect(z < 123 and !(p < 123) and n < 123);
1155 try expect(-123 <= z and -123 <= p and -123 <= n);
1156 try expect(123 >= z and 123 >= p and 123 >= n);
1157 try expect(!(0 != z or 123 != p or -123 != n));
1158 try expect(!(z > 0 or -123 > p or 123 < n));
1159
1160 try expect(min <= max and z <= max and p <= max and n <= max and half_max <= max and half_min <= max);
1161 try expect(min <= max and min <= z and min <= p and min <= n and min <= half_min and min <= half_max);
1162}
1163
1164fn testUnsignedCmp(comptime T: type) !void {
1165 var z: T = 0;
1166 var p: T = 123;
1167 var max: T = std.math.maxInt(T);
1168 var half_max: T = std.math.minInt(T) / 2;
1169 _ = .{ &z, &p, &max, &half_max };
1170 try expect(z == z and z != p);
1171 try expect(p == p);
1172 try expect(z < p and z <= p);
1173 try expect(!(z > p or z >= p or z > z or z < z));
1174 try expect(p >= p and p <= p);
1175 try expect(!(p > p or p < p));
1176 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p);
1177 try expect(z > -123 and p > -123);
1178 try expect(z < 123 and !(p < 123));
1179 try expect(-123 <= z and -123 <= p);
1180 try expect(123 >= z and 123 >= p);
1181 try expect(!(0 != z or 123 != p));
1182 try expect(!(z > 0 or -123 > p));
1183
1184 try expect(z <= max and p <= max and half_max <= max);
1185 try expect(half_max != max);
1186}
1187
1188test "integer compare <= 64 bits" {
11381189 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
11391190
1140 const S = struct {
1141 fn doTheTestSigned(comptime T: type) !void {
1142 var z: T = 0;
1143 var p: T = 123;
1144 var n: T = -123;
1145 _ = .{ &z, &p, &n };
1146 try expect(z == z and z != p and z != n);
1147 try expect(p == p and p != n and n == n);
1148 try expect(z > n and z < p and z >= n and z <= p);
1149 try expect(!(z < n or z > p or z <= n or z >= p or z > z or z < z));
1150 try expect(p > n and n < p and p >= n and n <= p and p >= p and p <= p and n >= n and n <= n);
1151 try expect(!(p < n or n > p or p <= n or n >= p or p > p or p < p or n > n or n < n));
1152 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p and 0 != n);
1153 try expect(z > -123 and p > -123 and !(n > 123));
1154 try expect(z < 123 and !(p < 123) and n < 123);
1155 try expect(-123 <= z and -123 <= p and -123 <= n);
1156 try expect(123 >= z and 123 >= p and 123 >= n);
1157 try expect(!(0 != z or 123 != p or -123 != n));
1158 try expect(!(z > 0 or -123 > p or 123 < n));
1159 }
1160 fn doTheTestUnsigned(comptime T: type) !void {
1161 var z: T = 0;
1162 var p: T = 123;
1163 _ = .{ &z, &p };
1164 try expect(z == z and z != p);
1165 try expect(p == p);
1166 try expect(z < p and z <= p);
1167 try expect(!(z > p or z >= p or z > z or z < z));
1168 try expect(p >= p and p <= p);
1169 try expect(!(p > p or p < p));
1170 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p);
1171 try expect(z > -123 and p > -123);
1172 try expect(z < 123 and !(p < 123));
1173 try expect(-123 <= z and -123 <= p);
1174 try expect(123 >= z and 123 >= p);
1175 try expect(!(0 != z or 123 != p));
1176 try expect(!(z > 0 or -123 > p));
1177 }
1178 };
11791191 inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| {
1180 try S.doTheTestUnsigned(T);
1181 try comptime S.doTheTestUnsigned(T);
1192 try testUnsignedCmp(T);
1193 try comptime testUnsignedCmp(T);
11821194 }
11831195 inline for (.{ i8, i16, i32, i64, isize, i10, i20, i30, i60 }) |T| {
1184 try S.doTheTestSigned(T);
1185 try comptime S.doTheTestSigned(T);
1196 try testSignedCmp(T);
1197 try comptime testSignedCmp(T);
1198 }
1199}
1200
1201test "integer compare <= 128 bits" {
1202 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1203
1204 inline for (.{ u65, u96, u127, u128 }) |T| {
1205 try testUnsignedCmp(T);
1206 try comptime testUnsignedCmp(T);
1207 }
1208 inline for (.{ i65, i96, i127, i128 }) |T| {
1209 try testSignedCmp(T);
1210 try comptime testSignedCmp(T);
11861211 }
11871212}
11881213
test/behavior/math.zig+223-418
......@@ -828,56 +828,72 @@ test "128-bit multiplication" {
828828 }
829829}
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
831837test "@addWithOverflow" {
832838 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
833839 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
834840 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
835841 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
836842
837 {
838 var a: u8 = 250;
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 }
843 try testAddWithOverflow(u8, 250, 100, 94, 1);
844 try testAddWithOverflow(u8, 100, 150, 250, 0);
863845
864 {
865 var a: usize = 6;
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 }
846 try testAddWithOverflow(u8, 200, 99, 43, 1);
847 try testAddWithOverflow(u8, 200, 55, 255, 0);
872848
873 {
874 var a: isize = -6;
875 var b: isize = -6;
876 _ = .{ &a, &b };
877 const ov = @addWithOverflow(a, b);
878 try expect(ov[0] == -12);
879 try expect(ov[1] == 0);
880 }
849 try testAddWithOverflow(usize, 6, 6, 12, 0);
850 try testAddWithOverflow(usize, maxInt(usize), 6, 5, 1);
851
852 try testAddWithOverflow(isize, -6, -6, -12, 0);
853 try testAddWithOverflow(isize, minInt(isize), -6, maxInt(isize) - 5, 1);
854}
855
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);
881897}
882898
883899test "small int addition" {
......@@ -902,37 +918,22 @@ test "small int addition" {
902918 try expect(ov[1] == 1);
903919}
904920
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
905927test "basic @mulWithOverflow" {
906928 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
907929 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
908930 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
909931
910 {
911 var a: u8 = 86;
912 _ = &a;
913 const ov = @mulWithOverflow(a, 3);
914 try expect(ov[0] == 2);
915 try expect(ov[1] == 1);
916 }
917 {
918 var a: u8 = 85;
919 _ = &a;
920 const ov = @mulWithOverflow(a, 3);
921 try expect(ov[0] == 255);
922 try expect(ov[1] == 0);
923 }
924
925 var a: u8 = 123;
926 _ = &a;
927 var b: u8 = 2;
928 var ov = @mulWithOverflow(a, b);
929 try expect(ov[0] == 246);
930 try expect(ov[1] == 0);
932 try testMulWithOverflow(u8, 86, 3, 2, 1);
933 try testMulWithOverflow(u8, 85, 3, 255, 0);
931934
932 b = 4;
933 ov = @mulWithOverflow(a, b);
934 try expect(ov[0] == 236);
935 try expect(ov[1] == 1);
935 try testMulWithOverflow(u8, 123, 2, 246, 0);
936 try testMulWithOverflow(u8, 123, 4, 236, 1);
936937}
937938
938939test "extensive @mulWithOverflow" {
......@@ -940,173 +941,38 @@ test "extensive @mulWithOverflow" {
940941 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
941942 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
942943
943 {
944 var a: u5 = 3;
945 _ = &a;
946 var b: u5 = 10;
947 var ov = @mulWithOverflow(a, b);
948 try expect(ov[0] == 30);
949 try expect(ov[1] == 0);
950
951 b = 11;
952 ov = @mulWithOverflow(a, b);
953 try expect(ov[0] == 1);
954 try expect(ov[1] == 1);
955 }
956
957 {
958 var a: i5 = 3;
959 _ = &a;
960 var b: i5 = -5;
961 var ov = @mulWithOverflow(a, b);
962 try expect(ov[0] == -15);
963 try expect(ov[1] == 0);
964
965 b = -6;
966 ov = @mulWithOverflow(a, b);
967 try expect(ov[0] == 14);
968 try expect(ov[1] == 1);
969 }
970
971 {
972 var a: u8 = 3;
973 _ = &a;
974 var b: u8 = 85;
975
976 var ov = @mulWithOverflow(a, b);
977 try expect(ov[0] == 255);
978 try expect(ov[1] == 0);
979
980 b = 86;
981 ov = @mulWithOverflow(a, b);
982 try expect(ov[0] == 2);
983 try expect(ov[1] == 1);
984 }
985
986 {
987 var a: i8 = 3;
988 _ = &a;
989 var b: i8 = -42;
990 var ov = @mulWithOverflow(a, b);
991 try expect(ov[0] == -126);
992 try expect(ov[1] == 0);
993
994 b = -43;
995 ov = @mulWithOverflow(a, b);
996 try expect(ov[0] == 127);
997 try expect(ov[1] == 1);
998 }
999
1000 {
1001 var a: u14 = 3;
1002 _ = &a;
1003 var b: u14 = 0x1555;
1004 var ov = @mulWithOverflow(a, b);
1005 try expect(ov[0] == 0x3fff);
1006 try expect(ov[1] == 0);
1007
1008 b = 0x1556;
1009 ov = @mulWithOverflow(a, b);
1010 try expect(ov[0] == 2);
1011 try expect(ov[1] == 1);
1012 }
1013
1014 {
1015 var a: i14 = 3;
1016 _ = &a;
1017 var b: i14 = -0xaaa;
1018 var ov = @mulWithOverflow(a, b);
1019 try expect(ov[0] == -0x1ffe);
1020 try expect(ov[1] == 0);
1021
1022 b = -0xaab;
1023 ov = @mulWithOverflow(a, b);
1024 try expect(ov[0] == 0x1fff);
1025 }
1026
1027 {
1028 var a: u16 = 3;
1029 _ = &a;
1030 var b: u16 = 0x5555;
1031 var ov = @mulWithOverflow(a, b);
1032 try expect(ov[0] == 0xffff);
1033 try expect(ov[1] == 0);
1034
1035 b = 0x5556;
1036 ov = @mulWithOverflow(a, b);
1037 try expect(ov[0] == 2);
1038 try expect(ov[1] == 1);
1039 }
1040
1041 {
1042 var a: i16 = 3;
1043 _ = &a;
1044 var b: i16 = -0x2aaa;
1045 var ov = @mulWithOverflow(a, b);
1046 try expect(ov[0] == -0x7ffe);
1047 try expect(ov[1] == 0);
1048
1049 b = -0x2aab;
1050 ov = @mulWithOverflow(a, b);
1051 try expect(ov[0] == 0x7fff);
1052 try expect(ov[1] == 1);
1053 }
1054
1055 {
1056 var a: u30 = 3;
1057 _ = &a;
1058 var b: u30 = 0x15555555;
1059 var ov = @mulWithOverflow(a, b);
1060 try expect(ov[0] == 0x3fffffff);
1061 try expect(ov[1] == 0);
1062
1063 b = 0x15555556;
1064 ov = @mulWithOverflow(a, b);
1065 try expect(ov[0] == 2);
1066 try expect(ov[1] == 1);
1067 }
944 try testMulWithOverflow(u5, 3, 10, 30, 0);
945 try testMulWithOverflow(u5, 3, 11, 1, 1);
946 try testMulWithOverflow(i5, 3, -5, -15, 0);
947 try testMulWithOverflow(i5, 3, -6, 14, 1);
1068948
1069 {
1070 var a: i30 = 3;
1071 _ = &a;
1072 var b: i30 = -0xaaaaaaa;
1073 var ov = @mulWithOverflow(a, b);
1074 try expect(ov[0] == -0x1ffffffe);
1075 try expect(ov[1] == 0);
949 try testMulWithOverflow(u8, 3, 85, 255, 0);
950 try testMulWithOverflow(u8, 3, 86, 2, 1);
951 try testMulWithOverflow(i8, 3, -42, -126, 0);
952 try testMulWithOverflow(i8, 3, -43, 127, 1);
1076953
1077 b = -0xaaaaaab;
1078 ov = @mulWithOverflow(a, b);
1079 try expect(ov[0] == 0x1fffffff);
1080 try expect(ov[1] == 1);
1081 }
954 try testMulWithOverflow(u14, 3, 0x1555, 0x3fff, 0);
955 try testMulWithOverflow(u14, 3, 0x1556, 2, 1);
956 try testMulWithOverflow(i14, 3, -0xaaa, -0x1ffe, 0);
957 try testMulWithOverflow(i14, 3, -0xaab, 0x1fff, 1);
1082958
1083 {
1084 var a: u32 = 3;
1085 _ = &a;
1086 var b: u32 = 0x55555555;
1087 var ov = @mulWithOverflow(a, b);
1088 try expect(ov[0] == 0xffffffff);
1089 try expect(ov[1] == 0);
959 try testMulWithOverflow(u16, 3, 0x5555, 0xffff, 0);
960 try testMulWithOverflow(u16, 3, 0x5556, 2, 1);
961 try testMulWithOverflow(i16, 3, -0x2aaa, -0x7ffe, 0);
962 try testMulWithOverflow(i16, 3, -0x2aab, 0x7fff, 1);
1090963
1091 b = 0x55555556;
1092 ov = @mulWithOverflow(a, b);
1093 try expect(ov[0] == 2);
1094 try expect(ov[1] == 1);
1095 }
964 try testMulWithOverflow(u30, 3, 0x15555555, 0x3fffffff, 0);
965 try testMulWithOverflow(u30, 3, 0x15555556, 2, 1);
966 try testMulWithOverflow(i30, 3, -0xaaaaaaa, -0x1ffffffe, 0);
967 try testMulWithOverflow(i30, 3, -0xaaaaaab, 0x1fffffff, 1);
1096968
1097 {
1098 var a: i32 = 3;
1099 _ = &a;
1100 var b: i32 = -0x2aaaaaaa;
1101 var ov = @mulWithOverflow(a, b);
1102 try expect(ov[0] == -0x7ffffffe);
1103 try expect(ov[1] == 0);
969 try testMulWithOverflow(u32, 3, 0x55555555, 0xffffffff, 0);
970 try testMulWithOverflow(u32, 3, 0x55555556, 2, 1);
971 try testMulWithOverflow(i32, 3, -0x2aaaaaaa, -0x7ffffffe, 0);
972 try testMulWithOverflow(i32, 3, -0x2aaaaaab, 0x7fffffff, 1);
1104973
1105 b = -0x2aaaaaab;
1106 ov = @mulWithOverflow(a, b);
1107 try expect(ov[0] == 0x7fffffff);
1108 try expect(ov[1] == 1);
1109 }
974 try testMulWithOverflow(u31, 1 << 30, 1 << 30, 0, 1);
975 try testMulWithOverflow(i31, minInt(i31), minInt(i31), 0, 1);
1110976}
1111977
1112978test "@mulWithOverflow bitsize > 32" {
......@@ -1115,118 +981,51 @@ test "@mulWithOverflow bitsize > 32" {
1115981 // aarch64 fails on a release build of the compiler.
1116982 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1117983 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1118 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1119984 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1120985
1121 {
1122 var a: u40 = 3;
1123 var b: u40 = 0x55_5555_5555;
1124 var ov = @mulWithOverflow(a, b);
1125
1126 try expect(ov[0] == 0xff_ffff_ffff);
1127 try expect(ov[1] == 0);
1128
1129 // Check that overflow bits in the low-word of wide-multiplications are checked too.
1130 // Intermediate result is less than 2**64
1131 b = 0x55_5555_5556;
1132 ov = @mulWithOverflow(a, b);
1133 try expect(ov[0] == 2);
1134 try expect(ov[1] == 1);
1135
1136 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1137 // Intermediate result is more than 2**64 and bits 40..64 are not set.
1138 a = 0x10_0000_0000;
1139 b = 0x10_0000_0000;
1140 ov = @mulWithOverflow(a, b);
1141 try expect(ov[0] == 0);
1142 try expect(ov[1] == 1);
1143 }
1144
1145 {
1146 var a: i40 = 3;
1147 var b: i40 = -0x2a_aaaa_aaaa;
1148 var ov = @mulWithOverflow(a, b);
1149
1150 try expect(ov[0] == -0x7f_ffff_fffe);
1151 try expect(ov[1] == 0);
1152
1153 // Check that the sign bit is properly checked
1154 b = -0x2a_aaaa_aaab;
1155 ov = @mulWithOverflow(a, b);
1156 try expect(ov[0] == 0x7f_ffff_ffff);
1157 try expect(ov[1] == 1);
1158
1159 // Check that the low-order bits above the sign are checked.
1160 a = 6;
1161 ov = @mulWithOverflow(a, b);
1162 try expect(ov[0] == -2);
1163 try expect(ov[1] == 1);
1164
1165 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1166 // high parts and sign of low-order bits are all 1.
1167 a = 0x08_0000_0000;
1168 b = -0x08_0000_0001;
1169 ov = @mulWithOverflow(a, b);
1170
1171 try expect(ov[0] == -0x8_0000_0000);
1172 try expect(ov[1] == 1);
1173 }
986 try testMulWithOverflow(u40, 3, 0x55_5555_5555, 0xff_ffff_ffff, 0);
987 try testMulWithOverflow(u40, 3, 0x55_5555_5556, 2, 1);
988 try testMulWithOverflow(u40, 0x10_0000_0000, 0x10_0000_0000, 0, 1);
1174989
1175 {
1176 var a: u62 = 3;
1177 _ = &a;
1178 var b: u62 = 0x1555555555555555;
1179 var ov = @mulWithOverflow(a, b);
1180 try expect(ov[0] == 0x3fffffffffffffff);
1181 try expect(ov[1] == 0);
990 try testMulWithOverflow(i40, 3, -0x2a_aaaa_aaaa, -0x7f_ffff_fffe, 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);
1182994
1183 b = 0x1555555555555556;
1184 ov = @mulWithOverflow(a, b);
1185 try expect(ov[0] == 2);
1186 try expect(ov[1] == 1);
1187 }
995 try testMulWithOverflow(u62, 3, 0x1555555555555555, 0x3fffffffffffffff, 0);
996 try testMulWithOverflow(u62, 3, 0x1555555555555556, 2, 1);
997 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaaa, -0x1ffffffffffffffe, 0);
998 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaab, 0x1fffffffffffffff, 1);
1188999
1189 {
1190 var a: i62 = 3;
1191 _ = &a;
1192 var b: i62 = -0xaaaaaaaaaaaaaaa;
1193 var ov = @mulWithOverflow(a, b);
1194 try expect(ov[0] == -0x1ffffffffffffffe);
1195 try expect(ov[1] == 0);
1000 try testMulWithOverflow(u64, 3, 0x5555555555555555, 0xffffffffffffffff, 0);
1001 try testMulWithOverflow(u64, 3, 0x5555555555555556, 2, 1);
1002 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaaa, -0x7ffffffffffffffe, 0);
1003 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaab, 0x7fffffffffffffff, 1);
11961004
1197 b = -0xaaaaaaaaaaaaaab;
1198 ov = @mulWithOverflow(a, b);
1199 try expect(ov[0] == 0x1fffffffffffffff);
1200 try expect(ov[1] == 1);
1201 }
1005 try testMulWithOverflow(u63, 1 << 62, 1 << 62, 0, 1);
1006 try testMulWithOverflow(i63, minInt(i63), minInt(i63), 0, 1);
1007}
12021008
1203 {
1204 var a: u64 = 3;
1205 _ = &a;
1206 var b: u64 = 0x5555555555555555;
1207 var ov = @mulWithOverflow(a, b);
1208 try expect(ov[0] == 0xffffffffffffffff);
1209 try expect(ov[1] == 0);
1009test "@mulWithOverflow bitsize 128 bits" {
1010 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1011 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1012 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1013 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1014 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1015 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12101016
1211 b = 0x5555555555555556;
1212 ov = @mulWithOverflow(a, b);
1213 try expect(ov[0] == 2);
1214 try expect(ov[1] == 1);
1215 }
1017 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555555, 0xffffffffffffffff_ffffffffffffffff, 0);
1018 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555556, 2, 1);
12161019
1217 {
1218 var a: i64 = 3;
1219 _ = &a;
1220 var b: i64 = -0x2aaaaaaaaaaaaaaa;
1221 var ov = @mulWithOverflow(a, b);
1222 try expect(ov[0] == -0x7ffffffffffffffe);
1223 try expect(ov[1] == 0);
1020 try testMulWithOverflow(u128, 1 << 100, 1 << 27, 1 << 127, 0);
1021 try testMulWithOverflow(u128, maxInt(u128), maxInt(u128), 1, 1);
1022 try testMulWithOverflow(u128, 1 << 100, 1 << 28, 0, 1);
1023 try testMulWithOverflow(u128, 1 << 127, 1 << 127, 0, 1);
12241024
1225 b = -0x2aaaaaaaaaaaaaab;
1226 ov = @mulWithOverflow(a, b);
1227 try expect(ov[0] == 0x7fffffffffffffff);
1228 try expect(ov[1] == 1);
1229 }
1025 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaaa, -0x7fffffffffffffff_fffffffffffffffe, 0);
1026 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaab, 0x7fffffffffffffff_ffffffffffffffff, 1);
1027 try testMulWithOverflow(i128, -1, -1, 1, 0);
1028 try testMulWithOverflow(i128, minInt(i128), minInt(i128), 0, 1);
12301029}
12311030
12321031test "@mulWithOverflow u256" {
......@@ -1265,56 +1064,74 @@ test "@mulWithOverflow u256" {
12651064 }
12661065}
12671066
1067fn testSubWithOverflow(comptime T: type, a: T, b: T, sub: T, bit: u1) !void {
1068 const ov = @subWithOverflow(a, b);
1069 try expect(ov[0] == sub);
1070 try expect(ov[1] == bit);
1071}
1072
12681073test "@subWithOverflow" {
12691074 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12701075 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12711076 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12721077
1273 {
1274 var a: u8 = 1;
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 }
1078 try testSubWithOverflow(u8, 1, 2, 255, 1);
1079 try testSubWithOverflow(u8, 1, 1, 0, 0);
12871080
1288 {
1289 var a: u8 = 1;
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 }
1081 try testSubWithOverflow(u16, 10000, 10002, 65534, 1);
1082 try testSubWithOverflow(u16, 10000, 9999, 1, 0);
13001083
1301 {
1302 var a: usize = 6;
1303 var b: usize = 6;
1304 _ = .{ &a, &b };
1305 const ov = @subWithOverflow(a, b);
1306 try expect(ov[0] == 0);
1307 try expect(ov[1] == 0);
1308 }
1084 try testSubWithOverflow(usize, 6, 6, 0, 0);
1085 try testSubWithOverflow(usize, 6, 7, maxInt(usize), 1);
1086 try testSubWithOverflow(isize, -6, -6, 0, 0);
1087 try testSubWithOverflow(isize, minInt(isize), 6, maxInt(isize) - 5, 1);
1088}
13091089
1310 {
1311 var a: isize = -6;
1312 var b: isize = -6;
1313 _ = .{ &a, &b };
1314 const ov = @subWithOverflow(a, b);
1315 try expect(ov[0] == 0);
1316 try expect(ov[1] == 0);
1317 }
1090test "@subWithOverflow > 64 bits" {
1091 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1092 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1093 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1094 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1095
1096 try testSubWithOverflow(u65, 4, 105, maxInt(u65) - 100, 1);
1097 try testSubWithOverflow(u65, 1000, 100, 900, 0);
1098 try testSubWithOverflow(u65, maxInt(u65), maxInt(u65), 0, 0);
1099 try testSubWithOverflow(u65, maxInt(u65) - 1, maxInt(u65), maxInt(u65), 1);
1100 try testSubWithOverflow(u65, maxInt(u65), maxInt(u65) - 1, 1, 0);
1101
1102 try testSubWithOverflow(u128, 4, 105, maxInt(u128) - 100, 1);
1103 try testSubWithOverflow(u128, 1000, 100, 900, 0);
1104 try testSubWithOverflow(u128, maxInt(u128), maxInt(u128), 0, 0);
1105 try testSubWithOverflow(u128, maxInt(u128) - 1, maxInt(u128), maxInt(u128), 1);
1106 try testSubWithOverflow(u128, maxInt(u128), maxInt(u128) - 1, 1, 0);
1107
1108 try testSubWithOverflow(i65, 4, 105, -101, 0);
1109 try testSubWithOverflow(i65, 1000, 100, 900, 0);
1110 try testSubWithOverflow(i65, maxInt(i65), maxInt(i65), 0, 0);
1111 try testSubWithOverflow(i65, minInt(i65), minInt(i65), 0, 0);
1112 try testSubWithOverflow(i65, maxInt(i65) - 1, maxInt(i65), -1, 0);
1113 try testSubWithOverflow(i65, maxInt(i65), maxInt(i65) - 1, 1, 0);
1114 try testSubWithOverflow(i65, minInt(i65), 1, maxInt(i65), 1);
1115 try testSubWithOverflow(i65, maxInt(i65), minInt(i65), -1, 1);
1116 try testSubWithOverflow(i65, minInt(i65), maxInt(i65), 1, 1);
1117 try testSubWithOverflow(i65, maxInt(i65), -2, minInt(i65) + 1, 1);
1118
1119 try testSubWithOverflow(i128, 4, 105, -101, 0);
1120 try testSubWithOverflow(i128, 1000, 100, 900, 0);
1121 try testSubWithOverflow(i128, maxInt(i128), maxInt(i128), 0, 0);
1122 try testSubWithOverflow(i128, minInt(i128), minInt(i128), 0, 0);
1123 try testSubWithOverflow(i128, maxInt(i128) - 1, maxInt(i128), -1, 0);
1124 try testSubWithOverflow(i128, maxInt(i128), maxInt(i128) - 1, 1, 0);
1125 try testSubWithOverflow(i128, minInt(i128), 1, maxInt(i128), 1);
1126 try testSubWithOverflow(i128, maxInt(i128), minInt(i128), -1, 1);
1127 try testSubWithOverflow(i128, minInt(i128), maxInt(i128), 1, 1);
1128 try testSubWithOverflow(i128, maxInt(i128), -2, minInt(i128) + 1, 1);
1129}
1130
1131fn testShlWithOverflow(comptime T: type, a: T, b: math.Log2Int(T), shl: T, bit: u1) !void {
1132 const ov = @shlWithOverflow(a, b);
1133 try expect(ov[0] == shl);
1134 try expect(ov[1] == bit);
13181135}
13191136
13201137test "@shlWithOverflow" {
......@@ -1323,56 +1140,44 @@ test "@shlWithOverflow" {
13231140 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13241141 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13251142
1326 {
1327 var a: u4 = 2;
1328 _ = &a;
1329 var b: u2 = 1;
1330 var ov = @shlWithOverflow(a, b);
1331 try expect(ov[0] == 4);
1332 try expect(ov[1] == 0);
1143 try testShlWithOverflow(u4, 2, 1, 4, 0);
1144 try testShlWithOverflow(u4, 2, 3, 0, 1);
13331145
1334 b = 3;
1335 ov = @shlWithOverflow(a, b);
1336 try expect(ov[0] == 0);
1337 try expect(ov[1] == 1);
1338 }
1146 try testShlWithOverflow(i9, 127, 1, 254, 0);
1147 try testShlWithOverflow(i9, 127, 2, -4, 1);
13391148
1340 {
1341 var a: i9 = 127;
1342 _ = &a;
1343 var b: u4 = 1;
1344 var ov = @shlWithOverflow(a, b);
1345 try expect(ov[0] == 254);
1346 try expect(ov[1] == 0);
1149 try testShlWithOverflow(u16, 0b0010111111111111, 3, 0b0111111111111000, 1);
1150 try testShlWithOverflow(u16, 0b0010111111111111, 2, 0b1011111111111100, 0);
13471151
1348 b = 2;
1349 ov = @shlWithOverflow(a, b);
1350 try expect(ov[0] == -4);
1351 try expect(ov[1] == 1);
1352 }
1152 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 15, 0b1000_0000_0000_0000, 1);
1153 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 14, 0b1100_0000_0000_0000, 0);
1154}
13531155
1354 {
1355 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 3);
1356 try expect(ov[0] == 0b0111111111111000);
1357 try expect(ov[1] == 1);
1358 }
1359 {
1360 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 2);
1361 try expect(ov[0] == 0b1011111111111100);
1362 try expect(ov[1] == 0);
1363 }
1364 {
1365 var a: u16 = 0b0000_0000_0000_0011;
1366 _ = &a;
1367 var b: u4 = 15;
1368 var ov = @shlWithOverflow(a, b);
1369 try expect(ov[0] == 0b1000_0000_0000_0000);
1370 try expect(ov[1] == 1);
1371 b = 14;
1372 ov = @shlWithOverflow(a, b);
1373 try expect(ov[0] == 0b1100_0000_0000_0000);
1374 try expect(ov[1] == 0);
1375 }
1156test "@shlWithOverflow > 64 bits" {
1157 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1158 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1159 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1160 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1161
1162 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1163 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 8, 0x1_0000_0000_0000_0000, 0);
1164 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1165 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1166
1167 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 6, 0x4000_0000_0000_0000_0000000000000000, 0);
1168 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 7, 0x8000_0000_0000_0000_0000000000000000, 0);
1169 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 8, 0, 1);
1170 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1);
1171
1172 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1173 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 8, minInt(i65), 1);
1174 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1175 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1176
1177 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 6, 0x4000_0000_0000_0000_0000000000000000, 0);
1178 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 7, minInt(i128), 1);
1179 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 8, 0, 1);
1180 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1);
13761181}
13771182
13781183test "overflow arithmetic with u0 values" {