| author | |
| committer | |
| log | a10ddba9218abf452ccc2a244222b4d69b606865 |
| tree | a2d1316cf118d344e012432b015ac9d76d217d2f |
| parent | abfa2158820c963cddc0c861168b16021d846825 |
| parent | 44b322ce6410a0fab7c3cbdfc35bb1530a31a304 |
| signature |
wasm/linker: symbol resolution improvements6 files changed, 85 insertions(+), 90 deletions(-)
src/arch/wasm/CodeGen.zig+23-46| ... | ... | @@ -2938,49 +2938,31 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 2938 | 2938 | return WValue{ .stack = {} }; |
| 2939 | 2939 | } |
| 2940 | 2940 | |
| 2941 | fn lowerParentPtr(func: *CodeGen, ptr_val: Value) InnerError!WValue { | |
| 2941 | fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue { | |
| 2942 | 2942 | const mod = func.bin_file.base.options.module.?; |
| 2943 | 2943 | const ptr = mod.intern_pool.indexToKey(ptr_val.ip_index).ptr; |
| 2944 | 2944 | switch (ptr.addr) { |
| 2945 | 2945 | .decl => |decl_index| { |
| 2946 | return func.lowerParentPtrDecl(ptr_val, decl_index, 0); | |
| 2946 | return func.lowerParentPtrDecl(ptr_val, decl_index, offset); | |
| 2947 | 2947 | }, |
| 2948 | 2948 | .mut_decl => |mut_decl| { |
| 2949 | 2949 | const decl_index = mut_decl.decl; |
| 2950 | return func.lowerParentPtrDecl(ptr_val, decl_index, 0); | |
| 2951 | }, | |
| 2952 | .int, .eu_payload => |tag| return func.fail("TODO: Implement lowerParentPtr for {}", .{tag}), | |
| 2953 | .opt_payload => |base_ptr| { | |
| 2954 | return func.lowerParentPtr(base_ptr.toValue()); | |
| 2950 | return func.lowerParentPtrDecl(ptr_val, decl_index, offset); | |
| 2955 | 2951 | }, |
| 2952 | .eu_payload => |tag| return func.fail("TODO: Implement lowerParentPtr for {}", .{tag}), | |
| 2953 | .int => |base| return func.lowerConstant(base.toValue(), Type.usize), | |
| 2954 | .opt_payload => |base_ptr| return func.lowerParentPtr(base_ptr.toValue(), offset), | |
| 2956 | 2955 | .comptime_field => unreachable, |
| 2957 | 2956 | .elem => |elem| { |
| 2958 | 2957 | const index = elem.index; |
| 2959 | 2958 | const elem_type = mod.intern_pool.typeOf(elem.base).toType().elemType2(mod); |
| 2960 | const offset = index * elem_type.abiSize(mod); | |
| 2961 | const array_ptr = try func.lowerParentPtr(elem.base.toValue()); | |
| 2962 | ||
| 2963 | return switch (array_ptr) { | |
| 2964 | .memory => |ptr_| WValue{ | |
| 2965 | .memory_offset = .{ | |
| 2966 | .pointer = ptr_, | |
| 2967 | .offset = @intCast(u32, offset), | |
| 2968 | }, | |
| 2969 | }, | |
| 2970 | .memory_offset => |mem_off| WValue{ | |
| 2971 | .memory_offset = .{ | |
| 2972 | .pointer = mem_off.pointer, | |
| 2973 | .offset = @intCast(u32, offset) + mem_off.offset, | |
| 2974 | }, | |
| 2975 | }, | |
| 2976 | else => unreachable, | |
| 2977 | }; | |
| 2959 | const elem_offset = index * elem_type.abiSize(mod); | |
| 2960 | return func.lowerParentPtr(elem.base.toValue(), @intCast(u32, elem_offset + offset)); | |
| 2978 | 2961 | }, |
| 2979 | 2962 | .field => |field| { |
| 2980 | 2963 | const parent_ty = mod.intern_pool.typeOf(field.base).toType().childType(mod); |
| 2981 | const parent_ptr = try func.lowerParentPtr(field.base.toValue()); | |
| 2982 | 2964 | |
| 2983 | const offset = switch (parent_ty.zigTypeTag(mod)) { | |
| 2965 | const field_offset = switch (parent_ty.zigTypeTag(mod)) { | |
| 2984 | 2966 | .Struct => switch (parent_ty.containerLayout(mod)) { |
| 2985 | 2967 | .Packed => parent_ty.packedStructFieldByteOffset(@intCast(usize, field.index), mod), |
| 2986 | 2968 | else => parent_ty.structFieldOffset(@intCast(usize, field.index), mod), |
| ... | ... | @@ -2993,8 +2975,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value) InnerError!WValue { |
| 2993 | 2975 | if (layout.payload_align > layout.tag_align) break :blk 0; |
| 2994 | 2976 | |
| 2995 | 2977 | // tag is stored first so calculate offset from where payload starts |
| 2996 | const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align)); | |
| 2997 | break :blk offset; | |
| 2978 | break :blk @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align)); | |
| 2998 | 2979 | }, |
| 2999 | 2980 | }, |
| 3000 | 2981 | .Pointer => switch (parent_ty.ptrSize(mod)) { |
| ... | ... | @@ -3007,22 +2988,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value) InnerError!WValue { |
| 3007 | 2988 | }, |
| 3008 | 2989 | else => unreachable, |
| 3009 | 2990 | }; |
| 3010 | ||
| 3011 | return switch (parent_ptr) { | |
| 3012 | .memory => |ptr_| WValue{ | |
| 3013 | .memory_offset = .{ | |
| 3014 | .pointer = ptr_, | |
| 3015 | .offset = @intCast(u32, offset), | |
| 3016 | }, | |
| 3017 | }, | |
| 3018 | .memory_offset => |mem_off| WValue{ | |
| 3019 | .memory_offset = .{ | |
| 3020 | .pointer = mem_off.pointer, | |
| 3021 | .offset = @intCast(u32, offset) + mem_off.offset, | |
| 3022 | }, | |
| 3023 | }, | |
| 3024 | else => unreachable, | |
| 3025 | }; | |
| 2991 | return func.lowerParentPtr(field.base.toValue(), @intCast(u32, offset + field_offset)); | |
| 3026 | 2992 | }, |
| 3027 | 2993 | } |
| 3028 | 2994 | } |
| ... | ... | @@ -3042,6 +3008,17 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind |
| 3042 | 3008 | } |
| 3043 | 3009 | |
| 3044 | 3010 | const decl = mod.declPtr(decl_index); |
| 3011 | // check if decl is an alias to a function, in which case we | |
| 3012 | // want to lower the actual decl, rather than the alias itself. | |
| 3013 | if (decl.val.getFunction(mod)) |func_val| { | |
| 3014 | if (func_val.owner_decl != decl_index) { | |
| 3015 | return func.lowerDeclRefValue(tv, func_val.owner_decl, offset); | |
| 3016 | } | |
| 3017 | } else if (decl.val.getExternFunc(mod)) |func_val| { | |
| 3018 | if (func_val.decl != decl_index) { | |
| 3019 | return func.lowerDeclRefValue(tv, func_val.decl, offset); | |
| 3020 | } | |
| 3021 | } | |
| 3045 | 3022 | if (decl.ty.zigTypeTag(mod) != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 3046 | 3023 | return WValue{ .imm32 = 0xaaaaaaaa }; |
| 3047 | 3024 | } |
| ... | ... | @@ -3230,7 +3207,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 3230 | 3207 | .decl => |decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl, 0), |
| 3231 | 3208 | .mut_decl => |mut_decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, mut_decl.decl, 0), |
| 3232 | 3209 | .int => |int| return func.lowerConstant(int.toValue(), mod.intern_pool.typeOf(int).toType()), |
| 3233 | .opt_payload, .elem, .field => return func.lowerParentPtr(val), | |
| 3210 | .opt_payload, .elem, .field => return func.lowerParentPtr(val, 0), | |
| 3234 | 3211 | else => return func.fail("Wasm TODO: lowerConstant for other const addr tag {}", .{ptr.addr}), |
| 3235 | 3212 | }, |
| 3236 | 3213 | .opt => if (ty.optionalReprIsPayload(mod)) { |
src/codegen.zig+4| ... | ... | @@ -598,6 +598,10 @@ pub fn generateSymbol( |
| 598 | 598 | .fail => |em| return Result{ .fail = em }, |
| 599 | 599 | } |
| 600 | 600 | } |
| 601 | ||
| 602 | if (layout.padding > 0) { | |
| 603 | try code.writer().writeByteNTimes(0, layout.padding); | |
| 604 | } | |
| 601 | 605 | }, |
| 602 | 606 | .memoized_call => unreachable, |
| 603 | 607 | } |
src/link/Wasm.zig+57-37| ... | ... | @@ -1703,6 +1703,7 @@ pub fn updateDeclExports( |
| 1703 | 1703 | const decl = mod.declPtr(decl_index); |
| 1704 | 1704 | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1705 | 1705 | const atom = wasm.getAtom(atom_index); |
| 1706 | const atom_sym = atom.symbolLoc().getSymbol(wasm).*; | |
| 1706 | 1707 | const gpa = mod.gpa; |
| 1707 | 1708 | |
| 1708 | 1709 | for (exports) |exp| { |
| ... | ... | @@ -1716,43 +1717,21 @@ pub fn updateDeclExports( |
| 1716 | 1717 | continue; |
| 1717 | 1718 | } |
| 1718 | 1719 | |
| 1719 | const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name)); | |
| 1720 | if (wasm.globals.getPtr(export_name)) |existing_loc| { | |
| 1721 | if (existing_loc.index == atom.sym_index) continue; | |
| 1722 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; | |
| 1723 | ||
| 1724 | const exp_is_weak = exp.opts.linkage == .Internal or exp.opts.linkage == .Weak; | |
| 1725 | // When both the to-be-exported symbol and the already existing symbol | |
| 1726 | // are strong symbols, we have a linker error. | |
| 1727 | // In the other case we replace one with the other. | |
| 1728 | if (!exp_is_weak and !existing_sym.isWeak()) { | |
| 1729 | try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create( | |
| 1730 | gpa, | |
| 1731 | decl.srcLoc(mod), | |
| 1732 | \\LinkError: symbol '{}' defined multiple times | |
| 1733 | \\ first definition in '{s}' | |
| 1734 | \\ next definition in '{s}' | |
| 1735 | , | |
| 1736 | .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name }, | |
| 1737 | )); | |
| 1738 | continue; | |
| 1739 | } else if (exp_is_weak) { | |
| 1740 | continue; // to-be-exported symbol is weak, so we keep the existing symbol | |
| 1741 | } else { | |
| 1742 | // TODO: Revisit this, why was this needed? | |
| 1743 | existing_loc.index = atom.sym_index; | |
| 1744 | existing_loc.file = null; | |
| 1745 | // exp.link.wasm.sym_index = existing_loc.index; | |
| 1746 | } | |
| 1747 | } | |
| 1748 | ||
| 1749 | 1720 | const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl); |
| 1750 | 1721 | const exported_atom = wasm.getAtom(exported_atom_index); |
| 1722 | const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name)); | |
| 1751 | 1723 | const sym_loc = exported_atom.symbolLoc(); |
| 1752 | 1724 | const symbol = sym_loc.getSymbol(wasm); |
| 1725 | symbol.setGlobal(true); | |
| 1726 | symbol.setUndefined(false); | |
| 1727 | symbol.index = atom_sym.index; | |
| 1728 | symbol.tag = atom_sym.tag; | |
| 1729 | symbol.name = atom_sym.name; | |
| 1730 | ||
| 1753 | 1731 | switch (exp.opts.linkage) { |
| 1754 | 1732 | .Internal => { |
| 1755 | 1733 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 1734 | symbol.setFlag(.WASM_SYM_BINDING_WEAK); | |
| 1756 | 1735 | }, |
| 1757 | 1736 | .Weak => { |
| 1758 | 1737 | symbol.setFlag(.WASM_SYM_BINDING_WEAK); |
| ... | ... | @@ -1768,22 +1747,52 @@ pub fn updateDeclExports( |
| 1768 | 1747 | continue; |
| 1769 | 1748 | }, |
| 1770 | 1749 | } |
| 1750 | ||
| 1751 | if (wasm.globals.get(export_name)) |existing_loc| { | |
| 1752 | if (existing_loc.index == atom.sym_index) continue; | |
| 1753 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; | |
| 1754 | ||
| 1755 | if (!existing_sym.isUndefined()) blk: { | |
| 1756 | if (symbol.isWeak()) { | |
| 1757 | try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc); | |
| 1758 | continue; // to-be-exported symbol is weak, so we keep the existing symbol | |
| 1759 | } | |
| 1760 | ||
| 1761 | // new symbol is not weak while existing is, replace existing symbol | |
| 1762 | if (existing_sym.isWeak()) { | |
| 1763 | break :blk; | |
| 1764 | } | |
| 1765 | // When both the to-be-exported symbol and the already existing symbol | |
| 1766 | // are strong symbols, we have a linker error. | |
| 1767 | // In the other case we replace one with the other. | |
| 1768 | try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create( | |
| 1769 | gpa, | |
| 1770 | decl.srcLoc(mod), | |
| 1771 | \\LinkError: symbol '{}' defined multiple times | |
| 1772 | \\ first definition in '{s}' | |
| 1773 | \\ next definition in '{s}' | |
| 1774 | , | |
| 1775 | .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name }, | |
| 1776 | )); | |
| 1777 | continue; | |
| 1778 | } | |
| 1779 | ||
| 1780 | // in this case the existing symbol must be replaced either because it's weak or undefined. | |
| 1781 | try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc); | |
| 1782 | _ = wasm.imports.remove(existing_loc); | |
| 1783 | _ = wasm.undefs.swapRemove(existing_sym.name); | |
| 1784 | } | |
| 1785 | ||
| 1771 | 1786 | // Ensure the symbol will be exported using the given name |
| 1772 | 1787 | if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) { |
| 1773 | 1788 | try wasm.export_names.put(wasm.base.allocator, sym_loc, export_name); |
| 1774 | 1789 | } |
| 1775 | 1790 | |
| 1776 | symbol.setGlobal(true); | |
| 1777 | symbol.setUndefined(false); | |
| 1778 | 1791 | try wasm.globals.put( |
| 1779 | 1792 | wasm.base.allocator, |
| 1780 | 1793 | export_name, |
| 1781 | 1794 | sym_loc, |
| 1782 | 1795 | ); |
| 1783 | ||
| 1784 | // if the symbol was previously undefined, remove it as an import | |
| 1785 | _ = wasm.imports.remove(sym_loc); | |
| 1786 | _ = wasm.undefs.swapRemove(export_name); | |
| 1787 | 1796 | } |
| 1788 | 1797 | } |
| 1789 | 1798 | |
| ... | ... | @@ -1900,6 +1909,17 @@ pub fn addOrUpdateImport( |
| 1900 | 1909 | global_gop.value_ptr.* = loc; |
| 1901 | 1910 | try wasm.resolved_symbols.put(wasm.base.allocator, loc, {}); |
| 1902 | 1911 | try wasm.undefs.putNoClobber(wasm.base.allocator, decl_name_index, loc); |
| 1912 | } else if (global_gop.value_ptr.*.index != symbol_index) { | |
| 1913 | // We are not updating a symbol, but found an existing global | |
| 1914 | // symbol with the same name. This means we always favor the | |
| 1915 | // existing symbol, regardless whether it's defined or not. | |
| 1916 | // We can also skip storing the import as we will not output | |
| 1917 | // this symbol. | |
| 1918 | return wasm.discarded.put( | |
| 1919 | wasm.base.allocator, | |
| 1920 | .{ .file = null, .index = symbol_index }, | |
| 1921 | global_gop.value_ptr.*, | |
| 1922 | ); | |
| 1903 | 1923 | } |
| 1904 | 1924 | |
| 1905 | 1925 | if (type_index) |ty_index| { |
| ... | ... | @@ -1915,8 +1935,8 @@ pub fn addOrUpdateImport( |
| 1915 | 1935 | }; |
| 1916 | 1936 | } |
| 1917 | 1937 | } else { |
| 1938 | // non-functions will not be imported from the runtime, but only resolved during link-time | |
| 1918 | 1939 | symbol.tag = .data; |
| 1919 | return; // non-functions will not be imported from the runtime, but only resolved during link-time | |
| 1920 | 1940 | } |
| 1921 | 1941 | } |
| 1922 | 1942 |
test/behavior.zig+1-5| ... | ... | @@ -158,6 +158,7 @@ test { |
| 158 | 158 | _ = @import("behavior/enum.zig"); |
| 159 | 159 | _ = @import("behavior/error.zig"); |
| 160 | 160 | _ = @import("behavior/eval.zig"); |
| 161 | _ = @import("behavior/export_self_referential_type_info.zig"); | |
| 161 | 162 | _ = @import("behavior/field_parent_ptr.zig"); |
| 162 | 163 | _ = @import("behavior/floatop.zig"); |
| 163 | 164 | _ = @import("behavior/fn.zig"); |
| ... | ... | @@ -241,7 +242,6 @@ test { |
| 241 | 242 | if (builtin.zig_backend != .stage2_arm and |
| 242 | 243 | builtin.zig_backend != .stage2_x86_64 and |
| 243 | 244 | builtin.zig_backend != .stage2_aarch64 and |
| 244 | builtin.zig_backend != .stage2_wasm and | |
| 245 | 245 | builtin.zig_backend != .stage2_c and |
| 246 | 246 | builtin.zig_backend != .stage2_spirv64) |
| 247 | 247 | { |
| ... | ... | @@ -250,8 +250,4 @@ test { |
| 250 | 250 | _ = @import("behavior/bugs/14198.zig"); |
| 251 | 251 | _ = @import("behavior/export.zig"); |
| 252 | 252 | } |
| 253 | ||
| 254 | if (builtin.zig_backend != .stage2_wasm) { | |
| 255 | _ = @import("behavior/export_self_referential_type_info.zig"); | |
| 256 | } | |
| 257 | 253 | } |
test/behavior/bugs/1381.zig-1| ... | ... | @@ -17,7 +17,6 @@ test "union that needs padding bytes inside an array" { |
| 17 | 17 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 18 | 18 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 19 | 19 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 20 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 21 | 20 | |
| 22 | 21 | var as = [_]A{ |
| 23 | 22 | A{ .B = B{ .D = 1 } }, |
test/behavior/bugs/529.zig-1| ... | ... | @@ -11,7 +11,6 @@ comptime { |
| 11 | 11 | const builtin = @import("builtin"); |
| 12 | 12 | |
| 13 | 13 | test "issue 529 fixed" { |
| 14 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 15 | 14 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 16 | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 17 | 16 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |