authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-19 20:28:28+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-19 20:56:04+01:00
log56590218c5054fd5f4076916fd247213a00d5105
treeecdcceca46d3685106500575fed5273f2e4067c6
parent2041176c5e7d2b469c2fe0168e142fe8f322b9bc
signaturelock-open Commit is signed but in an unrecognized format.

wasm: All union/tuple/array tests passing

This implements improvements/fixes to get all the union, tuple, and array behavior tests passing. Previously, we lowered parent pointers for field_ptr and element_ptr incompletely. This has now been improved to recursively lower such pointer. Also a fix was done to `generateSymbol` when checking a container's layout. Previously it was assumed to always be a struct. However, the type can also be a tuple, and therefore panicking. Updating to ask a type's container layout instead allows us to keep a singular branch for both cases.

2 files changed, 119 insertions(+), 52 deletions(-)

src/arch/wasm/CodeGen.zig+118-50
......@@ -1816,18 +1816,103 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18161816 return bin_local;
18171817}
18181818
1819fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue {
1820 switch (ptr_val.tag()) {
1821 .decl_ref_mut => {
1822 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
1823 return self.lowerParentPtrDecl(ptr_val, decl);
1824 },
1825 .decl_ref => {
1826 const decl = ptr_val.castTag(.decl_ref).?.data;
1827 return self.lowerParentPtrDecl(ptr_val, decl);
1828 },
1829 .variable => {
1830 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
1831 return self.lowerParentPtrDecl(ptr_val, decl);
1832 },
1833 .field_ptr => {
1834 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
1835 const parent_ty = field_ptr.container_ty;
1836 const parent_ptr = try self.lowerParentPtr(field_ptr.container_ptr, parent_ty);
1837
1838 const offset = switch (parent_ty.zigTypeTag()) {
1839 .Struct => blk: {
1840 const offset = parent_ty.structFieldOffset(field_ptr.field_index, self.target);
1841 break :blk offset;
1842 },
1843 .Union => blk: {
1844 const layout: Module.Union.Layout = parent_ty.unionGetLayout(self.target);
1845 if (layout.payload_size == 0) break :blk 0;
1846 if (layout.payload_align > layout.tag_align) break :blk 0;
1847
1848 // tag is stored first so calculate offset from where payload starts
1849 const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
1850 break :blk offset;
1851 },
1852 else => unreachable,
1853 };
1854
1855 return switch (parent_ptr) {
1856 .memory => |ptr| WValue{
1857 .memory_offset = .{
1858 .pointer = ptr,
1859 .offset = @intCast(u32, offset),
1860 },
1861 },
1862 .memory_offset => |mem_off| WValue{
1863 .memory_offset = .{
1864 .pointer = mem_off.pointer,
1865 .offset = @intCast(u32, offset) + mem_off.offset,
1866 },
1867 },
1868 else => unreachable,
1869 };
1870 },
1871 .elem_ptr => {
1872 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
1873 const index = elem_ptr.index;
1874 const offset = index * ptr_child_ty.abiSize(self.target);
1875 const array_ptr = try self.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
1876
1877 return WValue{ .memory_offset = .{
1878 .pointer = array_ptr.memory,
1879 .offset = @intCast(u32, offset),
1880 } };
1881 },
1882 else => |tag| return self.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
1883 }
1884}
1885
1886fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl: *Module.Decl) InnerError!WValue {
1887 decl.markAlive();
1888 var ptr_ty_payload: Type.Payload.ElemType = .{
1889 .base = .{ .tag = .single_mut_pointer },
1890 .data = decl.ty,
1891 };
1892 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
1893 return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
1894}
1895
1896fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!WValue {
1897 if (tv.ty.isSlice()) {
1898 return WValue{ .memory = try self.bin_file.lowerUnnamedConst(self.decl, tv) };
1899 } else if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
1900 return WValue{ .imm32 = 0xaaaaaaaa };
1901 }
1902
1903 decl.markAlive();
1904 const target_sym_index = decl.link.wasm.sym_index;
1905 if (decl.ty.zigTypeTag() == .Fn) {
1906 try self.bin_file.addTableFunction(target_sym_index);
1907 return WValue{ .function_index = target_sym_index };
1908 } else return WValue{ .memory = target_sym_index };
1909}
1910
18191911fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
18201912 if (val.isUndefDeep()) return self.emitUndefined(ty);
18211913 if (val.castTag(.decl_ref)) |decl_ref| {
18221914 const decl = decl_ref.data;
1823 decl.markAlive();
1824 const target_sym_index = decl.link.wasm.sym_index;
1825 if (ty.isSlice()) {
1826 return WValue{ .memory = try self.bin_file.lowerUnnamedConst(self.decl, .{ .ty = ty, .val = val }) };
1827 } else if (decl.ty.zigTypeTag() == .Fn) {
1828 try self.bin_file.addTableFunction(target_sym_index);
1829 return WValue{ .function_index = target_sym_index };
1830 } else return WValue{ .memory = target_sym_index };
1915 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl);
18311916 }
18321917
18331918 switch (ty.zigTypeTag()) {
......@@ -1854,37 +1939,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
18541939 else => unreachable,
18551940 },
18561941 .Pointer => switch (val.tag()) {
1857 .elem_ptr => {
1858 const elem_ptr = val.castTag(.elem_ptr).?.data;
1859 const index = elem_ptr.index;
1860 const offset = index * ty.childType().abiSize(self.target);
1861 const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty);
1862
1863 return WValue{ .memory_offset = .{
1864 .pointer = array_ptr.memory,
1865 .offset = @intCast(u32, offset),
1866 } };
1867 },
1868 .field_ptr => {
1869 const field_ptr = val.castTag(.field_ptr).?.data;
1870 const container = field_ptr.container_ptr;
1871 const parent_ptr = try self.lowerConstant(container, ty);
1872
1873 const offset = switch (container.tag()) {
1874 .decl_ref => blk: {
1875 const decl_ref = container.castTag(.decl_ref).?.data;
1876 if (decl_ref.ty.castTag(.@"struct")) |_| {
1877 const offset = decl_ref.ty.structFieldOffset(field_ptr.field_index, self.target);
1878 break :blk offset;
1879 }
1880 return self.fail("Wasm TODO: field_ptr decl_ref for type '{}'", .{decl_ref.ty});
1881 },
1882 else => |tag| return self.fail("Wasm TODO: Implement field_ptr for value tag: '{s}'", .{tag}),
1883 };
1884 return WValue{ .memory_offset = .{
1885 .pointer = parent_ptr.memory,
1886 .offset = @intCast(u32, offset),
1887 } };
1942 .field_ptr, .elem_ptr => {
1943 return self.lowerParentPtr(val, ty.childType());
18881944 },
18891945 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
18901946 .zero, .null_value => return WValue{ .imm32 = 0 },
......@@ -1997,6 +2053,11 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
19972053 return self.valueAsI32(tag_val, enum_full.tag_ty);
19982054 } else return @bitCast(i32, field_index.data);
19992055 },
2056 .enum_numbered => {
2057 const index = field_index.data;
2058 const enum_data = ty.castTag(.enum_numbered).?.data;
2059 return self.valueAsI32(enum_data.values.keys()[index], enum_data.tag_ty);
2060 },
20002061 else => unreachable,
20012062 }
20022063 } else {
......@@ -2122,8 +2183,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
21222183 return self.cmpBigInt(lhs, rhs, operand_ty, op);
21232184 }
21242185
2125 try self.emitWValue(lhs);
2126 try self.emitWValue(rhs);
2186 // ensure that when we compare pointers, we emit
2187 // the true pointer of a stack value, rather than the stack pointer.
2188 switch (lhs) {
2189 .stack_offset => try self.emitWValue(try self.buildPointerOffset(lhs, 0, .new)),
2190 else => try self.emitWValue(lhs),
2191 }
2192 switch (rhs) {
2193 .stack_offset => try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)),
2194 else => try self.emitWValue(rhs),
2195 }
21272196
21282197 const signedness: std.builtin.Signedness = blk: {
21292198 // by default we tell the operand type is unsigned (i.e. bools and enum values)
......@@ -3159,16 +3228,16 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31593228fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31603229 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
31613230
3162 const vector_ty = self.air.typeOfIndex(inst);
3163 const len = vector_ty.vectorLen();
31643231 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3232 const result_ty = self.air.typeOfIndex(inst);
3233 const len = @intCast(usize, result_ty.arrayLen());
31653234 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
31663235
3167 switch (vector_ty.zigTypeTag()) {
3236 switch (result_ty.zigTypeTag()) {
31683237 .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
31693238 .Array => {
3170 const result = try self.allocStack(vector_ty);
3171 const elem_ty = vector_ty.childType();
3239 const result = try self.allocStack(result_ty);
3240 const elem_ty = result_ty.childType();
31723241 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
31733242
31743243 // When the element type is by reference, we must copy the entire
......@@ -3197,13 +3266,12 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31973266 return result;
31983267 },
31993268 .Struct => {
3200 const tuple = vector_ty.castTag(.tuple).?.data;
3201 const result = try self.allocStack(vector_ty);
3269 const result = try self.allocStack(result_ty);
32023270 const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset
32033271 for (elements) |elem, elem_index| {
3204 if (tuple.values[elem_index].tag() != .unreachable_value) continue;
3272 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
32053273
3206 const elem_ty = tuple.types[elem_index];
3274 const elem_ty = result_ty.structFieldType(elem_index);
32073275 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
32083276 const value = try self.resolveInst(elem);
32093277 try self.store(offset, value, elem_ty, 0);
src/codegen.zig+1-2
......@@ -530,8 +530,7 @@ pub fn generateSymbol(
530530 return Result{ .appended = {} };
531531 },
532532 .Struct => {
533 const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
534 if (struct_obj.layout == .Packed) {
533 if (typed_value.ty.containerLayout() == .Packed) {
535534 return Result{
536535 .fail = try ErrorMsg.create(
537536 bin_file.allocator,