authorgravatar for 94326797+riverbl@users.noreply.github.comriverbl <94326797+riverbl@users.noreply.github.com> 2023-08-20 11:39:07+01:00
committergravatar for 94326797+riverbl@users.noreply.github.comriverbl <94326797+riverbl@users.noreply.github.com> 2023-08-23 16:52:30+01:00
log383e6ffc7b9e22613f4c4b15ebdcd2bf487573d6
tree02012e9546a082e0f347d6039c5c5b029fc1bc72
parent6780a6bbfa7bbd44036de3b1338011d1e5bb43f3

Implement `@mod` and fix bugs with `divFloor` for wasm

Implement lowering code for `@mod` on integers in the stage2 wasm backend Fix invalid wasm being produced for `@divFloor` on signed integers by the stage2 wasm backend

1 files changed, 124 insertions(+), 49 deletions(-)

src/arch/wasm/CodeGen.zig+124-49
......@@ -1852,6 +1852,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18521852 .bool_and => func.airBinOp(inst, .@"and"),
18531853 .bool_or => func.airBinOp(inst, .@"or"),
18541854 .rem => func.airBinOp(inst, .rem),
1855 .mod => func.airMod(inst),
18551856 .shl => func.airWrapBinOp(inst, .shl),
18561857 .shl_exact => func.airBinOp(inst, .shl),
18571858 .shl_sat => func.airShlSat(inst),
......@@ -2012,7 +2013,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20122013 .frame_addr => func.airFrameAddress(inst),
20132014
20142015 .mul_sat,
2015 .mod,
20162016 .assembly,
20172017 .bit_reverse,
20182018 .is_err_ptr,
......@@ -4151,7 +4151,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
41514151 // when we upcast from a smaller integer to larger
41524152 // integers, we must get its absolute value similar to
41534153 // i64_extend_i32_s instruction.
4154 return func.signAbsValue(operand, given);
4154 return func.signExtendInt(operand, given);
41554155 }
41564156 return func.wrapOperand(operand, wanted);
41574157 }
......@@ -5649,13 +5649,13 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
56495649 // for signed integers, we first apply signed shifts by the difference in bits
56505650 // to get the signed value, as we store it internally as 2's complement.
56515651 var lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
5652 break :blk try (try func.signAbsValue(lhs_op, lhs_ty)).toLocal(func, lhs_ty);
5652 break :blk try (try func.signExtendInt(lhs_op, lhs_ty)).toLocal(func, lhs_ty);
56535653 } else lhs_op;
56545654 var rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
5655 break :blk try (try func.signAbsValue(rhs_op, lhs_ty)).toLocal(func, lhs_ty);
5655 break :blk try (try func.signExtendInt(rhs_op, lhs_ty)).toLocal(func, lhs_ty);
56565656 } else rhs_op;
56575657
5658 // in this case, we performed a signAbsValue which created a temporary local
5658 // in this case, we performed a signExtendInt which created a temporary local
56595659 // so let's free this so it can be re-used instead.
56605660 // In the other case we do not want to free it, because that would free the
56615661 // resolved instructions which may be referenced by other instructions.
......@@ -5677,7 +5677,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
56775677 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
56785678 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
56795679 }
5680 const abs = try func.signAbsValue(bin_op, lhs_ty);
5680 const abs = try func.signExtendInt(bin_op, lhs_ty);
56815681 break :blk try func.cmp(abs, bin_op, lhs_ty, .neq);
56825682 } else if (wasm_bits == int_info.bits)
56835683 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
......@@ -5797,7 +5797,7 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57975797 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {
57985798 // emit lhs to stack to we can keep 'wrapped' on the stack also
57995799 try func.emitWValue(lhs);
5800 const abs = try func.signAbsValue(shl, lhs_ty);
5800 const abs = try func.signExtendInt(shl, lhs_ty);
58015801 const wrapped = try func.wrapBinOp(abs, rhs_final, lhs_ty, .shr);
58025802 break :blk try func.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq);
58035803 } else blk: {
......@@ -5869,10 +5869,10 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58695869 break :blk down_cast;
58705870 }
58715871 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
5872 const lhs_abs = try func.signAbsValue(lhs, lhs_ty);
5873 const rhs_abs = try func.signAbsValue(rhs, lhs_ty);
5872 const lhs_abs = try func.signExtendInt(lhs, lhs_ty);
5873 const rhs_abs = try func.signExtendInt(rhs, lhs_ty);
58745874 const bin_op = try (try func.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(func, lhs_ty);
5875 const mul_abs = try func.signAbsValue(bin_op, lhs_ty);
5875 const mul_abs = try func.signExtendInt(bin_op, lhs_ty);
58765876 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
58775877 try func.addLabel(.local_set, overflow_bit.local.value);
58785878 break :blk try func.wrapOperand(bin_op, lhs_ty);
......@@ -6405,47 +6405,77 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64056405 const rhs = try func.resolveInst(bin_op.rhs);
64066406
64076407 if (ty.isUnsignedInt(mod)) {
6408 const result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
6409 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6408 _ = try func.binOp(lhs, rhs, ty, .div);
64106409 } else if (ty.isSignedInt(mod)) {
64116410 const int_bits = ty.intInfo(mod).bits;
64126411 const wasm_bits = toWasmBits(int_bits) orelse {
6413 return func.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits});
6412 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
64146413 };
6415 const lhs_res = if (wasm_bits != int_bits) blk: {
6416 break :blk try (try func.signAbsValue(lhs, ty)).toLocal(func, ty);
6417 } else lhs;
6418 const rhs_res = if (wasm_bits != int_bits) blk: {
6419 break :blk try (try func.signAbsValue(rhs, ty)).toLocal(func, ty);
6420 } else rhs;
6414
6415 if (wasm_bits > 64) {
6416 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6417 }
6418
6419 const lhs_wasm = if (wasm_bits != int_bits)
6420 try (try func.signExtendInt(lhs, ty)).toLocal(func, ty)
6421 else
6422 lhs;
6423
6424 const rhs_wasm = if (wasm_bits != int_bits)
6425 try (try func.signExtendInt(rhs, ty)).toLocal(func, ty)
6426 else
6427 rhs;
64216428
64226429 const zero = switch (wasm_bits) {
64236430 32 => WValue{ .imm32 = 0 },
64246431 64 => WValue{ .imm64 = 0 },
6425 else => unreachable,
6432 else => @panic("Unexpected wasm integer width"),
64266433 };
64276434
6428 const div_result = try func.allocLocal(ty);
6429 // leave on stack
6430 _ = try func.binOp(lhs_res, rhs_res, ty, .div);
6431 try func.addLabel(.local_tee, div_result.local.value);
6432 _ = try func.cmp(lhs_res, zero, ty, .lt);
6433 _ = try func.cmp(rhs_res, zero, ty, .lt);
6435 // tee leaves the value on the stack and stores it in a local.
6436 const quotient = try func.allocLocal(ty);
6437 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .div);
6438 try func.addLabel(.local_tee, quotient.local.value);
6439
6440 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow
6441 // the 64 bit value we want to use as the condition to 32 bits.
6442 // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and
6443 // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits.
6444 if (wasm_bits == 64) {
6445 try func.emitWValue(quotient);
6446 }
6447
6448 // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise.
6449 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .xor);
6450 _ = try func.cmp(.stack, zero, ty, .lt);
6451
64346452 switch (wasm_bits) {
64356453 32 => {
6436 try func.addTag(.i32_xor);
64376454 try func.addTag(.i32_sub);
6455 try func.emitWValue(quotient);
64386456 },
64396457 64 => {
6440 try func.addTag(.i64_xor);
6458 try func.addTag(.i64_extend_i32_u);
64416459 try func.addTag(.i64_sub);
64426460 },
6443 else => unreachable,
6461 else => @panic("Unexpected wasm integer width"),
6462 }
6463
6464 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem);
6465
6466 if (wasm_bits == 64) {
6467 try func.addTag(.i64_eqz);
64446468 }
6445 try func.emitWValue(div_result);
6446 // leave value on the stack
6447 _ = try func.binOp(lhs_res, rhs_res, ty, .rem);
6469
64486470 try func.addTag(.select);
6471
6472 // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and
6473 // expect all but the lowest N bits to be 0.
6474 // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits
6475 // when performing comparisons?
6476 if (int_bits != wasm_bits) {
6477 _ = try func.wrapOperand(.{ .stack = {} }, ty);
6478 }
64496479 } else {
64506480 const float_bits = ty.floatBits(func.target);
64516481 if (float_bits > 64) {
......@@ -6453,15 +6483,11 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64536483 }
64546484 const is_f16 = float_bits == 16;
64556485
6456 const lhs_operand = if (is_f16) blk: {
6457 break :blk try func.fpext(lhs, Type.f16, Type.f32);
6458 } else lhs;
6459 const rhs_operand = if (is_f16) blk: {
6460 break :blk try func.fpext(rhs, Type.f16, Type.f32);
6461 } else rhs;
6486 const lhs_wasm = if (is_f16) try func.fpext(lhs, Type.f16, Type.f32) else lhs;
6487 const rhs_wasm = if (is_f16) try func.fpext(rhs, Type.f16, Type.f32) else rhs;
64626488
6463 try func.emitWValue(lhs_operand);
6464 try func.emitWValue(rhs_operand);
6489 try func.emitWValue(lhs_wasm);
6490 try func.emitWValue(rhs_wasm);
64656491
64666492 switch (float_bits) {
64676493 16, 32 => {
......@@ -6498,8 +6524,8 @@ fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WVal
64986524
64996525 if (wasm_bits != int_bits) {
65006526 // Leave both values on the stack
6501 _ = try func.signAbsValue(lhs, ty);
6502 _ = try func.signAbsValue(rhs, ty);
6527 _ = try func.signExtendInt(lhs, ty);
6528 _ = try func.signExtendInt(rhs, ty);
65036529 } else {
65046530 try func.emitWValue(lhs);
65056531 try func.emitWValue(rhs);
......@@ -6511,19 +6537,68 @@ fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WVal
65116537 return result;
65126538}
65136539
6514/// Retrieves the absolute value of a signed integer
6515/// NOTE: Leaves the result value on the stack.
6516fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
6540/// Remainder after floor division, defined by:
6541/// @divFloor(a, b) * b + @mod(a, b) = a
6542fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6543 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
6544
6545 const mod = func.bin_file.base.options.module.?;
6546 const ty = func.typeOfIndex(inst);
6547 const lhs = try func.resolveInst(bin_op.lhs);
6548 const rhs = try func.resolveInst(bin_op.rhs);
6549
6550 if (ty.isUnsignedInt(mod)) {
6551 _ = try func.binOp(lhs, rhs, ty, .rem);
6552 } else if (ty.isSignedInt(mod)) {
6553 // The wasm rem instruction gives the remainder after truncating division (rounding towards
6554 // 0), equivalent to @rem.
6555 // We make use of the fact that:
6556 // @mod(a, b) = @rem(@rem(a, b) + b, b)
6557 const int_bits = ty.intInfo(mod).bits;
6558 const wasm_bits = toWasmBits(int_bits) orelse {
6559 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6560 };
6561
6562 if (wasm_bits > 64) {
6563 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6564 }
6565
6566 const lhs_wasm = if (wasm_bits != int_bits)
6567 try (try func.signExtendInt(lhs, ty)).toLocal(func, ty)
6568 else
6569 lhs;
6570
6571 const rhs_wasm = if (wasm_bits != int_bits)
6572 try (try func.signExtendInt(rhs, ty)).toLocal(func, ty)
6573 else
6574 rhs;
6575
6576 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem);
6577 _ = try func.binOp(.stack, rhs_wasm, ty, .add);
6578 _ = try func.binOp(.stack, rhs_wasm, ty, .rem);
6579 } else {
6580 return func.fail("TODO: implement `@mod` on floating point types for {}", .{func.target.cpu.arch});
6581 }
6582
6583 const result = try func.allocLocal(ty);
6584 try func.addLabel(.local_set, result.local.value);
6585 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6586}
6587
6588/// Sign extends an N bit signed integer and pushes the result to the stack.
6589/// The result will be sign extended to 32 bits if N <= 32 or 64 bits if N <= 64.
6590/// Support for integers wider than 64 bits has not yet been implemented.
6591fn signExtendInt(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
65176592 const mod = func.bin_file.base.options.module.?;
65186593 const int_bits = ty.intInfo(mod).bits;
65196594 const wasm_bits = toWasmBits(int_bits) orelse {
6520 return func.fail("TODO: signAbsValue for signed integers larger than '{d}' bits", .{int_bits});
6595 return func.fail("TODO: signExtendInt for signed integers larger than '{d}' bits", .{int_bits});
65216596 };
65226597
65236598 const shift_val = switch (wasm_bits) {
65246599 32 => WValue{ .imm32 = wasm_bits - int_bits },
65256600 64 => WValue{ .imm64 = wasm_bits - int_bits },
6526 else => return func.fail("TODO: signAbsValue for i128", .{}),
6601 else => return func.fail("TODO: signExtendInt for i128", .{}),
65276602 };
65286603
65296604 try func.emitWValue(operand);
......@@ -6604,10 +6679,10 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
66046679 const is_wasm_bits = wasm_bits == int_info.bits;
66056680
66066681 var lhs = if (!is_wasm_bits) lhs: {
6607 break :lhs try (try func.signAbsValue(lhs_operand, ty)).toLocal(func, ty);
6682 break :lhs try (try func.signExtendInt(lhs_operand, ty)).toLocal(func, ty);
66086683 } else lhs_operand;
66096684 var rhs = if (!is_wasm_bits) rhs: {
6610 break :rhs try (try func.signAbsValue(rhs_operand, ty)).toLocal(func, ty);
6685 break :rhs try (try func.signExtendInt(rhs_operand, ty)).toLocal(func, ty);
66116686 } else rhs_operand;
66126687
66136688 const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1));