| ... | @@ -112,6 +112,8 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, | ... | @@ -112,6 +112,8 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, |
| 112 | functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.wasm.Func) = .{}, | 112 | functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.wasm.Func) = .{}, |
| 113 | /// Output global section | 113 | /// Output global section |
| 114 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, | 114 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, |
| | 115 | /// Global symbols for exported data symbols |
| | 116 | address_globals: std.ArrayListUnmanaged(SymbolLoc) = .{}, |
| 115 | /// Memory section | 117 | /// Memory section |
| 116 | memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, | 118 | memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, |
| 117 | /// Output table section | 119 | /// Output table section |
| ... | @@ -311,13 +313,23 @@ pub const StringTable = struct { | ... | @@ -311,13 +313,23 @@ pub const StringTable = struct { |
| 311 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { | 313 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 312 | assert(options.target.ofmt == .wasm); | 314 | assert(options.target.ofmt == .wasm); |
| 313 | | 315 | |
| 314 | if (build_options.have_llvm and options.use_llvm) { | 316 | if (build_options.have_llvm and options.use_llvm and options.use_lld) { |
| 315 | return createEmpty(allocator, options); | 317 | return createEmpty(allocator, options); |
| 316 | } | 318 | } |
| 317 | | 319 | |
| 318 | const wasm_bin = try createEmpty(allocator, options); | 320 | const wasm_bin = try createEmpty(allocator, options); |
| 319 | errdefer wasm_bin.base.destroy(); | 321 | errdefer wasm_bin.base.destroy(); |
| 320 | | 322 | |
| | 323 | // We are not using LLD at this point, so ensure we set the intermediary basename |
| | 324 | if (build_options.have_llvm and options.use_llvm and options.module != null) { |
| | 325 | // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, |
| | 326 | // we also want to put the intermediary object file in the cache while the |
| | 327 | // main emit directory is the cwd. |
| | 328 | wasm_bin.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ |
| | 329 | options.emit.?.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), |
| | 330 | }); |
| | 331 | } |
| | 332 | |
| 321 | // TODO: read the file and keep valid parts instead of truncating | 333 | // TODO: read the file and keep valid parts instead of truncating |
| 322 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); | 334 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 323 | wasm_bin.base.file = file; | 335 | wasm_bin.base.file = file; |
| ... | @@ -613,7 +625,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -613,7 +625,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 613 | try wasm.resolved_symbols.put(wasm.base.allocator, location, {}); | 625 | try wasm.resolved_symbols.put(wasm.base.allocator, location, {}); |
| 614 | assert(wasm.resolved_symbols.swapRemove(existing_loc)); | 626 | assert(wasm.resolved_symbols.swapRemove(existing_loc)); |
| 615 | if (existing_sym.isUndefined()) { | 627 | if (existing_sym.isUndefined()) { |
| 616 | assert(wasm.undefs.swapRemove(sym_name)); | 628 | _ = wasm.undefs.swapRemove(sym_name); |
| 617 | } | 629 | } |
| 618 | } | 630 | } |
| 619 | } | 631 | } |
| ... | @@ -624,8 +636,7 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { | ... | @@ -624,8 +636,7 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 624 | log.debug("Resolving symbols in archives", .{}); | 636 | log.debug("Resolving symbols in archives", .{}); |
| 625 | var index: u32 = 0; | 637 | var index: u32 = 0; |
| 626 | undef_loop: while (index < wasm.undefs.count()) { | 638 | undef_loop: while (index < wasm.undefs.count()) { |
| 627 | const undef_sym_loc = wasm.undefs.values()[index]; | 639 | const sym_name = wasm.undefs.keys()[index]; |
| 628 | const sym_name = undef_sym_loc.getName(wasm); | | |
| 629 | | 640 | |
| 630 | for (wasm.archives.items) |archive| { | 641 | for (wasm.archives.items) |archive| { |
| 631 | const offset = archive.toc.get(sym_name) orelse { | 642 | const offset = archive.toc.get(sym_name) orelse { |
| ... | @@ -829,6 +840,7 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -829,6 +840,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 829 | wasm.func_types.deinit(gpa); | 840 | wasm.func_types.deinit(gpa); |
| 830 | wasm.functions.deinit(gpa); | 841 | wasm.functions.deinit(gpa); |
| 831 | wasm.wasm_globals.deinit(gpa); | 842 | wasm.wasm_globals.deinit(gpa); |
| | 843 | wasm.address_globals.deinit(gpa); |
| 832 | wasm.function_table.deinit(gpa); | 844 | wasm.function_table.deinit(gpa); |
| 833 | wasm.tables.deinit(gpa); | 845 | wasm.tables.deinit(gpa); |
| 834 | wasm.exports.deinit(gpa); | 846 | wasm.exports.deinit(gpa); |
| ... | @@ -1556,9 +1568,13 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1556,9 +1568,13 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1556 | var atom: *Atom = entry.value_ptr.*.getFirst(); | 1568 | var atom: *Atom = entry.value_ptr.*.getFirst(); |
| 1557 | var offset: u32 = 0; | 1569 | var offset: u32 = 0; |
| 1558 | while (true) { | 1570 | while (true) { |
| | 1571 | const symbol_loc = atom.symbolLoc(); |
| | 1572 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| | 1573 | atom = atom.next orelse break; |
| | 1574 | continue; |
| | 1575 | } |
| 1559 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); | 1576 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); |
| 1560 | atom.offset = offset; | 1577 | atom.offset = offset; |
| 1561 | const symbol_loc = atom.symbolLoc(); | | |
| 1562 | log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ | 1578 | log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ |
| 1563 | symbol_loc.getName(wasm), | 1579 | symbol_loc.getName(wasm), |
| 1564 | offset, | 1580 | offset, |
| ... | @@ -1566,7 +1582,7 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1566,7 +1582,7 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1566 | atom.size, | 1582 | atom.size, |
| 1567 | }); | 1583 | }); |
| 1568 | offset += atom.size; | 1584 | offset += atom.size; |
| 1569 | try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); // Update atom pointers | 1585 | try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers |
| 1570 | atom = atom.next orelse break; | 1586 | atom = atom.next orelse break; |
| 1571 | } | 1587 | } |
| 1572 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); | 1588 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | @@ -1702,7 +1718,7 @@ fn mergeSections(wasm: *Wasm) !void { | ... | @@ -1702,7 +1718,7 @@ fn mergeSections(wasm: *Wasm) !void { |
| 1702 | continue; | 1718 | continue; |
| 1703 | } | 1719 | } |
| 1704 | | 1720 | |
| 1705 | const object = wasm.objects.items[sym_loc.file.?]; | 1721 | const object = &wasm.objects.items[sym_loc.file.?]; |
| 1706 | const symbol = &object.symtable[sym_loc.index]; | 1722 | const symbol = &object.symtable[sym_loc.index]; |
| 1707 | if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) { | 1723 | if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) { |
| 1708 | // Skip undefined symbols as they go in the `import` section | 1724 | // Skip undefined symbols as they go in the `import` section |
| ... | @@ -1714,13 +1730,12 @@ fn mergeSections(wasm: *Wasm) !void { | ... | @@ -1714,13 +1730,12 @@ fn mergeSections(wasm: *Wasm) !void { |
| 1714 | const index = symbol.index - offset; | 1730 | const index = symbol.index - offset; |
| 1715 | switch (symbol.tag) { | 1731 | switch (symbol.tag) { |
| 1716 | .function => { | 1732 | .function => { |
| 1717 | const original_func = object.functions[index]; | | |
| 1718 | const gop = try wasm.functions.getOrPut( | 1733 | const gop = try wasm.functions.getOrPut( |
| 1719 | wasm.base.allocator, | 1734 | wasm.base.allocator, |
| 1720 | .{ .file = sym_loc.file, .index = symbol.index }, | 1735 | .{ .file = sym_loc.file, .index = symbol.index }, |
| 1721 | ); | 1736 | ); |
| 1722 | if (!gop.found_existing) { | 1737 | if (!gop.found_existing) { |
| 1723 | gop.value_ptr.* = original_func; | 1738 | gop.value_ptr.* = object.functions[index]; |
| 1724 | } | 1739 | } |
| 1725 | symbol.index = @intCast(u32, gop.index) + wasm.imported_functions_count; | 1740 | symbol.index = @intCast(u32, gop.index) + wasm.imported_functions_count; |
| 1726 | }, | 1741 | }, |
| ... | @@ -1768,7 +1783,7 @@ fn mergeTypes(wasm: *Wasm) !void { | ... | @@ -1768,7 +1783,7 @@ fn mergeTypes(wasm: *Wasm) !void { |
| 1768 | | 1783 | |
| 1769 | if (symbol.isUndefined()) { | 1784 | if (symbol.isUndefined()) { |
| 1770 | log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); | 1785 | log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); |
| 1771 | const import: *types.Import = wasm.imports.getPtr(sym_loc).?; | 1786 | const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue; |
| 1772 | const original_type = object.func_types[import.kind.function]; | 1787 | const original_type = object.func_types[import.kind.function]; |
| 1773 | import.kind.function = try wasm.putOrGetFuncType(original_type); | 1788 | import.kind.function = try wasm.putOrGetFuncType(original_type); |
| 1774 | } else if (!dirty.contains(symbol.index)) { | 1789 | } else if (!dirty.contains(symbol.index)) { |
| ... | @@ -1794,7 +1809,15 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1794,7 +1809,15 @@ fn setupExports(wasm: *Wasm) !void { |
| 1794 | if (sym_loc.file == null) break :blk symbol.name; | 1809 | if (sym_loc.file == null) break :blk symbol.name; |
| 1795 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); | 1810 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); |
| 1796 | }; | 1811 | }; |
| 1797 | const exp: types.Export = .{ | 1812 | const exp: types.Export = if (symbol.tag == .data) exp: { |
| | 1813 | const global_index = @intCast(u32, wasm.wasm_globals.items.len + wasm.address_globals.items.len); |
| | 1814 | try wasm.address_globals.append(wasm.base.allocator, sym_loc); |
| | 1815 | break :exp .{ |
| | 1816 | .name = export_name, |
| | 1817 | .kind = .global, |
| | 1818 | .index = global_index, |
| | 1819 | }; |
| | 1820 | } else .{ |
| 1798 | .name = export_name, | 1821 | .name = export_name, |
| 1799 | .kind = symbol.tag.externalType(), | 1822 | .kind = symbol.tag.externalType(), |
| 1800 | .index = symbol.index, | 1823 | .index = symbol.index, |
| ... | @@ -2134,7 +2157,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! | ... | @@ -2134,7 +2157,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! |
| 2134 | const new_index = @intCast(u32, wasm.segments.items.len); | 2157 | const new_index = @intCast(u32, wasm.segments.items.len); |
| 2135 | index.* = new_index; | 2158 | index.* = new_index; |
| 2136 | try wasm.appendDummySegment(); | 2159 | try wasm.appendDummySegment(); |
| 2137 | // _ = index; | | |
| 2138 | | 2160 | |
| 2139 | const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { | 2161 | const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { |
| 2140 | const tmp_index = @intCast(u32, wasm.symbols.items.len); | 2162 | const tmp_index = @intCast(u32, wasm.symbols.items.len); |
| ... | @@ -2202,13 +2224,213 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin | ... | @@ -2202,13 +2224,213 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin |
| 2202 | } | 2224 | } |
| 2203 | return; | 2225 | return; |
| 2204 | } | 2226 | } |
| | 2227 | |
| 2205 | if (build_options.have_llvm and wasm.base.options.use_lld) { | 2228 | if (build_options.have_llvm and wasm.base.options.use_lld) { |
| 2206 | return wasm.linkWithLLD(comp, prog_node); | 2229 | return wasm.linkWithLLD(comp, prog_node); |
| | 2230 | } else if (build_options.have_llvm and wasm.base.options.use_llvm and !wasm.base.options.use_lld) { |
| | 2231 | return wasm.linkWithZld(comp, prog_node); |
| 2207 | } else { | 2232 | } else { |
| 2208 | return wasm.flushModule(comp, prog_node); | 2233 | return wasm.flushModule(comp, prog_node); |
| 2209 | } | 2234 | } |
| 2210 | } | 2235 | } |
| 2211 | | 2236 | |
| | 2237 | /// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary. |
| | 2238 | fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| | 2239 | const tracy = trace(@src()); |
| | 2240 | defer tracy.end(); |
| | 2241 | |
| | 2242 | const gpa = wasm.base.allocator; |
| | 2243 | const options = wasm.base.options; |
| | 2244 | |
| | 2245 | // Used for all temporary memory allocated during flushin |
| | 2246 | var arena_instance = std.heap.ArenaAllocator.init(gpa); |
| | 2247 | defer arena_instance.deinit(); |
| | 2248 | const arena = arena_instance.allocator(); |
| | 2249 | |
| | 2250 | const directory = options.emit.?.directory; // Just an alias to make it shorter to type. |
| | 2251 | const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path}); |
| | 2252 | |
| | 2253 | // If there is no Zig code to compile, then we should skip flushing the output file because it |
| | 2254 | // will not be part of the linker line anyway. |
| | 2255 | const module_obj_path: ?[]const u8 = if (options.module != null) blk: { |
| | 2256 | assert(options.use_llvm); // `linkWithZld` should never be called when the Wasm backend is used |
| | 2257 | try wasm.flushModule(comp, prog_node); |
| | 2258 | |
| | 2259 | if (fs.path.dirname(full_out_path)) |dirname| { |
| | 2260 | break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? }); |
| | 2261 | } else { |
| | 2262 | break :blk wasm.base.intermediary_basename.?; |
| | 2263 | } |
| | 2264 | } else null; |
| | 2265 | |
| | 2266 | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| | 2267 | sub_prog_node.activate(); |
| | 2268 | defer sub_prog_node.end(); |
| | 2269 | |
| | 2270 | const is_obj = options.output_mode == .Obj; |
| | 2271 | const compiler_rt_path: ?[]const u8 = if (options.include_compiler_rt and !is_obj) |
| | 2272 | comp.compiler_rt_lib.?.full_object_path |
| | 2273 | else |
| | 2274 | null; |
| | 2275 | const id_symlink_basename = "zld.id"; |
| | 2276 | |
| | 2277 | var man: Cache.Manifest = undefined; |
| | 2278 | defer if (!options.disable_lld_caching) man.deinit(); |
| | 2279 | var digest: [Cache.hex_digest_len]u8 = undefined; |
| | 2280 | |
| | 2281 | // NOTE: The following section must be maintained to be equal |
| | 2282 | // as the section defined in `linkWithLLD` |
| | 2283 | if (!options.disable_lld_caching) { |
| | 2284 | man = comp.cache_parent.obtain(); |
| | 2285 | |
| | 2286 | // We are about to obtain this lock, so here we give other processes a chance first. |
| | 2287 | wasm.base.releaseLock(); |
| | 2288 | |
| | 2289 | comptime assert(Compilation.link_hash_implementation_version == 7); |
| | 2290 | |
| | 2291 | for (options.objects) |obj| { |
| | 2292 | _ = try man.addFile(obj.path, null); |
| | 2293 | man.hash.add(obj.must_link); |
| | 2294 | } |
| | 2295 | for (comp.c_object_table.keys()) |key| { |
| | 2296 | _ = try man.addFile(key.status.success.object_path, null); |
| | 2297 | } |
| | 2298 | try man.addOptionalFile(module_obj_path); |
| | 2299 | try man.addOptionalFile(compiler_rt_path); |
| | 2300 | man.hash.addOptionalBytes(options.entry); |
| | 2301 | man.hash.addOptional(options.stack_size_override); |
| | 2302 | man.hash.add(options.import_memory); |
| | 2303 | man.hash.add(options.import_table); |
| | 2304 | man.hash.add(options.export_table); |
| | 2305 | man.hash.addOptional(options.initial_memory); |
| | 2306 | man.hash.addOptional(options.max_memory); |
| | 2307 | man.hash.add(options.shared_memory); |
| | 2308 | man.hash.addOptional(options.global_base); |
| | 2309 | man.hash.add(options.export_symbol_names.len); |
| | 2310 | // strip does not need to go into the linker hash because it is part of the hash namespace |
| | 2311 | for (options.export_symbol_names) |symbol_name| { |
| | 2312 | man.hash.addBytes(symbol_name); |
| | 2313 | } |
| | 2314 | |
| | 2315 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| | 2316 | _ = try man.hit(); |
| | 2317 | digest = man.final(); |
| | 2318 | |
| | 2319 | var prev_digest_buf: [digest.len]u8 = undefined; |
| | 2320 | const prev_digest: []u8 = Cache.readSmallFile( |
| | 2321 | directory.handle, |
| | 2322 | id_symlink_basename, |
| | 2323 | &prev_digest_buf, |
| | 2324 | ) catch |err| blk: { |
| | 2325 | log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); |
| | 2326 | // Handle this as a cache miss. |
| | 2327 | break :blk prev_digest_buf[0..0]; |
| | 2328 | }; |
| | 2329 | if (mem.eql(u8, prev_digest, &digest)) { |
| | 2330 | log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); |
| | 2331 | // Hot diggity dog! The output binary is already there. |
| | 2332 | wasm.base.lock = man.toOwnedLock(); |
| | 2333 | return; |
| | 2334 | } |
| | 2335 | log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); |
| | 2336 | |
| | 2337 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| | 2338 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| | 2339 | error.FileNotFound => {}, |
| | 2340 | else => |e| return e, |
| | 2341 | }; |
| | 2342 | } |
| | 2343 | |
| | 2344 | // Positional arguments to the linker such as object files and static archives. |
| | 2345 | var positionals = std.ArrayList([]const u8).init(arena); |
| | 2346 | try positionals.ensureUnusedCapacity(options.objects.len); |
| | 2347 | |
| | 2348 | // When the target os is WASI, we allow linking with WASI-LIBC |
| | 2349 | if (options.target.os.tag == .wasi) { |
| | 2350 | const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or |
| | 2351 | (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic); |
| | 2352 | if (is_exe_or_dyn_lib) { |
| | 2353 | const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs; |
| | 2354 | for (wasi_emulated_libs) |crt_file| { |
| | 2355 | try positionals.append(try comp.get_libc_crt_file( |
| | 2356 | arena, |
| | 2357 | wasi_libc.emulatedLibCRFileLibName(crt_file), |
| | 2358 | )); |
| | 2359 | } |
| | 2360 | |
| | 2361 | if (wasm.base.options.link_libc) { |
| | 2362 | try positionals.append(try comp.get_libc_crt_file( |
| | 2363 | arena, |
| | 2364 | wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), |
| | 2365 | )); |
| | 2366 | try positionals.append(try comp.get_libc_crt_file(arena, "libc.a")); |
| | 2367 | } |
| | 2368 | |
| | 2369 | if (wasm.base.options.link_libcpp) { |
| | 2370 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); |
| | 2371 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); |
| | 2372 | } |
| | 2373 | } |
| | 2374 | } |
| | 2375 | |
| | 2376 | if (module_obj_path) |path| { |
| | 2377 | try positionals.append(path); |
| | 2378 | } |
| | 2379 | |
| | 2380 | for (options.objects) |object| { |
| | 2381 | try positionals.append(object.path); |
| | 2382 | } |
| | 2383 | |
| | 2384 | for (comp.c_object_table.keys()) |c_object| { |
| | 2385 | try positionals.append(c_object.status.success.object_path); |
| | 2386 | } |
| | 2387 | |
| | 2388 | if (comp.compiler_rt_lib) |lib| { |
| | 2389 | try positionals.append(lib.full_object_path); |
| | 2390 | } |
| | 2391 | |
| | 2392 | try wasm.parseInputFiles(positionals.items); |
| | 2393 | |
| | 2394 | for (wasm.objects.items) |_, object_index| { |
| | 2395 | try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); |
| | 2396 | } |
| | 2397 | |
| | 2398 | var emit_features_count: u32 = 0; |
| | 2399 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| | 2400 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| | 2401 | try wasm.resolveSymbolsInArchives(); |
| | 2402 | |
| | 2403 | try wasm.setupStart(); |
| | 2404 | try wasm.setupImports(); |
| | 2405 | |
| | 2406 | for (wasm.objects.items) |*object, object_index| { |
| | 2407 | try object.parseIntoAtoms(gpa, @intCast(u16, object_index), wasm); |
| | 2408 | } |
| | 2409 | |
| | 2410 | try wasm.allocateAtoms(); |
| | 2411 | try wasm.setupMemory(); |
| | 2412 | wasm.mapFunctionTable(); |
| | 2413 | try wasm.mergeSections(); |
| | 2414 | try wasm.mergeTypes(); |
| | 2415 | try wasm.setupExports(); |
| | 2416 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| | 2417 | |
| | 2418 | if (!wasm.base.options.disable_lld_caching) { |
| | 2419 | // Update the file with the digest. If it fails we can continue; it only |
| | 2420 | // means that the next invocation will have an unnecessary cache miss. |
| | 2421 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { |
| | 2422 | log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); |
| | 2423 | }; |
| | 2424 | // Again failure here only means an unnecessary cache miss. |
| | 2425 | man.writeManifest() catch |err| { |
| | 2426 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); |
| | 2427 | }; |
| | 2428 | // We hang on to this lock so that the output file path can be used without |
| | 2429 | // other processes clobbering it. |
| | 2430 | wasm.base.lock = man.toOwnedLock(); |
| | 2431 | } |
| | 2432 | } |
| | 2433 | |
| 2212 | pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { | 2434 | pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 2213 | const tracy = trace(@src()); | 2435 | const tracy = trace(@src()); |
| 2214 | defer tracy.end(); | 2436 | defer tracy.end(); |
| ... | @@ -2219,20 +2441,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2219,20 +2441,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2219 | } | 2441 | } |
| 2220 | } | 2442 | } |
| 2221 | | 2443 | |
| 2222 | var sub_prog_node = prog_node.start("WASM Flush", 0); | 2444 | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 2223 | sub_prog_node.activate(); | 2445 | sub_prog_node.activate(); |
| 2224 | defer sub_prog_node.end(); | 2446 | defer sub_prog_node.end(); |
| 2225 | | 2447 | |
| 2226 | // ensure the error names table is populated when an error name is referenced | 2448 | // ensure the error names table is populated when an error name is referenced |
| 2227 | try wasm.populateErrorNameTable(); | 2449 | try wasm.populateErrorNameTable(); |
| 2228 | | 2450 | |
| 2229 | // The amount of sections that will be written | | |
| 2230 | var section_count: u32 = 0; | | |
| 2231 | // Index of the code section. Used to tell relocation table where the section lives. | | |
| 2232 | var code_section_index: ?u32 = null; | | |
| 2233 | // Index of the data section. Used to tell relocation table where the section lives. | | |
| 2234 | var data_section_index: ?u32 = null; | | |
| 2235 | | | |
| 2236 | // Used for all temporary memory allocated during flushin | 2451 | // Used for all temporary memory allocated during flushin |
| 2237 | var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator); | 2452 | var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator); |
| 2238 | defer arena_instance.deinit(); | 2453 | defer arena_instance.deinit(); |
| ... | @@ -2312,9 +2527,25 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2312,9 +2527,25 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2312 | try wasm.mergeSections(); | 2527 | try wasm.mergeSections(); |
| 2313 | try wasm.mergeTypes(); | 2528 | try wasm.mergeTypes(); |
| 2314 | try wasm.setupExports(); | 2529 | try wasm.setupExports(); |
| | 2530 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| | 2531 | } |
| 2315 | | 2532 | |
| | 2533 | /// Writes the WebAssembly in-memory module to the file |
| | 2534 | fn writeToFile( |
| | 2535 | wasm: *Wasm, |
| | 2536 | enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool, |
| | 2537 | feature_count: u32, |
| | 2538 | arena: Allocator, |
| | 2539 | ) !void { |
| | 2540 | // Size of each section header |
| 2316 | const header_size = 5 + 1; | 2541 | const header_size = 5 + 1; |
| 2317 | const is_obj = wasm.base.options.output_mode == .Obj; | 2542 | // The amount of sections that will be written |
| | 2543 | var section_count: u32 = 0; |
| | 2544 | // Index of the code section. Used to tell relocation table where the section lives. |
| | 2545 | var code_section_index: ?u32 = null; |
| | 2546 | // Index of the data section. Used to tell relocation table where the section lives. |
| | 2547 | var data_section_index: ?u32 = null; |
| | 2548 | const is_obj = wasm.base.options.output_mode == .Obj or (!wasm.base.options.use_llvm and wasm.base.options.use_lld); |
| 2318 | | 2549 | |
| 2319 | var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator); | 2550 | var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator); |
| 2320 | defer binary_bytes.deinit(); | 2551 | defer binary_bytes.deinit(); |
| ... | @@ -2461,10 +2692,22 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2461,10 +2692,22 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2461 | if (wasm.wasm_globals.items.len > 0) { | 2692 | if (wasm.wasm_globals.items.len > 0) { |
| 2462 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 2693 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2463 | | 2694 | |
| | 2695 | var global_count: u32 = 0; |
| 2464 | for (wasm.wasm_globals.items) |global| { | 2696 | for (wasm.wasm_globals.items) |global| { |
| 2465 | try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype)); | 2697 | try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype)); |
| 2466 | try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); | 2698 | try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); |
| 2467 | try emitInit(binary_writer, global.init); | 2699 | try emitInit(binary_writer, global.init); |
| | 2700 | global_count += 1; |
| | 2701 | } |
| | 2702 | |
| | 2703 | for (wasm.address_globals.items) |sym_loc| { |
| | 2704 | const atom = wasm.symbol_atom.get(sym_loc).?; |
| | 2705 | try binary_writer.writeByte(std.wasm.valtype(.i32)); |
| | 2706 | try binary_writer.writeByte(0); // immutable |
| | 2707 | try emitInit(binary_writer, .{ |
| | 2708 | .i32_const = @bitCast(i32, atom.offset), |
| | 2709 | }); |
| | 2710 | global_count += 1; |
| 2468 | } | 2711 | } |
| 2469 | | 2712 | |
| 2470 | try writeVecSectionHeader( | 2713 | try writeVecSectionHeader( |
| ... | @@ -2472,7 +2715,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2472,7 +2715,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2472 | header_offset, | 2715 | header_offset, |
| 2473 | .global, | 2716 | .global, |
| 2474 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), | 2717 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2475 | @intCast(u32, wasm.wasm_globals.items.len), | 2718 | @intCast(u32, global_count), |
| 2476 | ); | 2719 | ); |
| 2477 | section_count += 1; | 2720 | section_count += 1; |
| 2478 | } | 2721 | } |
| ... | @@ -2708,8 +2951,8 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2708,8 +2951,8 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2708 | } | 2951 | } |
| 2709 | | 2952 | |
| 2710 | try emitProducerSection(&binary_bytes); | 2953 | try emitProducerSection(&binary_bytes); |
| 2711 | if (emit_features_count > 0) { | 2954 | if (feature_count > 0) { |
| 2712 | try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count); | 2955 | try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count); |
| 2713 | } | 2956 | } |
| 2714 | } | 2957 | } |
| 2715 | | 2958 | |
| ... | @@ -2849,7 +3092,9 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem | ... | @@ -2849,7 +3092,9 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem |
| 2849 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 3092 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2850 | const symbol = sym_loc.getSymbol(wasm).*; | 3093 | const symbol = sym_loc.getSymbol(wasm).*; |
| 2851 | const name = if (symbol.isUndefined()) blk: { | 3094 | const name = if (symbol.isUndefined()) blk: { |
| 2852 | break :blk wasm.string_table.get(wasm.imports.get(sym_loc).?.name); | 3095 | if (symbol.tag == .data) continue; |
| | 3096 | const imp = wasm.imports.get(sym_loc) orelse continue; |
| | 3097 | break :blk wasm.string_table.get(imp.name); |
| 2853 | } else sym_loc.getName(wasm); | 3098 | } else sym_loc.getName(wasm); |
| 2854 | switch (symbol.tag) { | 3099 | switch (symbol.tag) { |
| 2855 | .function => { | 3100 | .function => { |
| ... | @@ -3607,15 +3852,23 @@ fn emitDataRelocations( | ... | @@ -3607,15 +3852,23 @@ fn emitDataRelocations( |
| 3607 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); | 3852 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); |
| 3608 | } | 3853 | } |
| 3609 | | 3854 | |
| 3610 | /// Searches for an a matching function signature, when not found | 3855 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 3611 | /// a new entry will be made. The index of the existing/new signature will be returned. | | |
| 3612 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { | | |
| 3613 | var index: u32 = 0; | 3856 | var index: u32 = 0; |
| 3614 | while (index < wasm.func_types.items.len) : (index += 1) { | 3857 | while (index < wasm.func_types.items.len) : (index += 1) { |
| 3615 | if (wasm.func_types.items[index].eql(func_type)) return index; | 3858 | if (wasm.func_types.items[index].eql(func_type)) return index; |
| 3616 | } | 3859 | } |
| | 3860 | return null; |
| | 3861 | } |
| | 3862 | |
| | 3863 | /// Searches for an a matching function signature, when not found |
| | 3864 | /// a new entry will be made. The index of the existing/new signature will be returned. |
| | 3865 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| | 3866 | if (wasm.getTypeIndex(func_type)) |index| { |
| | 3867 | return index; |
| | 3868 | } |
| 3617 | | 3869 | |
| 3618 | // functype does not exist. | 3870 | // functype does not exist. |
| | 3871 | const index = @intCast(u32, wasm.func_types.items.len); |
| 3619 | const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params); | 3872 | const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params); |
| 3620 | errdefer wasm.base.allocator.free(params); | 3873 | errdefer wasm.base.allocator.free(params); |
| 3621 | const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns); | 3874 | const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns); |