diff options
| author | Pavel Verigo <paul.verigo@gmail.com> | 2024-07-23 17:06:18 +0200 |
|---|---|---|
| committer | Pavel Verigo <paul.verigo@gmail.com> | 2024-07-23 17:06:18 +0200 |
| commit | d71312d1048d93d5e806a600066426fd2db9b1fb (patch) | |
| tree | 7cdbc18071685a8c14b04743a343e8eccbd3d9de /src/arch/wasm/CodeGen.zig | |
| parent | 0c6aa44bc3c0670f3533c503ad9921c2439d8e35 (diff) | |
| download | zig-d71312d1048d93d5e806a600066426fd2db9b1fb.tar.gz zig-d71312d1048d93d5e806a600066426fd2db9b1fb.zip | |
stage2-wasm: mul_sat 32 bits <=, i64, i128
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 102 |
1 files changed, 101 insertions, 1 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e1b362ef2e..c23b8f3f72 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1837,6 +1837,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .sub_sat => func.airSatBinOp(inst, .sub), .sub_wrap => func.airWrapBinOp(inst, .sub), .mul => func.airBinOp(inst, .mul), + .mul_sat => func.airSatMul(inst), .mul_wrap => func.airWrapBinOp(inst, .mul), .div_float, .div_exact => func.airDiv(inst), .div_trunc => func.airDivTrunc(inst), @@ -2002,7 +2003,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .error_set_has_value => func.airErrorSetHasValue(inst), .frame_addr => func.airFrameAddress(inst), - .mul_sat, .assembly, .is_err_ptr, .is_non_err_ptr, @@ -6783,6 +6783,106 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); } +fn airSatMul(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + + const pt = func.pt; + const mod = pt.zcu; + const ty = func.typeOfIndex(inst); + const int_info = ty.intInfo(mod); + const is_signed = int_info.signedness == .signed; + + const lhs = try func.resolveInst(bin_op.lhs); + const rhs = try func.resolveInst(bin_op.rhs); + const wasm_bits = toWasmBits(int_info.bits) orelse { + return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); + }; + + switch (wasm_bits) { + 32 => { + const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64; + const lhs_up = try func.intcast(lhs, ty, upcast_ty); + const rhs_up = try func.intcast(rhs, ty, upcast_ty); + var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty); + defer mul_res.free(func); + if (is_signed) { + const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) }; + try func.emitWValue(mul_res); + try func.emitWValue(imm_max); + _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); + try func.addTag(.select); + + var tmp = try func.allocLocal(upcast_ty); + defer tmp.free(func); + try func.addLabel(.local_set, tmp.local.value); + + const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) }; + try func.emitWValue(tmp); + try func.emitWValue(imm_min); + _ = try func.cmp(tmp, imm_min, upcast_ty, .gt); + try func.addTag(.select); + } else { + const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) }; + try func.emitWValue(mul_res); + try func.emitWValue(imm_max); + _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); + try func.addTag(.select); + } + try func.addTag(.i32_wrap_i64); + }, + 64 => { + if (!(int_info.bits == 64 and int_info.signedness == .signed)) { + return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); + } + const overflow_ret = try func.allocStack(Type.i32); + _ = try func.callIntrinsic( + "__mulodi4", + &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, + Type.i64, + &.{ lhs, rhs, overflow_ret }, + ); + const xor = try func.binOp(lhs, rhs, Type.i64, .xor); + const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr); + _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor); + _ = try func.load(overflow_ret, Type.i32, 0); + try func.addTag(.i32_eqz); + try func.addTag(.select); + }, + 128 => { + if (!(int_info.bits == 128 and int_info.signedness == .signed)) { + return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); + } + const overflow_ret = try func.allocStack(Type.i32); + const ret = try func.callIntrinsic( + "__muloti4", + &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, + Type.i128, + &.{ lhs, rhs, overflow_ret }, + ); + try func.lowerToStack(ret); + const xor = try func.binOp(lhs, rhs, Type.i128, .xor); + const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr); + + // xor ~@as(u127, 0) + try func.emitWValue(sign_v); + const lsb = try func.load(sign_v, Type.u64, 0); + _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + try func.store(.stack, .stack, Type.u64, sign_v.offset()); + try func.emitWValue(sign_v); + const msb = try func.load(sign_v, Type.u64, 8); + _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor); + try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8); + + try func.lowerToStack(sign_v); + _ = try func.load(overflow_ret, Type.i32, 0); + try func.addTag(.i32_eqz); + try func.addTag(.select); + }, + else => unreachable, + } + return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); +} + fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { assert(op == .add or op == .sub); const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
