| author | |
| committer | |
| log | 93c546c8c9bf20b70b49546f278b4663b5c3f6d7 |
| tree | 6d4083ab7ac0d3259be51388675c92c3775c6f67 |
| parent | 5f78e28899f99985fcc3d957b0fac08deffac81c |
| parent | a0795f11dfbe80fcdab96d2dc6d2de96eddf9f5c |
| signature |
stage2-wasm: overflow ops improvement3 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 | 2669 | .signed => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2670 | 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 }), | |
| 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 | }, | |
| 2674 | 2680 | .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2675 | 2681 | .@"and", .@"or", .xor => { |
| 2676 | 2682 | const result = try func.allocStack(ty); |
| 2677 | 2683 | 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()); | |
| 2682 | 2688 | |
| 2683 | 2689 | 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); | |
| 2688 | 2694 | return result; |
| 2689 | 2695 | }, |
| 2690 | 2696 | .add, .sub => { |
| 2691 | 2697 | 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); | |
| 2698 | 2704 | |
| 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); | |
| 2702 | 2708 | |
| 2703 | 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 | 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 | 2713 | } else unreachable; |
| 2708 | 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 | 2716 | defer tmp_op.free(func); |
| 2711 | 2717 | |
| 2712 | try func.store(result, high_op_res, Type.u64, 0); | |
| 2718 | try func.store(result, op_lsb, Type.u64, 0); | |
| 2713 | 2719 | try func.store(result, tmp_op, Type.u64, 8); |
| 2714 | 2720 | return result; |
| 2715 | 2721 | }, |
| ... | ... | @@ -4413,16 +4419,16 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4413 | 4419 | break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty); |
| 4414 | 4420 | } else operand; |
| 4415 | 4421 | |
| 4416 | // store msb first | |
| 4422 | // store lsb first | |
| 4417 | 4423 | try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset()); |
| 4418 | 4424 | |
| 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 | |
| 4420 | 4426 | if (wanted.isSignedInt(mod)) { |
| 4421 | 4427 | try func.emitWValue(stack_ptr); |
| 4422 | 4428 | const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); |
| 4423 | 4429 | try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); |
| 4424 | 4430 | } else { |
| 4425 | // Ensure memory of lsb is zero'd | |
| 4431 | // Ensure memory of msb is zero'd | |
| 4426 | 4432 | try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); |
| 4427 | 4433 | } |
| 4428 | 4434 | return stack_ptr; |
| ... | ... | @@ -5523,17 +5529,17 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std |
| 5523 | 5529 | return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(pt)}); |
| 5524 | 5530 | } |
| 5525 | 5531 | |
| 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); | |
| 5530 | 5536 | |
| 5531 | 5537 | switch (op) { |
| 5532 | 5538 | .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); | |
| 5537 | 5543 | const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or"); |
| 5538 | 5544 | |
| 5539 | 5545 | switch (op) { |
| ... | ... | @@ -5545,11 +5551,11 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std |
| 5545 | 5551 | else => { |
| 5546 | 5552 | const ty = if (operand_ty.isSignedInt(mod)) Type.i64 else Type.u64; |
| 5547 | 5553 | // 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); | |
| 5553 | 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 | 5986 | return func.finishAir(inst, result, &.{ty_op.operand}); |
| 5981 | 5987 | } |
| 5982 | 5988 | |
| 5989 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits | |
| 5990 | fn 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 | ||
| 5983 | 6009 | fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 5984 | 6010 | assert(op == .add or op == .sub); |
| 5985 | 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 | 6013 | |
| 5988 | 6014 | const lhs = try func.resolveInst(extra.lhs); |
| 5989 | 6015 | const rhs = try func.resolveInst(extra.rhs); |
| 5990 | const lhs_ty = func.typeOf(extra.lhs); | |
| 6016 | const ty = func.typeOf(extra.lhs); | |
| 5991 | 6017 | const pt = func.pt; |
| 5992 | 6018 | const mod = pt.zcu; |
| 5993 | 6019 | |
| 5994 | if (lhs_ty.zigTypeTag(mod) == .Vector) { | |
| 6020 | if (ty.zigTypeTag(mod) == .Vector) { | |
| 5995 | 6021 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 5996 | 6022 | } |
| 5997 | 6023 | |
| 5998 | const int_info = lhs_ty.intInfo(mod); | |
| 6024 | const int_info = ty.intInfo(mod); | |
| 5999 | 6025 | const is_signed = int_info.signedness == .signed; |
| 6000 | const wasm_bits = toWasmBits(int_info.bits) orelse { | |
| 6026 | if (int_info.bits > 128) { | |
| 6001 | 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 | } |
| 6008 | 6029 | |
| 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, | |
| 6012 | 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 | ||
| 6044 | fn 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 | 6039 | 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); | |
| 6101 | 6047 | |
| 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); | |
| 6106 | 6052 | |
| 6107 | return result_ptr; | |
| 6053 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | |
| 6108 | 6054 | } |
| 6109 | 6055 | |
| 6110 | 6056 | fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -6115,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6115 | 6061 | |
| 6116 | 6062 | const lhs = try func.resolveInst(extra.lhs); |
| 6117 | 6063 | const rhs = try func.resolveInst(extra.rhs); |
| 6118 | const lhs_ty = func.typeOf(extra.lhs); | |
| 6064 | const ty = func.typeOf(extra.lhs); | |
| 6119 | 6065 | const rhs_ty = func.typeOf(extra.rhs); |
| 6120 | 6066 | |
| 6121 | if (lhs_ty.zigTypeTag(mod) == .Vector) { | |
| 6067 | if (ty.zigTypeTag(mod) == .Vector) { | |
| 6122 | 6068 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 6123 | 6069 | } |
| 6124 | 6070 | |
| 6125 | const int_info = lhs_ty.intInfo(mod); | |
| 6071 | const int_info = ty.intInfo(mod); | |
| 6126 | 6072 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6127 | 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 | 6076 | // Ensure rhs is coerced to lhs as they must have the same WebAssembly types |
| 6131 | 6077 | // before we can perform any binary operation. |
| 6132 | 6078 | 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); | |
| 6136 | 6083 | } else rhs; |
| 6137 | 6084 | |
| 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 | 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) | |
| 6144 | 6087 | |
| 6145 | 6088 | 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); | |
| 6149 | 6091 | }; |
| 6150 | 6092 | var overflow_local = try overflow_bit.toLocal(func, Type.u1); |
| 6151 | 6093 | defer overflow_local.free(func); |
| 6152 | 6094 | |
| 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); | |
| 6157 | 6099 | |
| 6158 | return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs }); | |
| 6100 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | |
| 6159 | 6101 | } |
| 6160 | 6102 | |
| 6161 | 6103 | fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -6164,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6164 | 6106 | |
| 6165 | 6107 | const lhs = try func.resolveInst(extra.lhs); |
| 6166 | 6108 | const rhs = try func.resolveInst(extra.rhs); |
| 6167 | const lhs_ty = func.typeOf(extra.lhs); | |
| 6109 | const ty = func.typeOf(extra.lhs); | |
| 6168 | 6110 | const pt = func.pt; |
| 6169 | 6111 | const mod = pt.zcu; |
| 6170 | 6112 | |
| 6171 | if (lhs_ty.zigTypeTag(mod) == .Vector) { | |
| 6113 | if (ty.zigTypeTag(mod) == .Vector) { | |
| 6172 | 6114 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 6173 | 6115 | } |
| 6174 | 6116 | |
| ... | ... | @@ -6177,7 +6119,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6177 | 6119 | var overflow_bit = try func.ensureAllocLocal(Type.u1); |
| 6178 | 6120 | defer overflow_bit.free(func); |
| 6179 | 6121 | |
| 6180 | const int_info = lhs_ty.intInfo(mod); | |
| 6122 | const int_info = ty.intInfo(mod); | |
| 6181 | 6123 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6182 | 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 | 6131 | }; |
| 6190 | 6132 | |
| 6191 | 6133 | // 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 | 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); | |
| 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); | |
| 6196 | 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) { | |
| 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); | |
| 6218 | 6142 | 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); | |
| 6262 | 6152 | try func.addLabel(.local_set, overflow_bit.local.value); |
| 6263 | 6153 | break :blk res; |
| 6264 | 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); | |
| 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); | |
| 6268 | 6156 | 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); | |
| 6272 | 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); | |
| 6273 | 6163 | |
| 6274 | const mul1 = try func.callIntrinsic( | |
| 6164 | const cross_1 = try func.callIntrinsic( | |
| 6275 | 6165 | "__multi3", |
| 6276 | 6166 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6277 | 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 | 6171 | "__multi3", |
| 6282 | 6172 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6283 | 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 | 6177 | "__multi3", |
| 6288 | 6178 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6289 | 6179 | Type.i128, |
| 6290 | &.{ lhs_msb, zero, rhs_msb, zero }, | |
| 6180 | &.{ rhs_lsb, zero, lhs_lsb, zero }, | |
| 6291 | 6181 | ); |
| 6292 | 6182 | |
| 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); | |
| 6312 | 6202 | |
| 6313 | 6203 | // 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 | 6205 | try func.addLabel(.local_set, overflow_bit.local.value); |
| 6316 | 6206 | |
| 6317 | 6207 | const tmp_result = try func.allocStack(Type.u128); |
| 6318 | 6208 | 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); | |
| 6322 | 6212 | 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); | |
| 6325 | 6226 | defer bin_op_local.free(func); |
| 6326 | 6227 | |
| 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); | |
| 6331 | 6232 | |
| 6332 | return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs }); | |
| 6233 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | |
| 6333 | 6234 | } |
| 6334 | 6235 | |
| 6335 | 6236 | fn 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 | 6337 | try func.addTag(.i32_wrap_i64); |
| 6437 | 6338 | }, |
| 6438 | 6339 | 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); | |
| 6441 | 6342 | |
| 6442 | try func.emitWValue(lsb); | |
| 6343 | try func.emitWValue(msb); | |
| 6443 | 6344 | try func.addTag(.i64_clz); |
| 6444 | 6345 | _ = try func.load(operand, Type.u64, 0); |
| 6445 | 6346 | try func.addTag(.i64_clz); |
| 6446 | 6347 | try func.emitWValue(.{ .imm64 = 64 }); |
| 6447 | 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 | 6350 | try func.addTag(.select); |
| 6450 | 6351 | try func.addTag(.i32_wrap_i64); |
| 6451 | 6352 | }, |
| ... | ... | @@ -6496,10 +6397,10 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6496 | 6397 | try func.addTag(.i32_wrap_i64); |
| 6497 | 6398 | }, |
| 6498 | 6399 | 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); | |
| 6501 | 6402 | |
| 6502 | try func.emitWValue(msb); | |
| 6403 | try func.emitWValue(lsb); | |
| 6503 | 6404 | try func.addTag(.i64_ctz); |
| 6504 | 6405 | _ = try func.load(operand, Type.u64, 8); |
| 6505 | 6406 | if (wasm_bits != int_info.bits) { |
| ... | ... | @@ -6513,7 +6414,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6513 | 6414 | } else { |
| 6514 | 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 | 6418 | try func.addTag(.select); |
| 6518 | 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 | 1134 | try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const); |
| 1135 | 1135 | } |
| 1136 | 1136 | |
| 1137 | test "integer compare" { | |
| 1137 | fn 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 | ||
| 1164 | fn 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 | ||
| 1188 | test "integer compare <= 64 bits" { | |
| 1138 | 1189 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1139 | 1190 | |
| 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 | 1191 | 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); | |
| 1182 | 1194 | } |
| 1183 | 1195 | 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 | ||
| 1201 | test "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 | } |
| 1188 | 1213 |
test/behavior/math.zig+223-418| ... | ... | @@ -828,56 +828,72 @@ test "128-bit multiplication" { |
| 828 | 828 | } |
| 829 | 829 | } |
| 830 | 830 | |
| 831 | fn 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 | ||
| 831 | 837 | test "@addWithOverflow" { |
| 832 | 838 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 833 | 839 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 834 | 840 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 835 | 841 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 836 | 842 | |
| 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); | |
| 863 | 845 | |
| 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); | |
| 872 | 848 | |
| 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 | ||
| 856 | test "@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 | } |
| 882 | 898 | |
| 883 | 899 | test "small int addition" { |
| ... | ... | @@ -902,37 +918,22 @@ test "small int addition" { |
| 902 | 918 | try expect(ov[1] == 1); |
| 903 | 919 | } |
| 904 | 920 | |
| 921 | fn 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 | ||
| 905 | 927 | test "basic @mulWithOverflow" { |
| 906 | 928 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 907 | 929 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 908 | 930 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 909 | 931 | |
| 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); | |
| 931 | 934 | |
| 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); | |
| 936 | 937 | } |
| 937 | 938 | |
| 938 | 939 | test "extensive @mulWithOverflow" { |
| ... | ... | @@ -940,173 +941,38 @@ test "extensive @mulWithOverflow" { |
| 940 | 941 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 941 | 942 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 942 | 943 | |
| 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); | |
| 1068 | 948 | |
| 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); | |
| 1076 | 953 | |
| 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); | |
| 1082 | 958 | |
| 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); | |
| 1090 | 963 | |
| 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); | |
| 1096 | 968 | |
| 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); | |
| 1104 | 973 | |
| 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); | |
| 1110 | 976 | } |
| 1111 | 977 | |
| 1112 | 978 | test "@mulWithOverflow bitsize > 32" { |
| ... | ... | @@ -1115,118 +981,51 @@ test "@mulWithOverflow bitsize > 32" { |
| 1115 | 981 | // aarch64 fails on a release build of the compiler. |
| 1116 | 982 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1117 | 983 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1118 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 1119 | 984 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1120 | 985 | |
| 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); | |
| 1174 | 989 | |
| 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); | |
| 1182 | 994 | |
| 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); | |
| 1188 | 999 | |
| 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); | |
| 1196 | 1004 | |
| 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 | } | |
| 1202 | 1008 | |
| 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); | |
| 1009 | test "@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 | |
| 1210 | 1016 | |
| 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); | |
| 1216 | 1019 | |
| 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); | |
| 1224 | 1024 | |
| 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); | |
| 1230 | 1029 | } |
| 1231 | 1030 | |
| 1232 | 1031 | test "@mulWithOverflow u256" { |
| ... | ... | @@ -1265,56 +1064,74 @@ test "@mulWithOverflow u256" { |
| 1265 | 1064 | } |
| 1266 | 1065 | } |
| 1267 | 1066 | |
| 1067 | fn 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 | ||
| 1268 | 1073 | test "@subWithOverflow" { |
| 1269 | 1074 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1270 | 1075 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1271 | 1076 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1272 | 1077 | |
| 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); | |
| 1287 | 1080 | |
| 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); | |
| 1300 | 1083 | |
| 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 | } | |
| 1309 | 1089 | |
| 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 | } | |
| 1090 | test "@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 | ||
| 1131 | fn 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 | } |
| 1319 | 1136 | |
| 1320 | 1137 | test "@shlWithOverflow" { |
| ... | ... | @@ -1323,56 +1140,44 @@ test "@shlWithOverflow" { |
| 1323 | 1140 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 1324 | 1141 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1325 | 1142 | |
| 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); | |
| 1333 | 1145 | |
| 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); | |
| 1339 | 1148 | |
| 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); | |
| 1347 | 1151 | |
| 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 | } | |
| 1353 | 1155 | |
| 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 | } | |
| 1156 | test "@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); | |
| 1376 | 1181 | } |
| 1377 | 1182 | |
| 1378 | 1183 | test "overflow arithmetic with u0 values" { |