| ... | @@ -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()); |
| 2682 | | 2688 | |
| 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); |
| 2698 | | 2704 | |
| 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); |
| 2702 | | 2708 | |
| 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); |
| 2711 | | 2717 | |
| 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; |
| 4415 | | 4421 | |
| 4416 | // store msb first | 4422 | // 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()); |
| 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 | 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'd | 4431 | // 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 | } |
| 5525 | | 5531 | |
| 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); |
| 5530 | | 5536 | |
| 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"); |
| 5538 | | 5544 | |
| 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 | } |
| 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 | fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 6009 | fn 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 |
| 5987 | | 6013 | |
| 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; |
| 5993 | | 6019 | |
| 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 | } |
| 5997 | | 6023 | |
| 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 | } |
| 6008 | | 6029 | |
| 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 | | | |
| 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 | 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 signed | 6046 | 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); | | |
| 6101 | | 6047 | |
| 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); |
| 6106 | | 6052 | |
| 6107 | return result_ptr; | 6053 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6108 | } | 6054 | } |
| 6109 | | 6055 | |
| 6110 | fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 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,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6115 | | 6061 | |
| 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); |
| 6120 | | 6066 | |
| 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 | } |
| 6124 | | 6070 | |
| 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 types | 6076 | // 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; |
| 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 | 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) | | |
| 6144 | | 6087 | |
| 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); |
| 6152 | | 6094 | |
| 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); |
| 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 | fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 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,11 +6106,11 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6164 | | 6106 | |
| 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; |
| 6170 | | 6112 | |
| 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 | } |
| 6174 | | 6116 | |
| ... | @@ -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); |
| 6179 | | 6121 | |
| 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 | }; |
| 6190 | | 6132 | |
| 6191 | // for 32 bit integers we upcast it to a 64bit integer | 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 | 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 | else | 6149 | 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); |
| 6273 | | 6163 | |
| 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 | ); |
| 6292 | | 6182 | |
| 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"); |
| 6302 | | 6192 | |
| 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); |
| 6306 | | 6196 | |
| 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); |
| 6312 | | 6202 | |
| 6313 | // result for overflow bit | 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 | try func.addLabel(.local_set, overflow_bit.local.value); | 6205 | try func.addLabel(.local_set, overflow_bit.local.value); |
| 6316 | | 6206 | |
| 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); |
| 6326 | | 6227 | |
| 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); |
| 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 | fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 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,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); |
| 6441 | | 6342 | |
| 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); |
| 6501 | | 6402 | |
| 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 | }, |