authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-01 16:23:21+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-01 16:23:21+01:00
log3de111d993712f01cf5dd3bf6e2704550c9745e4
treeae6acdc0884a653eff54e7e7081e144f4e71be0a
parentad1b0409962c77ac414be31dc87b8d599be6d3aa
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Fix loading from pointers to support defer

Previously, the `load` instruction would just pass the pointer to the next instruction for types that comply to `isByRef`. However, this meant that a defer would directly write to the reference, rather than a copy. After this commit, we always copy the value.

2 files changed, 34 insertions(+), 28 deletions(-)

src/arch/wasm/CodeGen.zig+32-14
...@@ -1381,7 +1381,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1381,7 +1381,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1381 const ret_ty = self.air.typeOf(un_op).childType();1381 const ret_ty = self.air.typeOf(un_op).childType();
1382 if (!ret_ty.hasCodeGenBits()) return WValue.none;1382 if (!ret_ty.hasCodeGenBits()) return WValue.none;
13831383
1384 if (ret_ty.isSlice() or ret_ty.zigTypeTag() == .ErrorUnion) {1384 if (isByRef(ret_ty)) {
1385 try self.emitWValue(operand);1385 try self.emitWValue(operand);
1386 } else {1386 } else {
1387 const result = try self.load(operand, ret_ty, 0);1387 const result = try self.load(operand, ret_ty, 0);
...@@ -1521,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1521,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
15211521
1522 switch (rhs) {1522 switch (rhs) {
1523 .constant => {1523 .constant => {
1524 if (rhs.constant.val.castTag(.decl_ref)) |_| {
1525 // retrieve values from memory instead
1526 const mem_local = try self.allocLocal(Type.usize);
1527 try self.emitWValue(rhs);
1528 try self.addLabel(.local_set, mem_local.local);
1529 try self.store(lhs, mem_local, ty, 0);
1530 return;
1531 }
1524 // constant will contain both tag and payload,1532 // constant will contain both tag and payload,
1525 // so save those in 2 temporary locals before storing them1533 // so save those in 2 temporary locals before storing them
1526 // in memory1534 // in memory
...@@ -1616,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1616,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1616 // check if we should pass by pointer or value based on ABI size1624 // check if we should pass by pointer or value based on ABI size
1617 // TODO: Implement a way to get ABI values from a given type,1625 // TODO: Implement a way to get ABI values from a given type,
1618 // that is portable across the backend, rather than copying logic.1626 // that is portable across the backend, rather than copying logic.
1619 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)1627 const abi_size = switch (ty.zigTypeTag()) {
1620 @intCast(u8, ty.abiSize(self.target))1628 .Int,
1621 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum or ty.zigTypeTag() == .Bool)1629 .Float,
1622 @intCast(u8, ty.abiSize(self.target))1630 .ErrorSet,
1623 else1631 .Enum,
1624 @as(u8, 4);1632 .Bool,
1633 .ErrorUnion,
1634 => @intCast(u8, ty.abiSize(self.target)),
1635 else => @as(u8, 4),
1636 };
1625 const opcode = buildOpcode(.{1637 const opcode = buildOpcode(.{
1626 .valtype1 = valtype,1638 .valtype1 = valtype,
1627 .width = abi_size * 8, // use bitsize instead of byte size1639 .width = abi_size * 8, // use bitsize instead of byte size
...@@ -1643,7 +1655,9 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1643,7 +1655,9 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1643 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };1655 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
16441656
1645 if (isByRef(ty)) {1657 if (isByRef(ty)) {
1646 return operand;1658 const new_local = try self.allocStack(ty);
1659 try self.store(new_local, operand, ty, 0);
1660 return new_local;
1647 }1661 }
16481662
1649 return switch (operand) {1663 return switch (operand) {
...@@ -1662,12 +1676,16 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1662,12 +1676,16 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1662 .signed;1676 .signed;
1663 // TODO: Implement a way to get ABI values from a given type,1677 // TODO: Implement a way to get ABI values from a given type,
1664 // that is portable across the backend, rather than copying logic.1678 // that is portable across the backend, rather than copying logic.
1665 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)1679 const abi_size = switch (ty.zigTypeTag()) {
1666 @intCast(u8, ty.abiSize(self.target))1680 .Int,
1667 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum or ty.zigTypeTag() == .Bool)1681 .Float,
1668 @intCast(u8, ty.abiSize(self.target))1682 .ErrorSet,
1669 else1683 .Enum,
1670 @as(u8, 4);1684 .Bool,
1685 .ErrorUnion,
1686 => @intCast(u8, ty.abiSize(self.target)),
1687 else => @as(u8, 4),
1688 };
16711689
1672 const opcode = buildOpcode(.{1690 const opcode = buildOpcode(.{
1673 .valtype1 = try self.typeToValtype(ty),1691 .valtype1 = try self.typeToValtype(ty),
test/behavior.zig+2-14
...@@ -39,28 +39,16 @@ test {...@@ -39,28 +39,16 @@ test {
39 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");39 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
40 _ = @import("behavior/slice_sentinel_comptime.zig");40 _ = @import("behavior/slice_sentinel_comptime.zig");
41 _ = @import("behavior/truncate.zig");41 _ = @import("behavior/truncate.zig");
42 _ = @import("behavior/type.zig");
43 _ = @import("behavior/type_info.zig");42 _ = @import("behavior/type_info.zig");
43 _ = @import("behavior/type.zig");
44 _ = @import("behavior/usingnamespace.zig");44 _ = @import("behavior/usingnamespace.zig");
45 _ = @import("behavior/underscore.zig");
4546
46 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend47 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
47 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {48 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
48 _ = @import("behavior/align.zig");49 _ = @import("behavior/align.zig");
49 _ = @import("behavior/array.zig");50 _ = @import("behavior/array.zig");
50 _ = @import("behavior/bool.zig");
51 _ = @import("behavior/bugs/704.zig");
52 _ = @import("behavior/bugs/2692.zig");
53 _ = @import("behavior/bugs/2889.zig");
54 _ = @import("behavior/bugs/3046.zig");
55 _ = @import("behavior/bugs/3586.zig");
56 _ = @import("behavior/bugs/4560.zig");
57 _ = @import("behavior/bugs/4769_a.zig");
58 _ = @import("behavior/bugs/4769_b.zig");
59 _ = @import("behavior/bugs/4954.zig");
60 _ = @import("behavior/byval_arg_var.zig");
61 _ = @import("behavior/call.zig");
62 _ = @import("behavior/cast.zig");51 _ = @import("behavior/cast.zig");
63 _ = @import("behavior/fn_in_struct_in_comptime.zig");
64 _ = @import("behavior/for.zig");52 _ = @import("behavior/for.zig");
65 _ = @import("behavior/generics.zig");53 _ = @import("behavior/generics.zig");
66 _ = @import("behavior/int128.zig");54 _ = @import("behavior/int128.zig");