aboutsummaryrefslogtreecommitdiff
path: root/src/arch/wasm/CodeGen.zig
diff options
context:
space:
mode:
authorPavel Verigo <paul.verigo@gmail.com>2024-07-23 17:06:18 +0200
committerPavel Verigo <paul.verigo@gmail.com>2024-07-23 17:06:18 +0200
commitd71312d1048d93d5e806a600066426fd2db9b1fb (patch)
tree7cdbc18071685a8c14b04743a343e8eccbd3d9de /src/arch/wasm/CodeGen.zig
parent0c6aa44bc3c0670f3533c503ad9921c2439d8e35 (diff)
downloadzig-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.zig102
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;