authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-16 17:14:15+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
logfd081c74f10de71eaf00b0ccd7c4e4a0c0c5c3ad
tree1f53091dcbcb960a615a7361af1787caf36fc268
parent10fe24c043c95180f658c3eb4d7fbbfd0388c14e

wasm: Support `not` instruction for 128 bit integers

This also fixes the instruction for all other integer bitsizes, as it was previously assuming to always be a bool. 128 bit substraction was also fixed as it contained a bug where it swapped lhs with rhs.

1 files changed, 45 insertions(+), 16 deletions(-)

src/arch/wasm/CodeGen.zig+45-16
......@@ -2014,13 +2014,13 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
20142014 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
20152015 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
20162016
2017 const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op);
2017 const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
20182018 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
20192019
20202020 const lt = if (op == .add) blk: {
20212021 break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
20222022 } else if (op == .sub) blk: {
2023 break :blk try self.cmp(rhs_high_bit, lhs_high_bit, Type.u64, .lt);
2023 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
20242024 } else unreachable;
20252025 const tmp = try self.intcast(lt, Type.u32, Type.u64);
20262026 const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
......@@ -2078,6 +2078,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
20782078 const wasm_bits = toWasmBits(bitsize) orelse {
20792079 return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
20802080 };
2081
20812082 if (wasm_bits == bitsize) return operand;
20822083
20832084 if (wasm_bits == 128) {
......@@ -2424,15 +2425,16 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
24242425
24252426fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24262427 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2427 const block_ty = genBlockType(self.air.getRefType(ty_pl.ty), self.target);
2428 const block_ty = self.air.getRefType(ty_pl.ty);
2429 const wasm_block_ty = genBlockType(block_ty, self.target);
24282430 const extra = self.air.extraData(Air.Block, ty_pl.payload);
24292431 const body = self.air.extra[extra.end..][0..extra.data.body_len];
24302432
2431 // if block_ty is non-empty, we create a register to store the temporary value
2432 const block_result: WValue = if (block_ty != wasm.block_empty)
2433 try self.allocLocal(self.air.getRefType(ty_pl.ty))
2434 else
2435 WValue.none;
2433 // if wasm_block_ty is non-empty, we create a register to store the temporary value
2434 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {
2435 const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty;
2436 break :blk try self.allocLocal(ty);
2437 } else WValue.none;
24362438
24372439 try self.startBlock(.block, wasm.block_empty);
24382440 // Here we set the current block idx, so breaks know the depth to jump
......@@ -2600,16 +2602,43 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26002602 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26012603
26022604 const operand = try self.resolveInst(ty_op.operand);
2603 try self.emitWValue(operand);
2605 const operand_ty = self.air.typeOf(ty_op.operand);
26042606
2605 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
2606 // to create the same logic
2607 try self.addTag(.i32_eqz);
2607 if (operand_ty.zigTypeTag() == .Bool) {
2608 try self.emitWValue(operand);
2609 try self.addTag(.i32_eqz);
2610 const not_tmp = try self.allocLocal(operand_ty);
2611 try self.addLabel(.local_set, not_tmp.local);
2612 return not_tmp;
2613 } else {
2614 const operand_bits = operand_ty.intInfo(self.target).bits;
2615 const wasm_bits = toWasmBits(operand_bits) orelse {
2616 return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
2617 };
26082618
2609 // save the result in the local
2610 const not_tmp = try self.allocLocal(Type.initTag(.i32));
2611 try self.addLabel(.local_set, not_tmp.local);
2612 return not_tmp;
2619 switch (wasm_bits) {
2620 32 => {
2621 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
2622 return self.wrapOperand(bin_op, operand_ty);
2623 },
2624 64 => {
2625 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
2626 return self.wrapOperand(bin_op, operand_ty);
2627 },
2628 128 => {
2629 const result_ptr = try self.allocStack(operand_ty);
2630 const msb = try self.load(operand, Type.u64, 0);
2631 const lsb = try self.load(operand, Type.u64, 8);
2632
2633 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2634 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2635 try self.store(result_ptr, msb_xor, Type.u64, 0);
2636 try self.store(result_ptr, lsb_xor, Type.u64, 8);
2637 return result_ptr;
2638 },
2639 else => unreachable,
2640 }
2641 }
26132642}
26142643
26152644fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {