| ... | @@ -1816,18 +1816,103 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1816,18 +1816,103 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1816 | return bin_local; | 1816 | return bin_local; |
| 1817 | } | 1817 | } |
| 1818 | | 1818 | |
| | 1819 | fn 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 | |
| | 1886 | fn 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 | |
| | 1896 | fn 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 | |
| 1819 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | 1911 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 1820 | if (val.isUndefDeep()) return self.emitUndefined(ty); | 1912 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 1821 | if (val.castTag(.decl_ref)) |decl_ref| { | 1913 | if (val.castTag(.decl_ref)) |decl_ref| { |
| 1822 | const decl = decl_ref.data; | 1914 | const decl = decl_ref.data; |
| 1823 | decl.markAlive(); | 1915 | return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); |
| 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 }; | | |
| 1831 | } | 1916 | } |
| 1832 | | 1917 | |
| 1833 | switch (ty.zigTypeTag()) { | 1918 | switch (ty.zigTypeTag()) { |
| ... | @@ -1854,37 +1939,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | ... | @@ -1854,37 +1939,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 1854 | else => unreachable, | 1939 | else => unreachable, |
| 1855 | }, | 1940 | }, |
| 1856 | .Pointer => switch (val.tag()) { | 1941 | .Pointer => switch (val.tag()) { |
| 1857 | .elem_ptr => { | 1942 | .field_ptr, .elem_ptr => { |
| 1858 | const elem_ptr = val.castTag(.elem_ptr).?.data; | 1943 | return self.lowerParentPtr(val, ty.childType()); |
| 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 | } }; | | |
| 1888 | }, | 1944 | }, |
| 1889 | .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, | 1945 | .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, |
| 1890 | .zero, .null_value => return WValue{ .imm32 = 0 }, | 1946 | .zero, .null_value => return WValue{ .imm32 = 0 }, |
| ... | @@ -1997,6 +2053,11 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 { | ... | @@ -1997,6 +2053,11 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 { |
| 1997 | return self.valueAsI32(tag_val, enum_full.tag_ty); | 2053 | return self.valueAsI32(tag_val, enum_full.tag_ty); |
| 1998 | } else return @bitCast(i32, field_index.data); | 2054 | } else return @bitCast(i32, field_index.data); |
| 1999 | }, | 2055 | }, |
| | 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 | }, |
| 2000 | else => unreachable, | 2061 | else => unreachable, |
| 2001 | } | 2062 | } |
| 2002 | } else { | 2063 | } else { |
| ... | @@ -2122,8 +2183,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -2122,8 +2183,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2122 | return self.cmpBigInt(lhs, rhs, operand_ty, op); | 2183 | return self.cmpBigInt(lhs, rhs, operand_ty, op); |
| 2123 | } | 2184 | } |
| 2124 | | 2185 | |
| 2125 | try self.emitWValue(lhs); | 2186 | // ensure that when we compare pointers, we emit |
| 2126 | try self.emitWValue(rhs); | 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 | } |
| 2127 | | 2196 | |
| 2128 | const signedness: std.builtin.Signedness = blk: { | 2197 | const signedness: std.builtin.Signedness = blk: { |
| 2129 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2198 | // 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 { | ... | @@ -3159,16 +3228,16 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3159 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3228 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3160 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | 3229 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3161 | | 3230 | |
| 3162 | const vector_ty = self.air.typeOfIndex(inst); | | |
| 3163 | const len = vector_ty.vectorLen(); | | |
| 3164 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3231 | 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()); |
| 3165 | const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); | 3234 | const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); |
| 3166 | | 3235 | |
| 3167 | switch (vector_ty.zigTypeTag()) { | 3236 | switch (result_ty.zigTypeTag()) { |
| 3168 | .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), | 3237 | .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), |
| 3169 | .Array => { | 3238 | .Array => { |
| 3170 | const result = try self.allocStack(vector_ty); | 3239 | const result = try self.allocStack(result_ty); |
| 3171 | const elem_ty = vector_ty.childType(); | 3240 | const elem_ty = result_ty.childType(); |
| 3172 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); | 3241 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); |
| 3173 | | 3242 | |
| 3174 | // When the element type is by reference, we must copy the entire | 3243 | // 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 { | ... | @@ -3197,13 +3266,12 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3197 | return result; | 3266 | return result; |
| 3198 | }, | 3267 | }, |
| 3199 | .Struct => { | 3268 | .Struct => { |
| 3200 | const tuple = vector_ty.castTag(.tuple).?.data; | 3269 | const result = try self.allocStack(result_ty); |
| 3201 | const result = try self.allocStack(vector_ty); | | |
| 3202 | const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset | 3270 | const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset |
| 3203 | for (elements) |elem, elem_index| { | 3271 | for (elements) |elem, elem_index| { |
| 3204 | if (tuple.values[elem_index].tag() != .unreachable_value) continue; | 3272 | if (result_ty.structFieldValueComptime(elem_index) != null) continue; |
| 3205 | | 3273 | |
| 3206 | const elem_ty = tuple.types[elem_index]; | 3274 | const elem_ty = result_ty.structFieldType(elem_index); |
| 3207 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); | 3275 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); |
| 3208 | const value = try self.resolveInst(elem); | 3276 | const value = try self.resolveInst(elem); |
| 3209 | try self.store(offset, value, elem_ty, 0); | 3277 | try self.store(offset, value, elem_ty, 0); |