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...@@ -2669,47 +2669,53 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner
2669 .signed => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2669 .signed => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2670 .unsigned => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2670 .unsigned => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2671 },2671 },
2672 .rem => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2672 .rem => switch (int_info.signedness) {
2673 .shr => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),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 },
2674 .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),2680 .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2675 .@"and", .@"or", .xor => {2681 .@"and", .@"or", .xor => {
2676 const result = try func.allocStack(ty);2682 const result = try func.allocStack(ty);
2677 try func.emitWValue(result);2683 try func.emitWValue(result);
2678 const lhs_high_bit = try func.load(lhs, Type.u64, 0);2684 const lhs_lsb = try func.load(lhs, Type.u64, 0);
2679 const rhs_high_bit = try func.load(rhs, Type.u64, 0);2685 const rhs_lsb = try func.load(rhs, Type.u64, 0);
2680 const op_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);2686 const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2681 try func.store(.stack, op_high_bit, Type.u64, result.offset());2687 try func.store(.stack, op_lsb, Type.u64, result.offset());
26822688
2683 try func.emitWValue(result);2689 try func.emitWValue(result);
2684 const lhs_low_bit = try func.load(lhs, Type.u64, 8);2690 const lhs_msb = try func.load(lhs, Type.u64, 8);
2685 const rhs_low_bit = try func.load(rhs, Type.u64, 8);2691 const rhs_msb = try func.load(rhs, Type.u64, 8);
2686 const op_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);2692 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
2687 try func.store(.stack, op_low_bit, Type.u64, result.offset() + 8);2693 try func.store(.stack, op_msb, Type.u64, result.offset() + 8);
2688 return result;2694 return result;
2689 },2695 },
2690 .add, .sub => {2696 .add, .sub => {
2691 const result = try func.allocStack(ty);2697 const result = try func.allocStack(ty);
2692 var lhs_high_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);
2693 defer lhs_high_bit.free(func);2699 defer lhs_lsb.free(func);
2694 var rhs_high_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);
2695 defer rhs_high_bit.free(func);2701 defer rhs_lsb.free(func);
2696 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_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);
2697 defer high_op_res.free(func);2703 defer op_lsb.free(func);
26982704
2699 const lhs_low_bit = try func.load(lhs, Type.u64, 8);2705 const lhs_msb = try func.load(lhs, Type.u64, 8);
2700 const rhs_low_bit = try func.load(rhs, Type.u64, 8);2706 const rhs_msb = try func.load(rhs, Type.u64, 8);
2701 const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);2707 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
27022708
2703 const lt = if (op == .add) blk: {2709 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);
2705 } else if (op == .sub) blk: {2711 } 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);
2707 } else unreachable;2713 } else unreachable;
2708 const tmp = try func.intcast(lt, Type.u32, Type.u64);2714 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);
2710 defer tmp_op.free(func);2716 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);
2713 try func.store(result, tmp_op, Type.u64, 8);2719 try func.store(result, tmp_op, Type.u64, 8);
2714 return result;2720 return result;
2715 },2721 },
...@@ -4413,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4413,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4413 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);
4414 } else operand;4420 } else operand;
44154421
4416 // store msb first4422 // store lsb first
4417 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());4423 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 value4425 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value
4420 if (wanted.isSignedInt(mod)) {4426 if (wanted.isSignedInt(mod)) {
4421 try func.emitWValue(stack_ptr);4427 try func.emitWValue(stack_ptr);
4422 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);4428 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4423 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());4429 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
4424 } else {4430 } else {
4425 // Ensure memory of lsb is zero'd4431 // Ensure memory of msb is zero'd
4426 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);4432 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
4427 }4433 }
4428 return stack_ptr;4434 return stack_ptr;
...@@ -5523,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -5523,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
5523 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)});
5524 }5530 }
55255531
5526 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);5532 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5527 defer lhs_high_bit.free(func);5533 defer lhs_msb.free(func);
5528 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);5534 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5529 defer rhs_high_bit.free(func);5535 defer rhs_msb.free(func);
55305536
5531 switch (op) {5537 switch (op) {
5532 .eq, .neq => {5538 .eq, .neq => {
5533 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);
5534 const lhs_low_bit = try func.load(lhs, Type.u64, 8);5540 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5535 const rhs_low_bit = try func.load(rhs, Type.u64, 8);5541 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5536 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);
5537 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");
55385544
5539 switch (op) {5545 switch (op) {
...@@ -5545,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -5545,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
5545 else => {5551 else => {
5546 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;
5547 // leave those value on top of the stack for '.select'5553 // leave those value on top of the stack for '.select'
5548 const lhs_low_bit = try func.load(lhs, Type.u64, 8);5554 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5549 const rhs_low_bit = try func.load(rhs, Type.u64, 8);5555 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5550 _ = try func.cmp(lhs_low_bit, rhs_low_bit, ty, op);5556 _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5551 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, op);5557 _ = try func.cmp(lhs_msb, rhs_msb, ty, op);
5552 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);5558 _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq);
5553 try func.addTag(.select);5559 try func.addTag(.select);
5554 },5560 },
5555 }5561 }
...@@ -5980,6 +5986,26 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE...@@ -5980,6 +5986,26 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE
5980 return func.finishAir(inst, result, &.{ty_op.operand});5986 return func.finishAir(inst, result, &.{ty_op.operand});
5981}5987}
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
5983fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6009fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
5984 assert(op == .add or op == .sub);6010 assert(op == .add or op == .sub);
5985 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6011 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...@@ -5987,124 +6013,44 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
59876013
5988 const lhs = try func.resolveInst(extra.lhs);6014 const lhs = try func.resolveInst(extra.lhs);
5989 const rhs = try func.resolveInst(extra.rhs);6015 const rhs = try func.resolveInst(extra.rhs);
5990 const lhs_ty = func.typeOf(extra.lhs);6016 const ty = func.typeOf(extra.lhs);
5991 const pt = func.pt;6017 const pt = func.pt;
5992 const mod = pt.zcu;6018 const mod = pt.zcu;
59936019
5994 if (lhs_ty.zigTypeTag(mod) == .Vector) {6020 if (ty.zigTypeTag(mod) == .Vector) {
5995 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6021 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
5996 }6022 }
59976023
5998 const int_info = lhs_ty.intInfo(mod);6024 const int_info = ty.intInfo(mod);
5999 const is_signed = int_info.signedness == .signed;6025 const is_signed = int_info.signedness == .signed;
6000 const wasm_bits = toWasmBits(int_info.bits) orelse {6026 if (int_info.bits > 128) {
6001 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});6027 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
6002 };
6003
6004 if (wasm_bits == 128) {
6005 const result = try func.addSubWithOverflowBigInt(lhs, rhs, lhs_ty, func.typeOfIndex(inst), op);
6006 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6007 }6028 }
60086029
6009 const zero: WValue = switch (wasm_bits) {6030 const op_result = try func.wrapBinOp(lhs, rhs, ty, op);
6010 32 => .{ .imm32 = 0 },6031 var op_tmp = try op_result.toLocal(func, ty);
6011 64 => .{ .imm64 = 0 },6032 defer op_tmp.free(func);
6033
6034 const cmp_op: std.math.CompareOperator = switch (op) {
6035 .add => .lt,
6036 .sub => .gt,
6012 else => unreachable,6037 else => unreachable,
6013 };6038 };
6014
6015 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
6016 var result = if (wasm_bits != int_info.bits) blk: {
6017 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
6018 } else bin_op;
6019 defer result.free(func);
6020
6021 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
6022 const overflow_bit: WValue = if (is_signed) blk: {
6023 if (wasm_bits == int_info.bits) {
6024 const cmp_zero = try func.cmp(rhs, zero, lhs_ty, cmp_op);
6025 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
6026 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
6027 }
6028 break :blk try func.cmp(bin_op, bin_op, lhs_ty, .neq);
6029 } else if (wasm_bits == int_info.bits)
6030 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
6031 else
6032 try func.cmp(bin_op, result, lhs_ty, .neq);
6033 var overflow_local = try overflow_bit.toLocal(func, Type.u32);
6034 defer overflow_local.free(func);
6035
6036 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
6037 try func.store(result_ptr, result, lhs_ty, 0);
6038 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));
6039 try func.store(result_ptr, overflow_local, Type.u1, offset);
6040
6041 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
6042}
6043
6044fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
6045 const pt = func.pt;
6046 const mod = pt.zcu;
6047 assert(op == .add or op == .sub);
6048 const int_info = ty.intInfo(mod);
6049 const is_signed = int_info.signedness == .signed;
6050 if (int_info.bits != 128) {
6051 return func.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
6052 }
6053
6054 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
6055 defer lhs_high_bit.free(func);
6056 var lhs_low_bit = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6057 defer lhs_low_bit.free(func);
6058 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
6059 defer rhs_high_bit.free(func);
6060 var rhs_low_bit = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
6061 defer rhs_low_bit.free(func);
6062
6063 var low_op_res = try (try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(func, Type.u64);
6064 defer low_op_res.free(func);
6065 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
6066 defer high_op_res.free(func);
6067
6068 var lt = if (op == .add) blk: {
6069 break :blk try (try func.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
6070 } else if (op == .sub) blk: {
6071 break :blk try (try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
6072 } else unreachable;
6073 defer lt.free(func);
6074 var tmp = try (try func.intcast(lt, Type.u32, Type.u64)).toLocal(func, Type.u64);
6075 defer tmp.free(func);
6076 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
6077 defer tmp_op.free(func);
6078
6079 const overflow_bit = if (is_signed) blk: {6039 const overflow_bit = if (is_signed) blk: {
6080 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);6040 const zero = try intZeroValue(func, ty);
6081 const to_wrap = if (op == .add) wrap: {6041 const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt);
6082 break :wrap try func.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);6042 const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op);
6083 } else xor_low;6043 break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
6084 const xor_op = try func.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);6044 } else try func.cmp(op_tmp, lhs, ty, cmp_op);
6085 const wrap = try func.binOp(to_wrap, xor_op, Type.u64, .@"and");6045 var bit_tmp = try overflow_bit.toLocal(func, Type.u1);
6086 break :blk try func.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed6046 defer bit_tmp.free(func);
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);
61016047
6102 const result_ptr = try func.allocStack(result_ty);6048 const result = try func.allocStack(func.typeOfIndex(inst));
6103 try func.store(result_ptr, high_op_res, Type.u64, 0);6049 const offset: u32 = @intCast(ty.abiSize(pt));
6104 try func.store(result_ptr, tmp_op, Type.u64, 8);6050 try func.store(result, op_tmp, ty, 0);
6105 try func.store(result_ptr, overflow_local, Type.u1, 16);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 });
6108}6054}
61096055
6110fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6056fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -6115,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6115,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61156061
6116 const lhs = try func.resolveInst(extra.lhs);6062 const lhs = try func.resolveInst(extra.lhs);
6117 const rhs = try func.resolveInst(extra.rhs);6063 const rhs = try func.resolveInst(extra.rhs);
6118 const lhs_ty = func.typeOf(extra.lhs);6064 const ty = func.typeOf(extra.lhs);
6119 const rhs_ty = func.typeOf(extra.rhs);6065 const rhs_ty = func.typeOf(extra.rhs);
61206066
6121 if (lhs_ty.zigTypeTag(mod) == .Vector) {6067 if (ty.zigTypeTag(mod) == .Vector) {
6122 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6068 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
6123 }6069 }
61246070
6125 const int_info = lhs_ty.intInfo(mod);6071 const int_info = ty.intInfo(mod);
6126 const wasm_bits = toWasmBits(int_info.bits) orelse {6072 const wasm_bits = toWasmBits(int_info.bits) orelse {
6127 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});6073 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
6128 };6074 };
...@@ -6130,32 +6076,28 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6130,32 +6076,28 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6130 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types6076 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types
6131 // before we can perform any binary operation.6077 // before we can perform any binary operation.
6132 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(mod).bits).?;6078 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(mod).bits).?;
6133 const rhs_final = if (wasm_bits != rhs_wasm_bits) blk: {6079 // If wasm_bits == 128, compiler-rt expects i32 for shift
6134 const rhs_casted = try func.intcast(rhs, rhs_ty, lhs_ty);6080 const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: {
6135 break :blk try rhs_casted.toLocal(func, lhs_ty);6081 const rhs_casted = try func.intcast(rhs, rhs_ty, ty);
6082 break :blk try rhs_casted.toLocal(func, ty);
6136 } else rhs;6083 } 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);
6139 defer shl.free(func);6086 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
6145 const overflow_bit = blk: {6088 const overflow_bit = blk: {
6146 try func.emitWValue(lhs);6089 const shr = try func.binOp(shl, rhs_final, ty, .shr);
6147 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);6090 break :blk try func.cmp(shr, lhs, ty, .neq);
6148 break :blk try func.cmp(.stack, shr, lhs_ty, .neq);
6149 };6091 };
6150 var overflow_local = try overflow_bit.toLocal(func, Type.u1);6092 var overflow_local = try overflow_bit.toLocal(func, Type.u1);
6151 defer overflow_local.free(func);6093 defer overflow_local.free(func);
61526094
6153 const result_ptr = try func.allocStack(func.typeOfIndex(inst));6095 const result = try func.allocStack(func.typeOfIndex(inst));
6154 try func.store(result_ptr, result, lhs_ty, 0);6096 const offset: u32 = @intCast(ty.abiSize(pt));
6155 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));6097 try func.store(result, shl, ty, 0);
6156 try func.store(result_ptr, overflow_local, Type.u1, offset);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 });
6159}6101}
61606102
6161fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6103fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -6164,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6164,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61646106
6165 const lhs = try func.resolveInst(extra.lhs);6107 const lhs = try func.resolveInst(extra.lhs);
6166 const rhs = try func.resolveInst(extra.rhs);6108 const rhs = try func.resolveInst(extra.rhs);
6167 const lhs_ty = func.typeOf(extra.lhs);6109 const ty = func.typeOf(extra.lhs);
6168 const pt = func.pt;6110 const pt = func.pt;
6169 const mod = pt.zcu;6111 const mod = pt.zcu;
61706112
6171 if (lhs_ty.zigTypeTag(mod) == .Vector) {6113 if (ty.zigTypeTag(mod) == .Vector) {
6172 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6114 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
6173 }6115 }
61746116
...@@ -6177,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6177,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6177 var overflow_bit = try func.ensureAllocLocal(Type.u1);6119 var overflow_bit = try func.ensureAllocLocal(Type.u1);
6178 defer overflow_bit.free(func);6120 defer overflow_bit.free(func);
61796121
6180 const int_info = lhs_ty.intInfo(mod);6122 const int_info = ty.intInfo(mod);
6181 const wasm_bits = toWasmBits(int_info.bits) orelse {6123 const wasm_bits = toWasmBits(int_info.bits) orelse {
6182 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});
6183 };6125 };
...@@ -6189,147 +6131,106 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6189,147 +6131,106 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6189 };6131 };
61906132
6191 // for 32 bit integers we upcast it to a 64bit integer6133 // 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: {
6193 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;
6194 const lhs_upcast = try func.intcast(lhs, lhs_ty, new_ty);6136 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6195 const rhs_upcast = try func.intcast(rhs, lhs_ty, new_ty);6137 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6196 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);
6197 if (int_info.signedness == .unsigned) {6139 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6198 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);
6199 const wrap = try func.intcast(shr, new_ty, lhs_ty);6141 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
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);
6218 try func.addLabel(.local_set, overflow_bit.local.value);6142 try func.addLabel(.local_set, overflow_bit.local.value);
6219 break :blk try func.wrapOperand(bin_op, lhs_ty);6143 break :blk res;
6220 } else if (wasm_bits == 32) blk: {6144 } else if (wasm_bits == 64) blk: {
6221 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;
6222 defer bin_op.free(func);6146 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6223 const shift_imm: WValue = if (wasm_bits == 32)6147 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6224 .{ .imm32 = int_info.bits }6148 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6225 else6149 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6226 .{ .imm64 = int_info.bits };6150 const res_upcast = try func.intcast(res, ty, new_ty);
6227 const shr = try func.binOp(bin_op, shift_imm, lhs_ty, .shr);6151 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
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);
6262 try func.addLabel(.local_set, overflow_bit.local.value);6152 try func.addLabel(.local_set, overflow_bit.local.value);
6263 break :blk res;6153 break :blk res;
6264 } 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: {
6265 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);
6266 defer lhs_msb.free(func);
6267 var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6268 defer lhs_lsb.free(func);6156 defer lhs_lsb.free(func);
6269 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);
6270 defer rhs_msb.free(func);6158 defer lhs_msb.free(func);
6271 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);
6272 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);
62736163
6274 const mul1 = try func.callIntrinsic(6164 const cross_1 = try func.callIntrinsic(
6275 "__multi3",6165 "__multi3",
6276 &[_]InternPool.Index{.i64_type} ** 4,6166 &[_]InternPool.Index{.i64_type} ** 4,
6277 Type.i128,6167 Type.i128,
6278 &.{ lhs_lsb, zero, rhs_msb, zero },6168 &.{ lhs_msb, zero, rhs_lsb, zero },
6279 );6169 );
6280 const mul2 = try func.callIntrinsic(6170 const cross_2 = try func.callIntrinsic(
6281 "__multi3",6171 "__multi3",
6282 &[_]InternPool.Index{.i64_type} ** 4,6172 &[_]InternPool.Index{.i64_type} ** 4,
6283 Type.i128,6173 Type.i128,
6284 &.{ rhs_lsb, zero, lhs_msb, zero },6174 &.{ rhs_msb, zero, lhs_lsb, zero },
6285 );6175 );
6286 const mul3 = try func.callIntrinsic(6176 const mul_lsb = try func.callIntrinsic(
6287 "__multi3",6177 "__multi3",
6288 &[_]InternPool.Index{.i64_type} ** 4,6178 &[_]InternPool.Index{.i64_type} ** 4,
6289 Type.i128,6179 Type.i128,
6290 &.{ lhs_msb, zero, rhs_msb, zero },6180 &.{ rhs_lsb, zero, lhs_lsb, zero },
6291 );6181 );
62926182
6293 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);
6294 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);
6295 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");
6296 const mul1_lsb = try func.load(mul1, Type.u64, 8);6186 const cross_1_msb = try func.load(cross_1, Type.u64, 8);
6297 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);
6298 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");
6299 const mul2_lsb = try func.load(mul2, Type.u64, 8);6189 const cross_2_msb = try func.load(cross_2, Type.u64, 8);
6300 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);
6301 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");
63026192
6303 const mul1_msb = try func.load(mul1, Type.u64, 0);6193 const cross_1_lsb = try func.load(cross_1, Type.u64, 0);
6304 const mul2_msb = try func.load(mul2, Type.u64, 0);6194 const cross_2_lsb = try func.load(cross_2, Type.u64, 0);
6305 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);
63066196
6307 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);
6308 defer mul3_lsb.free(func);6198 defer mul_lsb_msb.free(func);
6309 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);
6310 defer mul_add2.free(func);6200 defer all_add.free(func);
6311 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);
63126202
6313 // result for overflow bit6203 // 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");
6315 try func.addLabel(.local_set, overflow_bit.local.value);6205 try func.addLabel(.local_set, overflow_bit.local.value);
63166206
6317 const tmp_result = try func.allocStack(Type.u128);6207 const tmp_result = try func.allocStack(Type.u128);
6318 try func.emitWValue(tmp_result);6208 try func.emitWValue(tmp_result);
6319 const mul3_msb = try func.load(mul3, Type.u64, 0);6209 const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0);
6320 try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset());6210 try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6321 try func.store(tmp_result, mul_add2, Type.u64, 8);6211 try func.store(tmp_result, all_add, Type.u64, 8);
6322 break :blk tmp_result;6212 break :blk tmp_result;
6323 } 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: {
6324 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);
6325 defer bin_op_local.free(func);6226 defer bin_op_local.free(func);
63266227
6327 const result_ptr = try func.allocStack(func.typeOfIndex(inst));6228 const result = try func.allocStack(func.typeOfIndex(inst));
6328 try func.store(result_ptr, bin_op_local, lhs_ty, 0);6229 const offset: u32 = @intCast(ty.abiSize(pt));
6329 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));6230 try func.store(result, bin_op_local, ty, 0);
6330 try func.store(result_ptr, overflow_bit, Type.u1, offset);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 });
6333}6234}
63346235
6335fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6236fn 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 {...@@ -6436,16 +6337,16 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6436 try func.addTag(.i32_wrap_i64);6337 try func.addTag(.i32_wrap_i64);
6437 },6338 },
6438 128 => {6339 128 => {
6439 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);
6440 defer lsb.free(func);6341 defer msb.free(func);
64416342
6442 try func.emitWValue(lsb);6343 try func.emitWValue(msb);
6443 try func.addTag(.i64_clz);6344 try func.addTag(.i64_clz);
6444 _ = try func.load(operand, Type.u64, 0);6345 _ = try func.load(operand, Type.u64, 0);
6445 try func.addTag(.i64_clz);6346 try func.addTag(.i64_clz);
6446 try func.emitWValue(.{ .imm64 = 64 });6347 try func.emitWValue(.{ .imm64 = 64 });
6447 try func.addTag(.i64_add);6348 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);
6449 try func.addTag(.select);6350 try func.addTag(.select);
6450 try func.addTag(.i32_wrap_i64);6351 try func.addTag(.i32_wrap_i64);
6451 },6352 },
...@@ -6496,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6496,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6496 try func.addTag(.i32_wrap_i64);6397 try func.addTag(.i32_wrap_i64);
6497 },6398 },
6498 128 => {6399 128 => {
6499 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);
6500 defer msb.free(func);6401 defer lsb.free(func);
65016402
6502 try func.emitWValue(msb);6403 try func.emitWValue(lsb);
6503 try func.addTag(.i64_ctz);6404 try func.addTag(.i64_ctz);
6504 _ = try func.load(operand, Type.u64, 8);6405 _ = try func.load(operand, Type.u64, 8);
6505 if (wasm_bits != int_info.bits) {6406 if (wasm_bits != int_info.bits) {
...@@ -6513,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6513,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6513 } else {6414 } else {
6514 try func.addTag(.i64_add);6415 try func.addTag(.i64_add);
6515 }6416 }
6516 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);6417 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6517 try func.addTag(.select);6418 try func.addTag(.select);
6518 try func.addTag(.i32_wrap_i64);6419 try func.addTag(.i32_wrap_i64);
6519 },6420 },
test/behavior/basic.zig+69-44
...@@ -1134,55 +1134,80 @@ test "pointer to struct literal with runtime field is constant" {...@@ -1134,55 +1134,80 @@ test "pointer to struct literal with runtime field is constant" {
1134 try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const);1134 try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const);
1135}1135}
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" {
1138 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1189 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 };
1179 inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| {1191 inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| {
1180 try S.doTheTestUnsigned(T);1192 try testUnsignedCmp(T);
1181 try comptime S.doTheTestUnsigned(T);1193 try comptime testUnsignedCmp(T);
1182 }1194 }
1183 inline for (.{ i8, i16, i32, i64, isize, i10, i20, i30, i60 }) |T| {1195 inline for (.{ i8, i16, i32, i64, isize, i10, i20, i30, i60 }) |T| {
1184 try S.doTheTestSigned(T);1196 try testSignedCmp(T);
1185 try comptime S.doTheTestSigned(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);
1186 }1211 }
1187}1212}
11881213
test/behavior/math.zig+223-418
...@@ -828,56 +828,72 @@ test "128-bit multiplication" {...@@ -828,56 +828,72 @@ test "128-bit multiplication" {
828 }828 }
829}829}
830830
831fn testAddWithOverflow(comptime T: type, a: T, b: T, add: T, bit: u1) !void {
832 const ov = @addWithOverflow(a, b);
833 try expect(ov[0] == add);
834 try expect(ov[1] == bit);
835}
836
831test "@addWithOverflow" {837test "@addWithOverflow" {
832 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO838 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
833 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO839 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
834 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO840 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
835 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;841 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
836842
837 {843 try testAddWithOverflow(u8, 250, 100, 94, 1);
838 var a: u8 = 250;844 try testAddWithOverflow(u8, 100, 150, 250, 0);
839 _ = &a;
840 const ov = @addWithOverflow(a, 100);
841 try expect(ov[0] == 94);
842 try expect(ov[1] == 1);
843 }
844 {
845 var a: u8 = 100;
846 _ = &a;
847 const ov = @addWithOverflow(a, 150);
848 try expect(ov[0] == 250);
849 try expect(ov[1] == 0);
850 }
851 {
852 var a: u8 = 200;
853 _ = &a;
854 var b: u8 = 99;
855 var ov = @addWithOverflow(a, b);
856 try expect(ov[0] == 43);
857 try expect(ov[1] == 1);
858 b = 55;
859 ov = @addWithOverflow(a, b);
860 try expect(ov[0] == 255);
861 try expect(ov[1] == 0);
862 }
863845
864 {846 try testAddWithOverflow(u8, 200, 99, 43, 1);
865 var a: usize = 6;847 try testAddWithOverflow(u8, 200, 55, 255, 0);
866 var b: usize = 6;
867 _ = .{ &a, &b };
868 const ov = @addWithOverflow(a, b);
869 try expect(ov[0] == 12);
870 try expect(ov[1] == 0);
871 }
872848
873 {849 try testAddWithOverflow(usize, 6, 6, 12, 0);
874 var a: isize = -6;850 try testAddWithOverflow(usize, maxInt(usize), 6, 5, 1);
875 var b: isize = -6;851
876 _ = .{ &a, &b };852 try testAddWithOverflow(isize, -6, -6, -12, 0);
877 const ov = @addWithOverflow(a, b);853 try testAddWithOverflow(isize, minInt(isize), -6, maxInt(isize) - 5, 1);
878 try expect(ov[0] == -12);854}
879 try expect(ov[1] == 0);855
880 }856test "@addWithOverflow > 64 bits" {
857 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
858 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
859 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
860 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
861
862 try testAddWithOverflow(u65, 4, 105, 109, 0);
863 try testAddWithOverflow(u65, 1000, 100, 1100, 0);
864 try testAddWithOverflow(u65, 100, maxInt(u65) - 99, 0, 1);
865 try testAddWithOverflow(u65, maxInt(u65), maxInt(u65), maxInt(u65) - 1, 1);
866 try testAddWithOverflow(u65, maxInt(u65) - 1, maxInt(u65), maxInt(u65) - 2, 1);
867 try testAddWithOverflow(u65, maxInt(u65), maxInt(u65) - 1, maxInt(u65) - 2, 1);
868
869 try testAddWithOverflow(u128, 4, 105, 109, 0);
870 try testAddWithOverflow(u128, 1000, 100, 1100, 0);
871 try testAddWithOverflow(u128, 100, maxInt(u128) - 99, 0, 1);
872 try testAddWithOverflow(u128, maxInt(u128), maxInt(u128), maxInt(u128) - 1, 1);
873 try testAddWithOverflow(u128, maxInt(u128) - 1, maxInt(u128), maxInt(u128) - 2, 1);
874 try testAddWithOverflow(u128, maxInt(u128), maxInt(u128) - 1, maxInt(u128) - 2, 1);
875
876 try testAddWithOverflow(i65, 4, -105, -101, 0);
877 try testAddWithOverflow(i65, 1000, 100, 1100, 0);
878 try testAddWithOverflow(i65, minInt(i65), 1, minInt(i65) + 1, 0);
879 try testAddWithOverflow(i65, maxInt(i65), minInt(i65), -1, 0);
880 try testAddWithOverflow(i65, minInt(i65), maxInt(i65), -1, 0);
881 try testAddWithOverflow(i65, maxInt(i65), -2, maxInt(i65) - 2, 0);
882 try testAddWithOverflow(i65, maxInt(i65), maxInt(i65), -2, 1);
883 try testAddWithOverflow(i65, minInt(i65), minInt(i65), 0, 1);
884 try testAddWithOverflow(i65, maxInt(i65) - 1, maxInt(i65), -3, 1);
885 try testAddWithOverflow(i65, maxInt(i65), maxInt(i65) - 1, -3, 1);
886
887 try testAddWithOverflow(i128, 4, -105, -101, 0);
888 try testAddWithOverflow(i128, 1000, 100, 1100, 0);
889 try testAddWithOverflow(i128, minInt(i128), 1, minInt(i128) + 1, 0);
890 try testAddWithOverflow(i128, maxInt(i128), minInt(i128), -1, 0);
891 try testAddWithOverflow(i128, minInt(i128), maxInt(i128), -1, 0);
892 try testAddWithOverflow(i128, maxInt(i128), -2, maxInt(i128) - 2, 0);
893 try testAddWithOverflow(i128, maxInt(i128), maxInt(i128), -2, 1);
894 try testAddWithOverflow(i128, minInt(i128), minInt(i128), 0, 1);
895 try testAddWithOverflow(i128, maxInt(i128) - 1, maxInt(i128), -3, 1);
896 try testAddWithOverflow(i128, maxInt(i128), maxInt(i128) - 1, -3, 1);
881}897}
882898
883test "small int addition" {899test "small int addition" {
...@@ -902,37 +918,22 @@ test "small int addition" {...@@ -902,37 +918,22 @@ test "small int addition" {
902 try expect(ov[1] == 1);918 try expect(ov[1] == 1);
903}919}
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
905test "basic @mulWithOverflow" {927test "basic @mulWithOverflow" {
906 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO928 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
907 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO929 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
908 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;930 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
909931
910 {932 try testMulWithOverflow(u8, 86, 3, 2, 1);
911 var a: u8 = 86;933 try testMulWithOverflow(u8, 85, 3, 255, 0);
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);
931934
932 b = 4;935 try testMulWithOverflow(u8, 123, 2, 246, 0);
933 ov = @mulWithOverflow(a, b);936 try testMulWithOverflow(u8, 123, 4, 236, 1);
934 try expect(ov[0] == 236);
935 try expect(ov[1] == 1);
936}937}
937938
938test "extensive @mulWithOverflow" {939test "extensive @mulWithOverflow" {
...@@ -940,173 +941,38 @@ test "extensive @mulWithOverflow" {...@@ -940,173 +941,38 @@ test "extensive @mulWithOverflow" {
940 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO941 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
941 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;942 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
942943
943 {944 try testMulWithOverflow(u5, 3, 10, 30, 0);
944 var a: u5 = 3;945 try testMulWithOverflow(u5, 3, 11, 1, 1);
945 _ = &a;946 try testMulWithOverflow(i5, 3, -5, -15, 0);
946 var b: u5 = 10;947 try testMulWithOverflow(i5, 3, -6, 14, 1);
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 }
1068948
1069 {949 try testMulWithOverflow(u8, 3, 85, 255, 0);
1070 var a: i30 = 3;950 try testMulWithOverflow(u8, 3, 86, 2, 1);
1071 _ = &a;951 try testMulWithOverflow(i8, 3, -42, -126, 0);
1072 var b: i30 = -0xaaaaaaa;952 try testMulWithOverflow(i8, 3, -43, 127, 1);
1073 var ov = @mulWithOverflow(a, b);
1074 try expect(ov[0] == -0x1ffffffe);
1075 try expect(ov[1] == 0);
1076953
1077 b = -0xaaaaaab;954 try testMulWithOverflow(u14, 3, 0x1555, 0x3fff, 0);
1078 ov = @mulWithOverflow(a, b);955 try testMulWithOverflow(u14, 3, 0x1556, 2, 1);
1079 try expect(ov[0] == 0x1fffffff);956 try testMulWithOverflow(i14, 3, -0xaaa, -0x1ffe, 0);
1080 try expect(ov[1] == 1);957 try testMulWithOverflow(i14, 3, -0xaab, 0x1fff, 1);
1081 }
1082958
1083 {959 try testMulWithOverflow(u16, 3, 0x5555, 0xffff, 0);
1084 var a: u32 = 3;960 try testMulWithOverflow(u16, 3, 0x5556, 2, 1);
1085 _ = &a;961 try testMulWithOverflow(i16, 3, -0x2aaa, -0x7ffe, 0);
1086 var b: u32 = 0x55555555;962 try testMulWithOverflow(i16, 3, -0x2aab, 0x7fff, 1);
1087 var ov = @mulWithOverflow(a, b);
1088 try expect(ov[0] == 0xffffffff);
1089 try expect(ov[1] == 0);
1090963
1091 b = 0x55555556;964 try testMulWithOverflow(u30, 3, 0x15555555, 0x3fffffff, 0);
1092 ov = @mulWithOverflow(a, b);965 try testMulWithOverflow(u30, 3, 0x15555556, 2, 1);
1093 try expect(ov[0] == 2);966 try testMulWithOverflow(i30, 3, -0xaaaaaaa, -0x1ffffffe, 0);
1094 try expect(ov[1] == 1);967 try testMulWithOverflow(i30, 3, -0xaaaaaab, 0x1fffffff, 1);
1095 }
1096968
1097 {969 try testMulWithOverflow(u32, 3, 0x55555555, 0xffffffff, 0);
1098 var a: i32 = 3;970 try testMulWithOverflow(u32, 3, 0x55555556, 2, 1);
1099 _ = &a;971 try testMulWithOverflow(i32, 3, -0x2aaaaaaa, -0x7ffffffe, 0);
1100 var b: i32 = -0x2aaaaaaa;972 try testMulWithOverflow(i32, 3, -0x2aaaaaab, 0x7fffffff, 1);
1101 var ov = @mulWithOverflow(a, b);
1102 try expect(ov[0] == -0x7ffffffe);
1103 try expect(ov[1] == 0);
1104973
1105 b = -0x2aaaaaab;974 try testMulWithOverflow(u31, 1 << 30, 1 << 30, 0, 1);
1106 ov = @mulWithOverflow(a, b);975 try testMulWithOverflow(i31, minInt(i31), minInt(i31), 0, 1);
1107 try expect(ov[0] == 0x7fffffff);
1108 try expect(ov[1] == 1);
1109 }
1110}976}
1111977
1112test "@mulWithOverflow bitsize > 32" {978test "@mulWithOverflow bitsize > 32" {
...@@ -1115,118 +981,51 @@ test "@mulWithOverflow bitsize > 32" {...@@ -1115,118 +981,51 @@ test "@mulWithOverflow bitsize > 32" {
1115 // aarch64 fails on a release build of the compiler.981 // aarch64 fails on a release build of the compiler.
1116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO982 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1117 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO983 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1118 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1119 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO984 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1120985
1121 {986 try testMulWithOverflow(u40, 3, 0x55_5555_5555, 0xff_ffff_ffff, 0);
1122 var a: u40 = 3;987 try testMulWithOverflow(u40, 3, 0x55_5555_5556, 2, 1);
1123 var b: u40 = 0x55_5555_5555;988 try testMulWithOverflow(u40, 0x10_0000_0000, 0x10_0000_0000, 0, 1);
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 }
1174989
1175 {990 try testMulWithOverflow(i40, 3, -0x2a_aaaa_aaaa, -0x7f_ffff_fffe, 0);
1176 var a: u62 = 3;991 try testMulWithOverflow(i40, 3, -0x2a_aaaa_aaab, 0x7f_ffff_ffff, 1);
1177 _ = &a;992 try testMulWithOverflow(i40, 6, -0x2a_aaaa_aaab, -2, 1);
1178 var b: u62 = 0x1555555555555555;993 try testMulWithOverflow(i40, 0x08_0000_0000, -0x08_0000_0001, -0x8_0000_0000, 1);
1179 var ov = @mulWithOverflow(a, b);
1180 try expect(ov[0] == 0x3fffffffffffffff);
1181 try expect(ov[1] == 0);
1182994
1183 b = 0x1555555555555556;995 try testMulWithOverflow(u62, 3, 0x1555555555555555, 0x3fffffffffffffff, 0);
1184 ov = @mulWithOverflow(a, b);996 try testMulWithOverflow(u62, 3, 0x1555555555555556, 2, 1);
1185 try expect(ov[0] == 2);997 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaaa, -0x1ffffffffffffffe, 0);
1186 try expect(ov[1] == 1);998 try testMulWithOverflow(i62, 3, -0xaaaaaaaaaaaaaab, 0x1fffffffffffffff, 1);
1187 }
1188999
1189 {1000 try testMulWithOverflow(u64, 3, 0x5555555555555555, 0xffffffffffffffff, 0);
1190 var a: i62 = 3;1001 try testMulWithOverflow(u64, 3, 0x5555555555555556, 2, 1);
1191 _ = &a;1002 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaaa, -0x7ffffffffffffffe, 0);
1192 var b: i62 = -0xaaaaaaaaaaaaaaa;1003 try testMulWithOverflow(i64, 3, -0x2aaaaaaaaaaaaaab, 0x7fffffffffffffff, 1);
1193 var ov = @mulWithOverflow(a, b);
1194 try expect(ov[0] == -0x1ffffffffffffffe);
1195 try expect(ov[1] == 0);
11961004
1197 b = -0xaaaaaaaaaaaaaab;1005 try testMulWithOverflow(u63, 1 << 62, 1 << 62, 0, 1);
1198 ov = @mulWithOverflow(a, b);1006 try testMulWithOverflow(i63, minInt(i63), minInt(i63), 0, 1);
1199 try expect(ov[0] == 0x1fffffffffffffff);1007}
1200 try expect(ov[1] == 1);
1201 }
12021008
1203 {1009test "@mulWithOverflow bitsize 128 bits" {
1204 var a: u64 = 3;1010 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1205 _ = &a;1011 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1206 var b: u64 = 0x5555555555555555;1012 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1207 var ov = @mulWithOverflow(a, b);1013 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1208 try expect(ov[0] == 0xffffffffffffffff);1014 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1209 try expect(ov[1] == 0);1015 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12101016
1211 b = 0x5555555555555556;1017 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555555, 0xffffffffffffffff_ffffffffffffffff, 0);
1212 ov = @mulWithOverflow(a, b);1018 try testMulWithOverflow(u128, 3, 0x5555555555555555_5555555555555556, 2, 1);
1213 try expect(ov[0] == 2);
1214 try expect(ov[1] == 1);
1215 }
12161019
1217 {1020 try testMulWithOverflow(u128, 1 << 100, 1 << 27, 1 << 127, 0);
1218 var a: i64 = 3;1021 try testMulWithOverflow(u128, maxInt(u128), maxInt(u128), 1, 1);
1219 _ = &a;1022 try testMulWithOverflow(u128, 1 << 100, 1 << 28, 0, 1);
1220 var b: i64 = -0x2aaaaaaaaaaaaaaa;1023 try testMulWithOverflow(u128, 1 << 127, 1 << 127, 0, 1);
1221 var ov = @mulWithOverflow(a, b);
1222 try expect(ov[0] == -0x7ffffffffffffffe);
1223 try expect(ov[1] == 0);
12241024
1225 b = -0x2aaaaaaaaaaaaaab;1025 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaaa, -0x7fffffffffffffff_fffffffffffffffe, 0);
1226 ov = @mulWithOverflow(a, b);1026 try testMulWithOverflow(i128, 3, -0x2aaaaaaaaaaaaaaa_aaaaaaaaaaaaaaab, 0x7fffffffffffffff_ffffffffffffffff, 1);
1227 try expect(ov[0] == 0x7fffffffffffffff);1027 try testMulWithOverflow(i128, -1, -1, 1, 0);
1228 try expect(ov[1] == 1);1028 try testMulWithOverflow(i128, minInt(i128), minInt(i128), 0, 1);
1229 }
1230}1029}
12311030
1232test "@mulWithOverflow u256" {1031test "@mulWithOverflow u256" {
...@@ -1265,56 +1064,74 @@ test "@mulWithOverflow u256" {...@@ -1265,56 +1064,74 @@ test "@mulWithOverflow u256" {
1265 }1064 }
1266}1065}
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
1268test "@subWithOverflow" {1073test "@subWithOverflow" {
1269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1074 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1270 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1075 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1271 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1076 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12721077
1273 {1078 try testSubWithOverflow(u8, 1, 2, 255, 1);
1274 var a: u8 = 1;1079 try testSubWithOverflow(u8, 1, 1, 0, 0);
1275 _ = &a;
1276 const ov = @subWithOverflow(a, 2);
1277 try expect(ov[0] == 255);
1278 try expect(ov[1] == 1);
1279 }
1280 {
1281 var a: u8 = 1;
1282 _ = &a;
1283 const ov = @subWithOverflow(a, 1);
1284 try expect(ov[0] == 0);
1285 try expect(ov[1] == 0);
1286 }
12871080
1288 {1081 try testSubWithOverflow(u16, 10000, 10002, 65534, 1);
1289 var a: u8 = 1;1082 try testSubWithOverflow(u16, 10000, 9999, 1, 0);
1290 _ = &a;
1291 var b: u8 = 2;
1292 var ov = @subWithOverflow(a, b);
1293 try expect(ov[0] == 255);
1294 try expect(ov[1] == 1);
1295 b = 1;
1296 ov = @subWithOverflow(a, b);
1297 try expect(ov[0] == 0);
1298 try expect(ov[1] == 0);
1299 }
13001083
1301 {1084 try testSubWithOverflow(usize, 6, 6, 0, 0);
1302 var a: usize = 6;1085 try testSubWithOverflow(usize, 6, 7, maxInt(usize), 1);
1303 var b: usize = 6;1086 try testSubWithOverflow(isize, -6, -6, 0, 0);
1304 _ = .{ &a, &b };1087 try testSubWithOverflow(isize, minInt(isize), 6, maxInt(isize) - 5, 1);
1305 const ov = @subWithOverflow(a, b);1088}
1306 try expect(ov[0] == 0);
1307 try expect(ov[1] == 0);
1308 }
13091089
1310 {1090test "@subWithOverflow > 64 bits" {
1311 var a: isize = -6;1091 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1312 var b: isize = -6;1092 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1313 _ = .{ &a, &b };1093 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1314 const ov = @subWithOverflow(a, b);1094 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1315 try expect(ov[0] == 0);1095
1316 try expect(ov[1] == 0);1096 try testSubWithOverflow(u65, 4, 105, maxInt(u65) - 100, 1);
1317 }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);
1318}1135}
13191136
1320test "@shlWithOverflow" {1137test "@shlWithOverflow" {
...@@ -1323,56 +1140,44 @@ test "@shlWithOverflow" {...@@ -1323,56 +1140,44 @@ test "@shlWithOverflow" {
1323 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1140 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1324 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1141 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13251142
1326 {1143 try testShlWithOverflow(u4, 2, 1, 4, 0);
1327 var a: u4 = 2;1144 try testShlWithOverflow(u4, 2, 3, 0, 1);
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);
13331145
1334 b = 3;1146 try testShlWithOverflow(i9, 127, 1, 254, 0);
1335 ov = @shlWithOverflow(a, b);1147 try testShlWithOverflow(i9, 127, 2, -4, 1);
1336 try expect(ov[0] == 0);
1337 try expect(ov[1] == 1);
1338 }
13391148
1340 {1149 try testShlWithOverflow(u16, 0b0010111111111111, 3, 0b0111111111111000, 1);
1341 var a: i9 = 127;1150 try testShlWithOverflow(u16, 0b0010111111111111, 2, 0b1011111111111100, 0);
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);
13471151
1348 b = 2;1152 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 15, 0b1000_0000_0000_0000, 1);
1349 ov = @shlWithOverflow(a, b);1153 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 14, 0b1100_0000_0000_0000, 0);
1350 try expect(ov[0] == -4);1154}
1351 try expect(ov[1] == 1);
1352 }
13531155
1354 {1156test "@shlWithOverflow > 64 bits" {
1355 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 3);1157 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1356 try expect(ov[0] == 0b0111111111111000);1158 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1357 try expect(ov[1] == 1);1159 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1358 }1160 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1359 {1161
1360 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 2);1162 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1361 try expect(ov[0] == 0b1011111111111100);1163 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 8, 0x1_0000_0000_0000_0000, 0);
1362 try expect(ov[1] == 0);1164 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1363 }1165 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1364 {1166
1365 var a: u16 = 0b0000_0000_0000_0011;1167 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 6, 0x4000_0000_0000_0000_0000000000000000, 0);
1366 _ = &a;1168 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 7, 0x8000_0000_0000_0000_0000000000000000, 0);
1367 var b: u4 = 15;1169 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 8, 0, 1);
1368 var ov = @shlWithOverflow(a, b);1170 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1);
1369 try expect(ov[0] == 0b1000_0000_0000_0000);1171
1370 try expect(ov[1] == 1);1172 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1371 b = 14;1173 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 8, minInt(i65), 1);
1372 ov = @shlWithOverflow(a, b);1174 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1373 try expect(ov[0] == 0b1100_0000_0000_0000);1175 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1374 try expect(ov[1] == 0);1176
1375 }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);
1376}1181}
13771182
1378test "overflow arithmetic with u0 values" {1183test "overflow arithmetic with u0 values" {