| ... | ... | @@ -1,67 +1,78 @@ |
| 1 | 1 | const Wasm = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | | const builtin = @import("builtin"); |
| 5 | | const mem = std.mem; |
| 6 | | const Allocator = std.mem.Allocator; |
| 4 | |
| 7 | 5 | const assert = std.debug.assert; |
| 6 | const build_options = @import("build_options"); |
| 7 | const builtin = @import("builtin"); |
| 8 | const codegen = @import("../codegen.zig"); |
| 8 | 9 | const fs = std.fs; |
| 9 | 10 | const leb = std.leb; |
| 10 | | const log = std.log.scoped(.link); |
| 11 | | |
| 12 | | pub const Atom = @import("Wasm/Atom.zig"); |
| 13 | | const Dwarf = @import("Dwarf.zig"); |
| 14 | | const Module = @import("../Module.zig"); |
| 15 | | const InternPool = @import("../InternPool.zig"); |
| 16 | | const Compilation = @import("../Compilation.zig"); |
| 17 | | const CodeGen = @import("../arch/wasm/CodeGen.zig"); |
| 18 | | const codegen = @import("../codegen.zig"); |
| 19 | 11 | const link = @import("../link.zig"); |
| 20 | 12 | const lldMain = @import("../main.zig").lldMain; |
| 13 | const log = std.log.scoped(.link); |
| 14 | const gc_log = std.log.scoped(.gc); |
| 15 | const mem = std.mem; |
| 21 | 16 | const trace = @import("../tracy.zig").trace; |
| 22 | | const build_options = @import("build_options"); |
| 17 | const types = @import("Wasm/types.zig"); |
| 23 | 18 | const wasi_libc = @import("../wasi_libc.zig"); |
| 24 | | const Cache = std.Build.Cache; |
| 25 | | const Type = @import("../type.zig").Type; |
| 26 | | const Value = @import("../Value.zig"); |
| 27 | | const TypedValue = @import("../TypedValue.zig"); |
| 28 | | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 19 | |
| 29 | 20 | const Air = @import("../Air.zig"); |
| 21 | const Allocator = std.mem.Allocator; |
| 22 | const Archive = @import("Wasm/Archive.zig"); |
| 23 | const Cache = std.Build.Cache; |
| 24 | const CodeGen = @import("../arch/wasm/CodeGen.zig"); |
| 25 | const Compilation = @import("../Compilation.zig"); |
| 26 | const Dwarf = @import("Dwarf.zig"); |
| 27 | const File = @import("Wasm/file.zig").File; |
| 28 | const InternPool = @import("../InternPool.zig"); |
| 30 | 29 | const Liveness = @import("../Liveness.zig"); |
| 31 | | const Symbol = @import("Wasm/Symbol.zig"); |
| 30 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 31 | const Module = @import("../Module.zig"); |
| 32 | 32 | const Object = @import("Wasm/Object.zig"); |
| 33 | | const Archive = @import("Wasm/Archive.zig"); |
| 34 | | const types = @import("Wasm/types.zig"); |
| 33 | const Symbol = @import("Wasm/Symbol.zig"); |
| 34 | const Type = @import("../type.zig").Type; |
| 35 | const TypedValue = @import("../TypedValue.zig"); |
| 36 | const ZigObject = @import("Wasm/ZigObject.zig"); |
| 37 | |
| 38 | pub const Atom = @import("Wasm/Atom.zig"); |
| 35 | 39 | pub const Relocation = types.Relocation; |
| 36 | 40 | |
| 37 | 41 | pub const base_tag: link.File.Tag = .wasm; |
| 38 | 42 | |
| 39 | 43 | base: link.File, |
| 44 | /// Symbol name of the entry function to export |
| 40 | 45 | entry_name: ?[]const u8, |
| 46 | /// When true, will allow undefined symbols |
| 41 | 47 | import_symbols: bool, |
| 48 | /// List of *global* symbol names to export to the host environment. |
| 42 | 49 | export_symbol_names: []const []const u8, |
| 50 | /// When defined, sets the start of the data section. |
| 43 | 51 | global_base: ?u64, |
| 52 | /// When defined, sets the initial memory size of the memory. |
| 44 | 53 | initial_memory: ?u64, |
| 54 | /// When defined, sets the maximum memory size of the memory. |
| 45 | 55 | max_memory: ?u64, |
| 56 | /// When true, will import the function table from the host environment. |
| 57 | import_table: bool, |
| 58 | /// When true, will export the function table to the host environment. |
| 59 | export_table: bool, |
| 46 | 60 | /// Output name of the file |
| 47 | 61 | name: []const u8, |
| 48 | 62 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 49 | 63 | llvm_object: ?*LlvmObject = null, |
| 64 | /// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists, |
| 65 | /// and the chosen backend is the Wasm backend. |
| 66 | zig_object_index: File.Index = .null, |
| 67 | /// List of relocatable files to be linked into the final binary. |
| 68 | files: std.MultiArrayList(File.Entry) = .{}, |
| 50 | 69 | /// When importing objects from the host environment, a name must be supplied. |
| 51 | 70 | /// LLVM uses "env" by default when none is given. This would be a good default for Zig |
| 52 | 71 | /// to support existing code. |
| 53 | 72 | /// TODO: Allow setting this through a flag? |
| 54 | 73 | host_name: []const u8 = "env", |
| 55 | | /// List of all `Decl` that are currently alive. |
| 56 | | /// Each index maps to the corresponding `Atom.Index`. |
| 57 | | decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{}, |
| 58 | | /// Mapping between an `Atom` and its type index representing the Wasm |
| 59 | | /// type of the function signature. |
| 60 | | atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{}, |
| 61 | | /// List of all symbols generated by Zig code. |
| 62 | | symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 63 | | /// List of symbol indexes which are free to be used. |
| 64 | | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 74 | /// List of symbols generated by the linker. |
| 75 | synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 65 | 76 | /// Maps atoms to their segment index |
| 66 | 77 | atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, |
| 67 | 78 | /// List of all atoms. |
| ... | ... | @@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 107 | 118 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, |
| 108 | 119 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 109 | 120 | string_table: StringTable = .{}, |
| 110 | | /// Debug information for wasm |
| 111 | | dwarf: ?Dwarf = null, |
| 112 | 121 | |
| 113 | 122 | // Output sections |
| 114 | 123 | /// Output type section |
| ... | ... | @@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, |
| 116 | 125 | /// Output function section where the key is the original |
| 117 | 126 | /// function index and the value is function. |
| 118 | 127 | /// This allows us to map multiple symbols to the same function. |
| 119 | | functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, struct { func: std.wasm.Func, sym_index: u32 }) = .{}, |
| 128 | functions: std.AutoArrayHashMapUnmanaged( |
| 129 | struct { file: File.Index, index: u32 }, |
| 130 | struct { func: std.wasm.Func, sym_index: Symbol.Index }, |
| 131 | ) = .{}, |
| 120 | 132 | /// Output global section |
| 121 | 133 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, |
| 122 | 134 | /// Memory section |
| ... | ... | @@ -143,7 +155,7 @@ entry: ?u32 = null, |
| 143 | 155 | function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 144 | 156 | |
| 145 | 157 | /// All object files and their data which are linked into the final binary |
| 146 | | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 158 | objects: std.ArrayListUnmanaged(File.Index) = .{}, |
| 147 | 159 | /// All archive files that are lazy loaded. |
| 148 | 160 | /// e.g. when an undefined symbol references a symbol from the archive. |
| 149 | 161 | archives: std.ArrayListUnmanaged(Archive) = .{}, |
| ... | ... | @@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{}, |
| 165 | 177 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 166 | 178 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. |
| 167 | 179 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{}, |
| 168 | | /// Maps a symbol's location to its export name, which may differ from the decl's name |
| 169 | | /// which does the exporting. |
| 170 | | /// Note: The value represents the offset into the string table, rather than the actual string. |
| 171 | | export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 172 | | |
| 173 | | /// Represents the symbol index of the error name table |
| 174 | | /// When this is `null`, no code references an error using runtime `@errorName`. |
| 175 | | /// During initializion, a symbol with corresponding atom will be created that is |
| 176 | | /// used to perform relocations to the pointer of this table. |
| 177 | | /// The actual table is populated during `flush`. |
| 178 | | error_table_symbol: ?u32 = null, |
| 179 | | |
| 180 | | // Debug section atoms. These are only set when the current compilation |
| 181 | | // unit contains Zig code. The lifetime of these atoms are extended |
| 182 | | // until the end of the compiler's lifetime. Meaning they're not freed |
| 183 | | // during `flush()` in incremental-mode. |
| 184 | | debug_info_atom: ?Atom.Index = null, |
| 185 | | debug_line_atom: ?Atom.Index = null, |
| 186 | | debug_loc_atom: ?Atom.Index = null, |
| 187 | | debug_ranges_atom: ?Atom.Index = null, |
| 188 | | debug_abbrev_atom: ?Atom.Index = null, |
| 189 | | debug_str_atom: ?Atom.Index = null, |
| 190 | | debug_pubnames_atom: ?Atom.Index = null, |
| 191 | | debug_pubtypes_atom: ?Atom.Index = null, |
| 192 | | |
| 193 | | /// List of atom indexes of functions that are generated by the backend, |
| 194 | | /// rather than by the linker. |
| 195 | | synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 196 | | |
| 197 | | /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. |
| 198 | | anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{}, |
| 199 | | |
| 200 | | import_table: bool, |
| 201 | | export_table: bool, |
| 202 | 180 | |
| 203 | 181 | pub const Alignment = types.Alignment; |
| 204 | 182 | |
| ... | ... | @@ -226,39 +204,33 @@ pub const Segment = struct { |
| 226 | 204 | } |
| 227 | 205 | }; |
| 228 | 206 | |
| 229 | | pub const Export = struct { |
| 230 | | sym_index: ?u32 = null, |
| 231 | | }; |
| 232 | | |
| 233 | 207 | pub const SymbolLoc = struct { |
| 234 | 208 | /// The index of the symbol within the specified file |
| 235 | | index: u32, |
| 209 | index: Symbol.Index, |
| 236 | 210 | /// The index of the object file where the symbol resides. |
| 237 | | /// When this is `null` the symbol comes from a non-object file. |
| 238 | | file: ?u16, |
| 211 | file: File.Index, |
| 239 | 212 | |
| 240 | 213 | /// From a given location, returns the corresponding symbol in the wasm binary |
| 241 | | pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol { |
| 242 | | if (wasm_bin.discarded.get(loc)) |new_loc| { |
| 243 | | return new_loc.getSymbol(wasm_bin); |
| 214 | pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol { |
| 215 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 216 | return new_loc.getSymbol(wasm_file); |
| 244 | 217 | } |
| 245 | | if (loc.file) |object_index| { |
| 246 | | const object = wasm_bin.objects.items[object_index]; |
| 247 | | return &object.symtable[loc.index]; |
| 218 | if (wasm_file.file(loc.file)) |obj_file| { |
| 219 | return obj_file.symbol(loc.index); |
| 248 | 220 | } |
| 249 | | return &wasm_bin.symbols.items[loc.index]; |
| 221 | return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)]; |
| 250 | 222 | } |
| 251 | 223 | |
| 252 | 224 | /// From a given location, returns the name of the symbol. |
| 253 | | pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 { |
| 254 | | if (wasm_bin.discarded.get(loc)) |new_loc| { |
| 255 | | return new_loc.getName(wasm_bin); |
| 225 | pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 { |
| 226 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 227 | return new_loc.getName(wasm_file); |
| 256 | 228 | } |
| 257 | | if (loc.file) |object_index| { |
| 258 | | const object = wasm_bin.objects.items[object_index]; |
| 259 | | return object.string_table.get(object.symtable[loc.index].name); |
| 229 | if (wasm_file.file(loc.file)) |obj_file| { |
| 230 | return obj_file.symbolName(loc.index); |
| 260 | 231 | } |
| 261 | | return wasm_bin.string_table.get(wasm_bin.symbols.items[loc.index].name); |
| 232 | const sym = wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)]; |
| 233 | return wasm_file.string_table.get(sym.name); |
| 262 | 234 | } |
| 263 | 235 | |
| 264 | 236 | /// From a given symbol location, returns the final location. |
| ... | ... | @@ -266,9 +238,9 @@ pub const SymbolLoc = struct { |
| 266 | 238 | /// in a different file, this will return said location. |
| 267 | 239 | /// If the symbol wasn't replaced by another, this will return |
| 268 | 240 | /// the given location itwasm. |
| 269 | | pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { |
| 270 | | if (wasm_bin.discarded.get(loc)) |new_loc| { |
| 271 | | return new_loc.finalLoc(wasm_bin); |
| 241 | pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc { |
| 242 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 243 | return new_loc.finalLoc(wasm_file); |
| 272 | 244 | } |
| 273 | 245 | return loc; |
| 274 | 246 | } |
| ... | ... | @@ -280,9 +252,9 @@ pub const InitFuncLoc = struct { |
| 280 | 252 | /// object file index in the list of objects. |
| 281 | 253 | /// Unlike `SymbolLoc` this cannot be `null` as we never define |
| 282 | 254 | /// our own ctors. |
| 283 | | file: u16, |
| 255 | file: File.Index, |
| 284 | 256 | /// Symbol index within the corresponding object file. |
| 285 | | index: u32, |
| 257 | index: Symbol.Index, |
| 286 | 258 | /// The priority in which the constructor must be called. |
| 287 | 259 | priority: u32, |
| 288 | 260 | |
| ... | ... | @@ -459,7 +431,7 @@ pub fn createEmpty( |
| 459 | 431 | // can be passed to LLD. |
| 460 | 432 | const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; |
| 461 | 433 | |
| 462 | | const file = try emit.directory.handle.createFile(sub_path, .{ |
| 434 | wasm.base.file = try emit.directory.handle.createFile(sub_path, .{ |
| 463 | 435 | .truncate = true, |
| 464 | 436 | .read = true, |
| 465 | 437 | .mode = if (fs.has_executable_bit) |
| ... | ... | @@ -470,7 +442,6 @@ pub fn createEmpty( |
| 470 | 442 | else |
| 471 | 443 | 0, |
| 472 | 444 | }); |
| 473 | | wasm.base.file = file; |
| 474 | 445 | wasm.name = sub_path; |
| 475 | 446 | |
| 476 | 447 | // create stack pointer symbol |
| ... | ... | @@ -550,6 +521,7 @@ pub fn createEmpty( |
| 550 | 521 | const symbol = loc.getSymbol(wasm); |
| 551 | 522 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 552 | 523 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 524 | symbol.mark(); |
| 553 | 525 | try wasm.wasm_globals.append(gpa, .{ |
| 554 | 526 | .global_type = .{ .valtype = .i32, .mutable = true }, |
| 555 | 527 | .init = .{ .i32_const = undefined }, |
| ... | ... | @@ -560,6 +532,7 @@ pub fn createEmpty( |
| 560 | 532 | const symbol = loc.getSymbol(wasm); |
| 561 | 533 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 562 | 534 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 535 | symbol.mark(); |
| 563 | 536 | try wasm.wasm_globals.append(gpa, .{ |
| 564 | 537 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| 565 | 538 | .init = .{ .i32_const = undefined }, |
| ... | ... | @@ -570,6 +543,7 @@ pub fn createEmpty( |
| 570 | 543 | const symbol = loc.getSymbol(wasm); |
| 571 | 544 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 572 | 545 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 546 | symbol.mark(); |
| 573 | 547 | try wasm.wasm_globals.append(gpa, .{ |
| 574 | 548 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| 575 | 549 | .init = .{ .i32_const = undefined }, |
| ... | ... | @@ -582,9 +556,64 @@ pub fn createEmpty( |
| 582 | 556 | } |
| 583 | 557 | } |
| 584 | 558 | |
| 559 | if (comp.module) |zcu| { |
| 560 | if (!use_llvm) { |
| 561 | const index: File.Index = @enumFromInt(wasm.files.len); |
| 562 | var zig_object: ZigObject = .{ |
| 563 | .index = index, |
| 564 | .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}), |
| 565 | .stack_pointer_sym = .null, |
| 566 | }; |
| 567 | try zig_object.init(wasm); |
| 568 | try wasm.files.append(gpa, .{ .zig_object = zig_object }); |
| 569 | wasm.zig_object_index = index; |
| 570 | } |
| 571 | } |
| 572 | |
| 585 | 573 | return wasm; |
| 586 | 574 | } |
| 587 | 575 | |
| 576 | pub fn file(wasm: *const Wasm, index: File.Index) ?File { |
| 577 | if (index == .null) return null; |
| 578 | const tag = wasm.files.items(.tags)[@intFromEnum(index)]; |
| 579 | return switch (tag) { |
| 580 | .zig_object => .{ .zig_object = &wasm.files.items(.data)[@intFromEnum(index)].zig_object }, |
| 581 | .object => .{ .object = &wasm.files.items(.data)[@intFromEnum(index)].object }, |
| 582 | }; |
| 583 | } |
| 584 | |
| 585 | pub fn zigObjectPtr(wasm: *Wasm) ?*ZigObject { |
| 586 | if (wasm.zig_object_index == .null) return null; |
| 587 | return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object; |
| 588 | } |
| 589 | |
| 590 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 591 | var index: u32 = 0; |
| 592 | while (index < wasm.func_types.items.len) : (index += 1) { |
| 593 | if (wasm.func_types.items[index].eql(func_type)) return index; |
| 594 | } |
| 595 | return null; |
| 596 | } |
| 597 | |
| 598 | /// Either creates a new import, or updates one if existing. |
| 599 | /// When `type_index` is non-null, we assume an external function. |
| 600 | /// In all other cases, a data-symbol will be created instead. |
| 601 | pub fn addOrUpdateImport( |
| 602 | wasm: *Wasm, |
| 603 | /// Name of the import |
| 604 | name: []const u8, |
| 605 | /// Symbol index that is external |
| 606 | symbol_index: Symbol.Index, |
| 607 | /// Optional library name (i.e. `extern "c" fn foo() void` |
| 608 | lib_name: ?[:0]const u8, |
| 609 | /// The index of the type that represents the function signature |
| 610 | /// when the extern is a function. When this is null, a data-symbol |
| 611 | /// is asserted instead. |
| 612 | type_index: ?u32, |
| 613 | ) !void { |
| 614 | return wasm.zigObjectPtr().?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index); |
| 615 | } |
| 616 | |
| 588 | 617 | /// For a given name, creates a new global synthetic symbol. |
| 589 | 618 | /// Leaves index undefined and the default flags (0). |
| 590 | 619 | fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc { |
| ... | ... | @@ -594,10 +623,10 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 594 | 623 | } |
| 595 | 624 | |
| 596 | 625 | fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc { |
| 597 | | const sym_index = @as(u32, @intCast(wasm.symbols.items.len)); |
| 598 | | const loc: SymbolLoc = .{ .index = sym_index, .file = null }; |
| 626 | const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len); |
| 627 | const loc: SymbolLoc = .{ .index = sym_index, .file = .null }; |
| 599 | 628 | const gpa = wasm.base.comp.gpa; |
| 600 | | try wasm.symbols.append(gpa, .{ |
| 629 | try wasm.synthetic_symbols.append(gpa, .{ |
| 601 | 630 | .name = name_offset, |
| 602 | 631 | .flags = 0, |
| 603 | 632 | .tag = tag, |
| ... | ... | @@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) ! |
| 609 | 638 | return loc; |
| 610 | 639 | } |
| 611 | 640 | |
| 612 | | /// Initializes symbols and atoms for the debug sections |
| 613 | | /// Initialization is only done when compiling Zig code. |
| 614 | | /// When Zig is invoked as a linker instead, the atoms |
| 615 | | /// and symbols come from the object files instead. |
| 616 | | pub fn initDebugSections(wasm: *Wasm) !void { |
| 617 | | if (wasm.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections |
| 618 | | assert(wasm.debug_info_index == null); |
| 619 | | // this will create an Atom and set the index for us. |
| 620 | | wasm.debug_info_atom = try wasm.createDebugSectionForIndex(&wasm.debug_info_index, ".debug_info"); |
| 621 | | wasm.debug_line_atom = try wasm.createDebugSectionForIndex(&wasm.debug_line_index, ".debug_line"); |
| 622 | | wasm.debug_loc_atom = try wasm.createDebugSectionForIndex(&wasm.debug_loc_index, ".debug_loc"); |
| 623 | | wasm.debug_abbrev_atom = try wasm.createDebugSectionForIndex(&wasm.debug_abbrev_index, ".debug_abbrev"); |
| 624 | | wasm.debug_ranges_atom = try wasm.createDebugSectionForIndex(&wasm.debug_ranges_index, ".debug_ranges"); |
| 625 | | wasm.debug_str_atom = try wasm.createDebugSectionForIndex(&wasm.debug_str_index, ".debug_str"); |
| 626 | | wasm.debug_pubnames_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubnames_index, ".debug_pubnames"); |
| 627 | | wasm.debug_pubtypes_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubtypes_index, ".debug_pubtypes"); |
| 628 | | } |
| 629 | | |
| 630 | 641 | fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { |
| 631 | 642 | for (files) |path| { |
| 632 | 643 | if (try wasm.parseObjectFile(path)) continue; |
| ... | ... | @@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { |
| 639 | 650 | /// file and parsed successfully. Returns false when file is not an object file. |
| 640 | 651 | /// May return an error instead when parsing failed. |
| 641 | 652 | fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { |
| 642 | | const file = try fs.cwd().openFile(path, .{}); |
| 643 | | errdefer file.close(); |
| 653 | const obj_file = try fs.cwd().openFile(path, .{}); |
| 654 | errdefer obj_file.close(); |
| 644 | 655 | |
| 645 | 656 | const gpa = wasm.base.comp.gpa; |
| 646 | | var object = Object.create(gpa, file, path, null) catch |err| switch (err) { |
| 657 | var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) { |
| 647 | 658 | error.InvalidMagicByte, error.NotObjectFile => return false, |
| 648 | | else => |e| return e, |
| 659 | else => |e| { |
| 660 | var err_note = try wasm.addErrorWithNotes(1); |
| 661 | try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)}); |
| 662 | try err_note.addNote(wasm, "while parsing '{s}'", .{path}); |
| 663 | return error.FlushFailure; |
| 664 | }, |
| 649 | 665 | }; |
| 650 | 666 | errdefer object.deinit(gpa); |
| 651 | | try wasm.objects.append(gpa, object); |
| 667 | object.index = @enumFromInt(wasm.files.len); |
| 668 | try wasm.files.append(gpa, .{ .object = object }); |
| 669 | try wasm.objects.append(gpa, object.index); |
| 652 | 670 | return true; |
| 653 | 671 | } |
| 654 | 672 | |
| 655 | | /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`. |
| 656 | | /// When the index was not found, a new `Atom` will be created, and its index will be returned. |
| 657 | | /// The newly created Atom is empty with default fields as specified by `Atom.empty`. |
| 658 | | pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index { |
| 659 | | const gpa = wasm.base.comp.gpa; |
| 660 | | const gop = try wasm.decls.getOrPut(gpa, decl_index); |
| 661 | | if (!gop.found_existing) { |
| 662 | | const atom_index = try wasm.createAtom(); |
| 663 | | gop.value_ptr.* = atom_index; |
| 664 | | const atom = wasm.getAtom(atom_index); |
| 665 | | const symbol = atom.symbolLoc().getSymbol(wasm); |
| 666 | | const mod = wasm.base.comp.module.?; |
| 667 | | const decl = mod.declPtr(decl_index); |
| 668 | | const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); |
| 669 | | symbol.name = try wasm.string_table.put(gpa, full_name); |
| 670 | | } |
| 671 | | return gop.value_ptr.*; |
| 672 | | } |
| 673 | | |
| 674 | 673 | /// Creates a new empty `Atom` and returns its `Atom.Index` |
| 675 | | fn createAtom(wasm: *Wasm) !Atom.Index { |
| 674 | pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, file_index: File.Index) !Atom.Index { |
| 676 | 675 | const gpa = wasm.base.comp.gpa; |
| 677 | | const index: Atom.Index = @intCast(wasm.managed_atoms.items.len); |
| 676 | const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len); |
| 678 | 677 | const atom = try wasm.managed_atoms.addOne(gpa); |
| 679 | | atom.* = Atom.empty; |
| 680 | | atom.sym_index = try wasm.allocateSymbol(); |
| 681 | | try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index); |
| 678 | atom.* = .{ .file = file_index, .sym_index = sym_index }; |
| 679 | try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index); |
| 682 | 680 | |
| 683 | 681 | return index; |
| 684 | 682 | } |
| 685 | 683 | |
| 686 | 684 | pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { |
| 687 | | return wasm.managed_atoms.items[index]; |
| 685 | return wasm.managed_atoms.items[@intFromEnum(index)]; |
| 688 | 686 | } |
| 689 | 687 | |
| 690 | 688 | pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { |
| 691 | | return &wasm.managed_atoms.items[index]; |
| 689 | return &wasm.managed_atoms.items[@intFromEnum(index)]; |
| 692 | 690 | } |
| 693 | 691 | |
| 694 | 692 | /// Parses an archive file and will then parse each object file |
| ... | ... | @@ -702,11 +700,11 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { |
| 702 | 700 | fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 703 | 701 | const gpa = wasm.base.comp.gpa; |
| 704 | 702 | |
| 705 | | const file = try fs.cwd().openFile(path, .{}); |
| 706 | | errdefer file.close(); |
| 703 | const archive_file = try fs.cwd().openFile(path, .{}); |
| 704 | errdefer archive_file.close(); |
| 707 | 705 | |
| 708 | 706 | var archive: Archive = .{ |
| 709 | | .file = file, |
| 707 | .file = archive_file, |
| 710 | 708 | .name = path, |
| 711 | 709 | }; |
| 712 | 710 | archive.parse(gpa) catch |err| switch (err) { |
| ... | ... | @@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 714 | 712 | archive.deinit(gpa); |
| 715 | 713 | return false; |
| 716 | 714 | }, |
| 717 | | else => |e| return e, |
| 715 | else => |e| { |
| 716 | var err_note = try wasm.addErrorWithNotes(1); |
| 717 | try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)}); |
| 718 | try err_note.addNote(wasm, "while parsing archive {s}", .{path}); |
| 719 | return error.FlushFailure; |
| 720 | }, |
| 718 | 721 | }; |
| 719 | 722 | |
| 720 | 723 | if (!force_load) { |
| ... | ... | @@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 736 | 739 | } |
| 737 | 740 | |
| 738 | 741 | for (offsets.keys()) |file_offset| { |
| 739 | | const object = try wasm.objects.addOne(gpa); |
| 740 | | object.* = try archive.parseObject(gpa, file_offset); |
| 742 | var object = archive.parseObject(wasm, file_offset) catch |e| { |
| 743 | var err_note = try wasm.addErrorWithNotes(1); |
| 744 | try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)}); |
| 745 | try err_note.addNote(wasm, "while parsing object in archive {s}", .{path}); |
| 746 | return error.FlushFailure; |
| 747 | }; |
| 748 | object.index = @enumFromInt(wasm.files.len); |
| 749 | try wasm.files.append(gpa, .{ .object = object }); |
| 750 | try wasm.objects.append(gpa, object.index); |
| 741 | 751 | } |
| 742 | 752 | |
| 743 | 753 | return true; |
| ... | ... | @@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool { |
| 752 | 762 | return false; |
| 753 | 763 | } |
| 754 | 764 | |
| 755 | | fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 765 | fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { |
| 756 | 766 | const gpa = wasm.base.comp.gpa; |
| 757 | | const object: Object = wasm.objects.items[object_index]; |
| 758 | | log.debug("Resolving symbols in object: '{s}'", .{object.name}); |
| 759 | | |
| 760 | | for (object.symtable, 0..) |symbol, i| { |
| 761 | | const sym_index = @as(u32, @intCast(i)); |
| 762 | | const location: SymbolLoc = .{ |
| 763 | | .file = object_index, |
| 764 | | .index = sym_index, |
| 765 | | }; |
| 766 | | const sym_name = object.string_table.get(symbol.name); |
| 767 | const obj_file = wasm.file(file_index).?; |
| 768 | log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()}); |
| 769 | |
| 770 | for (obj_file.symbols(), 0..) |symbol, i| { |
| 771 | const sym_index: Symbol.Index = @enumFromInt(i); |
| 772 | const location: SymbolLoc = .{ .file = file_index, .index = sym_index }; |
| 773 | const sym_name = obj_file.string(symbol.name); |
| 767 | 774 | if (mem.eql(u8, sym_name, "__indirect_function_table")) { |
| 768 | 775 | continue; |
| 769 | 776 | } |
| ... | ... | @@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 771 | 778 | |
| 772 | 779 | if (symbol.isLocal()) { |
| 773 | 780 | if (symbol.isUndefined()) { |
| 774 | | log.err("Local symbols are not allowed to reference imports", .{}); |
| 775 | | log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name }); |
| 776 | | return error.UndefinedLocal; |
| 781 | var err = try wasm.addErrorWithNotes(1); |
| 782 | try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{}); |
| 783 | try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() }); |
| 777 | 784 | } |
| 778 | 785 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); |
| 779 | 786 | continue; |
| ... | ... | @@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 792 | 799 | |
| 793 | 800 | const existing_loc = maybe_existing.value_ptr.*; |
| 794 | 801 | const existing_sym: *Symbol = existing_loc.getSymbol(wasm); |
| 802 | const existing_file = wasm.file(existing_loc.file); |
| 795 | 803 | |
| 796 | | const existing_file_path = if (existing_loc.file) |file| blk: { |
| 797 | | break :blk wasm.objects.items[file].name; |
| 798 | | } else wasm.name; |
| 804 | const existing_file_path = if (existing_file) |existing_obj_file| |
| 805 | existing_obj_file.path() |
| 806 | else |
| 807 | wasm.name; |
| 799 | 808 | |
| 800 | 809 | if (!existing_sym.isUndefined()) outer: { |
| 801 | 810 | if (!symbol.isUndefined()) inner: { |
| ... | ... | @@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 806 | 815 | break :outer; // existing is weak, while new one isn't. Replace it. |
| 807 | 816 | } |
| 808 | 817 | // both are defined and weak, we have a symbol collision. |
| 809 | | log.err("symbol '{s}' defined multiple times", .{sym_name}); |
| 810 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 811 | | log.err(" next definition in '{s}'", .{object.name}); |
| 812 | | return error.SymbolCollision; |
| 818 | var err = try wasm.addErrorWithNotes(2); |
| 819 | try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name}); |
| 820 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 821 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 813 | 822 | } |
| 814 | 823 | |
| 815 | 824 | try wasm.discarded.put(gpa, location, existing_loc); |
| ... | ... | @@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 817 | 826 | } |
| 818 | 827 | |
| 819 | 828 | if (symbol.tag != existing_sym.tag) { |
| 820 | | log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) }); |
| 821 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 822 | | log.err(" next definition in '{s}'", .{object.name}); |
| 823 | | return error.SymbolMismatchingType; |
| 829 | var err = try wasm.addErrorWithNotes(2); |
| 830 | try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) }); |
| 831 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 832 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 824 | 833 | } |
| 825 | 834 | |
| 826 | 835 | if (existing_sym.isUndefined() and symbol.isUndefined()) { |
| 827 | 836 | // only verify module/import name for function symbols |
| 828 | 837 | if (symbol.tag == .function) { |
| 829 | | const existing_name = if (existing_loc.file) |file_index| blk: { |
| 830 | | const obj = wasm.objects.items[file_index]; |
| 831 | | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; |
| 832 | | break :blk obj.string_table.get(name_index); |
| 838 | const existing_name = if (existing_file) |existing_obj| blk: { |
| 839 | const imp = existing_obj.import(existing_loc.index); |
| 840 | break :blk existing_obj.string(imp.module_name); |
| 833 | 841 | } else blk: { |
| 834 | 842 | const name_index = wasm.imports.get(existing_loc).?.module_name; |
| 835 | 843 | break :blk wasm.string_table.get(name_index); |
| 836 | 844 | }; |
| 837 | 845 | |
| 838 | | const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; |
| 839 | | const module_name = object.string_table.get(module_index); |
| 846 | const imp = obj_file.import(sym_index); |
| 847 | const module_name = obj_file.string(imp.module_name); |
| 840 | 848 | if (!mem.eql(u8, existing_name, module_name)) { |
| 841 | | log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ |
| 849 | var err = try wasm.addErrorWithNotes(2); |
| 850 | try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ |
| 842 | 851 | sym_name, |
| 843 | 852 | existing_name, |
| 844 | 853 | module_name, |
| 845 | 854 | }); |
| 846 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 847 | | log.err(" next definition in '{s}'", .{object.name}); |
| 848 | | return error.ModuleNameMismatch; |
| 855 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 856 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 849 | 857 | } |
| 850 | 858 | } |
| 851 | 859 | |
| ... | ... | @@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 858 | 866 | const existing_ty = wasm.getGlobalType(existing_loc); |
| 859 | 867 | const new_ty = wasm.getGlobalType(location); |
| 860 | 868 | if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) { |
| 861 | | log.err("symbol '{s}' mismatching global types", .{sym_name}); |
| 862 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 863 | | log.err(" next definition in '{s}'", .{object.name}); |
| 864 | | return error.GlobalTypeMismatch; |
| 869 | var err = try wasm.addErrorWithNotes(2); |
| 870 | try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name}); |
| 871 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 872 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 865 | 873 | } |
| 866 | 874 | } |
| 867 | 875 | |
| ... | ... | @@ -869,11 +877,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 869 | 877 | const existing_ty = wasm.getFunctionSignature(existing_loc); |
| 870 | 878 | const new_ty = wasm.getFunctionSignature(location); |
| 871 | 879 | if (!existing_ty.eql(new_ty)) { |
| 872 | | log.err("symbol '{s}' mismatching function signatures.", .{sym_name}); |
| 873 | | log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty }); |
| 874 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 875 | | log.err(" next definition in '{s}'", .{object.name}); |
| 876 | | return error.FunctionSignatureMismatch; |
| 880 | var err = try wasm.addErrorWithNotes(3); |
| 881 | try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name}); |
| 882 | try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty }); |
| 883 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 884 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 877 | 885 | } |
| 878 | 886 | } |
| 879 | 887 | |
| ... | ... | @@ -888,7 +896,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 888 | 896 | // simply overwrite with the new symbol |
| 889 | 897 | log.debug("Overwriting symbol '{s}'", .{sym_name}); |
| 890 | 898 | log.debug(" old definition in '{s}'", .{existing_file_path}); |
| 891 | | log.debug(" new definition in '{s}'", .{object.name}); |
| 899 | log.debug(" new definition in '{s}'", .{obj_file.path()}); |
| 892 | 900 | try wasm.discarded.putNoClobber(gpa, existing_loc, location); |
| 893 | 901 | maybe_existing.value_ptr.* = location; |
| 894 | 902 | try wasm.globals.put(gpa, sym_name_index, location); |
| ... | ... | @@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 920 | 928 | // Symbol is found in unparsed object file within current archive. |
| 921 | 929 | // Parse object and and resolve symbols again before we check remaining |
| 922 | 930 | // undefined symbols. |
| 923 | | const object_file_index: u16 = @intCast(wasm.objects.items.len); |
| 924 | | const object = try archive.parseObject(gpa, offset.items[0]); |
| 925 | | try wasm.objects.append(gpa, object); |
| 926 | | try wasm.resolveSymbolsInObject(object_file_index); |
| 931 | var object = archive.parseObject(wasm, offset.items[0]) catch |e| { |
| 932 | var err_note = try wasm.addErrorWithNotes(1); |
| 933 | try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)}); |
| 934 | try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name}); |
| 935 | return error.FlushFailure; |
| 936 | }; |
| 937 | object.index = @enumFromInt(wasm.files.len); |
| 938 | try wasm.files.append(gpa, .{ .object = object }); |
| 939 | try wasm.objects.append(gpa, object.index); |
| 940 | try wasm.resolveSymbolsInObject(object.index); |
| 927 | 941 | |
| 928 | 942 | // continue loop for any remaining undefined symbols that still exist |
| 929 | 943 | // after resolving last object file |
| ... | ... | @@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 953 | 967 | if (!wasm.hasPassiveInitializationSegments()) { |
| 954 | 968 | return; |
| 955 | 969 | } |
| 970 | const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function); |
| 971 | sym_loc.getSymbol(wasm).mark(); |
| 956 | 972 | |
| 957 | 973 | const flag_address: u32 = if (shared_memory) address: { |
| 958 | 974 | // when we have passive initialization segments and shared memory |
| ... | ... | @@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { |
| 1115 | 1131 | return; |
| 1116 | 1132 | } |
| 1117 | 1133 | |
| 1118 | | // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs"); |
| 1134 | const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs", .function); |
| 1135 | loc.getSymbol(wasm).mark(); |
| 1119 | 1136 | var function_body = std.ArrayList(u8).init(gpa); |
| 1120 | 1137 | defer function_body.deinit(); |
| 1121 | 1138 | const writer = function_body.writer(); |
| ... | ... | @@ -1185,9 +1202,10 @@ fn validateFeatures( |
| 1185 | 1202 | |
| 1186 | 1203 | // extract all the used, disallowed and required features from each |
| 1187 | 1204 | // linked object file so we can test them. |
| 1188 | | for (wasm.objects.items, 0..) |object, object_index| { |
| 1205 | for (wasm.objects.items) |file_index| { |
| 1206 | const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object; |
| 1189 | 1207 | for (object.features) |feature| { |
| 1190 | | const value = @as(u16, @intCast(object_index)) << 1 | @as(u1, 1); |
| 1208 | const value = @as(u16, @intFromEnum(file_index)) << 1 | @as(u1, 1); |
| 1191 | 1209 | switch (feature.prefix) { |
| 1192 | 1210 | .used => { |
| 1193 | 1211 | used[@intFromEnum(feature.tag)] = value; |
| ... | ... | @@ -1218,29 +1236,30 @@ fn validateFeatures( |
| 1218 | 1236 | allowed[used_index] = is_enabled; |
| 1219 | 1237 | emit_features_count.* += @intFromBool(is_enabled); |
| 1220 | 1238 | } else if (is_enabled and !allowed[used_index]) { |
| 1221 | | log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))}); |
| 1222 | | log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name}); |
| 1239 | var err = try wasm.addErrorWithNotes(1); |
| 1240 | try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))}); |
| 1241 | try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path}); |
| 1223 | 1242 | valid_feature_set = false; |
| 1224 | 1243 | } |
| 1225 | 1244 | } |
| 1226 | 1245 | |
| 1227 | 1246 | if (!valid_feature_set) { |
| 1228 | | return error.InvalidFeatureSet; |
| 1247 | return error.FlushFailure; |
| 1229 | 1248 | } |
| 1230 | 1249 | |
| 1231 | 1250 | if (shared_memory) { |
| 1232 | 1251 | const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; |
| 1233 | 1252 | if (@as(u1, @truncate(disallowed_feature)) != 0) { |
| 1234 | | log.err( |
| 1253 | try wasm.addErrorWithoutNotes( |
| 1235 | 1254 | "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", |
| 1236 | | .{wasm.objects.items[disallowed_feature >> 1].name}, |
| 1255 | .{wasm.files.items(.data)[disallowed_feature >> 1].object.path}, |
| 1237 | 1256 | ); |
| 1238 | 1257 | valid_feature_set = false; |
| 1239 | 1258 | } |
| 1240 | 1259 | |
| 1241 | 1260 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 1242 | 1261 | if (!allowed[@intFromEnum(feature)]) { |
| 1243 | | log.err("feature '{}' is not used but is required for shared-memory", .{feature}); |
| 1262 | try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature}); |
| 1244 | 1263 | } |
| 1245 | 1264 | } |
| 1246 | 1265 | } |
| ... | ... | @@ -1248,21 +1267,23 @@ fn validateFeatures( |
| 1248 | 1267 | if (has_tls) { |
| 1249 | 1268 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 1250 | 1269 | if (!allowed[@intFromEnum(feature)]) { |
| 1251 | | log.err("feature '{}' is not used but is required for thread-local storage", .{feature}); |
| 1270 | try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature}); |
| 1252 | 1271 | } |
| 1253 | 1272 | } |
| 1254 | 1273 | } |
| 1255 | 1274 | // For each linked object, validate the required and disallowed features |
| 1256 | | for (wasm.objects.items) |object| { |
| 1275 | for (wasm.objects.items) |file_index| { |
| 1257 | 1276 | var object_used_features = [_]bool{false} ** known_features_count; |
| 1277 | const object = wasm.files.items(.data)[@intFromEnum(file_index)].object; |
| 1258 | 1278 | for (object.features) |feature| { |
| 1259 | 1279 | if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set. |
| 1260 | 1280 | // from here a feature is always used |
| 1261 | 1281 | const disallowed_feature = disallowed[@intFromEnum(feature.tag)]; |
| 1262 | 1282 | if (@as(u1, @truncate(disallowed_feature)) != 0) { |
| 1263 | | log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag}); |
| 1264 | | log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name}); |
| 1265 | | log.err(" used in '{s}'", .{object.name}); |
| 1283 | var err = try wasm.addErrorWithNotes(2); |
| 1284 | try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag}); |
| 1285 | try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path}); |
| 1286 | try err.addNote(wasm, "used in '{s}'", .{object.path}); |
| 1266 | 1287 | valid_feature_set = false; |
| 1267 | 1288 | } |
| 1268 | 1289 | |
| ... | ... | @@ -1273,16 +1294,17 @@ fn validateFeatures( |
| 1273 | 1294 | for (required, 0..) |required_feature, feature_index| { |
| 1274 | 1295 | const is_required = @as(u1, @truncate(required_feature)) != 0; |
| 1275 | 1296 | if (is_required and !object_used_features[feature_index]) { |
| 1276 | | log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))}); |
| 1277 | | log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name}); |
| 1278 | | log.err(" missing in '{s}'", .{object.name}); |
| 1297 | var err = try wasm.addErrorWithNotes(2); |
| 1298 | try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))}); |
| 1299 | try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path}); |
| 1300 | try err.addNote(wasm, "missing in '{s}'", .{object.path}); |
| 1279 | 1301 | valid_feature_set = false; |
| 1280 | 1302 | } |
| 1281 | 1303 | } |
| 1282 | 1304 | } |
| 1283 | 1305 | |
| 1284 | 1306 | if (!valid_feature_set) { |
| 1285 | | return error.InvalidFeatureSet; |
| 1307 | return error.FlushFailure; |
| 1286 | 1308 | } |
| 1287 | 1309 | |
| 1288 | 1310 | to_emit.* = allowed; |
| ... | ... | @@ -1329,13 +1351,6 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 1329 | 1351 | } |
| 1330 | 1352 | } |
| 1331 | 1353 | } |
| 1332 | | if (wasm.string_table.getOffset("__zig_errors_len")) |name_offset| { |
| 1333 | | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { |
| 1334 | | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); |
| 1335 | | try wasm.discarded.putNoClobber(gpa, kv.value, loc); |
| 1336 | | _ = wasm.resolved_symbols.swapRemove(kv.value); |
| 1337 | | } |
| 1338 | | } |
| 1339 | 1354 | } |
| 1340 | 1355 | |
| 1341 | 1356 | // Tries to find a global symbol by its name. Returns null when not found, |
| ... | ... | @@ -1355,16 +1370,18 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 1355 | 1370 | const symbol = undef.getSymbol(wasm); |
| 1356 | 1371 | if (symbol.tag == .data) { |
| 1357 | 1372 | found_undefined_symbols = true; |
| 1358 | | const file_name = if (undef.file) |file_index| name: { |
| 1359 | | break :name wasm.objects.items[file_index].name; |
| 1360 | | } else wasm.name; |
| 1373 | const file_name = if (wasm.file(undef.file)) |obj_file| |
| 1374 | obj_file.path() |
| 1375 | else |
| 1376 | wasm.name; |
| 1361 | 1377 | const symbol_name = undef.getName(wasm); |
| 1362 | | log.err("could not resolve undefined symbol '{s}'", .{symbol_name}); |
| 1363 | | log.err(" defined in '{s}'", .{file_name}); |
| 1378 | var err = try wasm.addErrorWithNotes(1); |
| 1379 | try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name}); |
| 1380 | try err.addNote(wasm, "defined in '{s}'", .{file_name}); |
| 1364 | 1381 | } |
| 1365 | 1382 | } |
| 1366 | 1383 | if (found_undefined_symbols) { |
| 1367 | | return error.UndefinedSymbol; |
| 1384 | return error.FlushFailure; |
| 1368 | 1385 | } |
| 1369 | 1386 | } |
| 1370 | 1387 | |
| ... | ... | @@ -1378,54 +1395,28 @@ pub fn deinit(wasm: *Wasm) void { |
| 1378 | 1395 | for (wasm.segment_info.values()) |segment_info| { |
| 1379 | 1396 | gpa.free(segment_info.name); |
| 1380 | 1397 | } |
| 1381 | | for (wasm.objects.items) |*object| { |
| 1382 | | object.deinit(gpa); |
| 1398 | if (wasm.zigObjectPtr()) |zig_obj| { |
| 1399 | zig_obj.deinit(wasm); |
| 1400 | } |
| 1401 | for (wasm.objects.items) |obj_index| { |
| 1402 | wasm.file(obj_index).?.object.deinit(gpa); |
| 1383 | 1403 | } |
| 1384 | 1404 | |
| 1385 | 1405 | for (wasm.archives.items) |*archive| { |
| 1386 | 1406 | archive.deinit(gpa); |
| 1387 | 1407 | } |
| 1388 | 1408 | |
| 1389 | | // For decls and anon decls we free the memory of its atoms. |
| 1390 | | // The memory of atoms parsed from object files is managed by |
| 1391 | | // the object file itself, and therefore we can skip those. |
| 1392 | | { |
| 1393 | | var it = wasm.decls.valueIterator(); |
| 1394 | | while (it.next()) |atom_index_ptr| { |
| 1395 | | const atom = wasm.getAtomPtr(atom_index_ptr.*); |
| 1396 | | for (atom.locals.items) |local_index| { |
| 1397 | | const local_atom = wasm.getAtomPtr(local_index); |
| 1398 | | local_atom.deinit(gpa); |
| 1399 | | } |
| 1400 | | atom.deinit(gpa); |
| 1401 | | } |
| 1402 | | } |
| 1403 | | { |
| 1404 | | for (wasm.anon_decls.values()) |atom_index| { |
| 1405 | | const atom = wasm.getAtomPtr(atom_index); |
| 1406 | | for (atom.locals.items) |local_index| { |
| 1407 | | const local_atom = wasm.getAtomPtr(local_index); |
| 1408 | | local_atom.deinit(gpa); |
| 1409 | | } |
| 1410 | | atom.deinit(gpa); |
| 1411 | | } |
| 1412 | | } |
| 1413 | | for (wasm.synthetic_functions.items) |atom_index| { |
| 1414 | | const atom = wasm.getAtomPtr(atom_index); |
| 1415 | | atom.deinit(gpa); |
| 1409 | if (wasm.findGlobalSymbol("__wasm_init_tls")) |loc| { |
| 1410 | const atom = wasm.symbol_atom.get(loc).?; |
| 1411 | wasm.getAtomPtr(atom).deinit(gpa); |
| 1416 | 1412 | } |
| 1417 | 1413 | |
| 1418 | | wasm.decls.deinit(gpa); |
| 1419 | | wasm.anon_decls.deinit(gpa); |
| 1420 | | wasm.atom_types.deinit(gpa); |
| 1421 | | wasm.symbols.deinit(gpa); |
| 1422 | | wasm.symbols_free_list.deinit(gpa); |
| 1414 | wasm.synthetic_symbols.deinit(gpa); |
| 1423 | 1415 | wasm.globals.deinit(gpa); |
| 1424 | 1416 | wasm.resolved_symbols.deinit(gpa); |
| 1425 | 1417 | wasm.undefs.deinit(gpa); |
| 1426 | 1418 | wasm.discarded.deinit(gpa); |
| 1427 | 1419 | wasm.symbol_atom.deinit(gpa); |
| 1428 | | wasm.export_names.deinit(gpa); |
| 1429 | 1420 | wasm.atoms.deinit(gpa); |
| 1430 | 1421 | wasm.managed_atoms.deinit(gpa); |
| 1431 | 1422 | wasm.segments.deinit(gpa); |
| ... | ... | @@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 1445 | 1436 | wasm.exports.deinit(gpa); |
| 1446 | 1437 | |
| 1447 | 1438 | wasm.string_table.deinit(gpa); |
| 1448 | | wasm.synthetic_functions.deinit(gpa); |
| 1449 | | |
| 1450 | | if (wasm.dwarf) |*dwarf| { |
| 1451 | | dwarf.deinit(); |
| 1452 | | } |
| 1453 | | } |
| 1454 | | |
| 1455 | | /// Allocates a new symbol and returns its index. |
| 1456 | | /// Will re-use slots when a symbol was freed at an earlier stage. |
| 1457 | | pub fn allocateSymbol(wasm: *Wasm) !u32 { |
| 1458 | | const gpa = wasm.base.comp.gpa; |
| 1459 | | |
| 1460 | | try wasm.symbols.ensureUnusedCapacity(gpa, 1); |
| 1461 | | const symbol: Symbol = .{ |
| 1462 | | .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls |
| 1463 | | .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1464 | | .tag = .undefined, // will be set after updateDecl |
| 1465 | | .index = std.math.maxInt(u32), // will be set during atom parsing |
| 1466 | | .virtual_address = std.math.maxInt(u32), // will be set during atom allocation |
| 1467 | | }; |
| 1468 | | if (wasm.symbols_free_list.popOrNull()) |index| { |
| 1469 | | wasm.symbols.items[index] = symbol; |
| 1470 | | return index; |
| 1471 | | } |
| 1472 | | const index = @as(u32, @intCast(wasm.symbols.items.len)); |
| 1473 | | wasm.symbols.appendAssumeCapacity(symbol); |
| 1474 | | return index; |
| 1439 | wasm.files.deinit(gpa); |
| 1475 | 1440 | } |
| 1476 | 1441 | |
| 1477 | 1442 | pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { |
| ... | ... | @@ -1479,64 +1444,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: |
| 1479 | 1444 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1480 | 1445 | } |
| 1481 | 1446 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness); |
| 1482 | | |
| 1483 | | const tracy = trace(@src()); |
| 1484 | | defer tracy.end(); |
| 1485 | | |
| 1486 | | const gpa = wasm.base.comp.gpa; |
| 1487 | | const func = mod.funcInfo(func_index); |
| 1488 | | const decl_index = func.owner_decl; |
| 1489 | | const decl = mod.declPtr(decl_index); |
| 1490 | | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1491 | | const atom = wasm.getAtomPtr(atom_index); |
| 1492 | | atom.clear(); |
| 1493 | | |
| 1494 | | // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null; |
| 1495 | | // defer if (decl_state) |*ds| ds.deinit(); |
| 1496 | | |
| 1497 | | var code_writer = std.ArrayList(u8).init(gpa); |
| 1498 | | defer code_writer.deinit(); |
| 1499 | | // const result = try codegen.generateFunction( |
| 1500 | | // &wasm.base, |
| 1501 | | // decl.srcLoc(mod), |
| 1502 | | // func, |
| 1503 | | // air, |
| 1504 | | // liveness, |
| 1505 | | // &code_writer, |
| 1506 | | // if (decl_state) |*ds| .{ .dwarf = ds } else .none, |
| 1507 | | // ); |
| 1508 | | const result = try codegen.generateFunction( |
| 1509 | | &wasm.base, |
| 1510 | | decl.srcLoc(mod), |
| 1511 | | func_index, |
| 1512 | | air, |
| 1513 | | liveness, |
| 1514 | | &code_writer, |
| 1515 | | .none, |
| 1516 | | ); |
| 1517 | | |
| 1518 | | const code = switch (result) { |
| 1519 | | .ok => code_writer.items, |
| 1520 | | .fail => |em| { |
| 1521 | | func.analysis(&mod.intern_pool).state = .codegen_failure; |
| 1522 | | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 1523 | | return; |
| 1524 | | }, |
| 1525 | | }; |
| 1526 | | |
| 1527 | | // if (wasm.dwarf) |*dwarf| { |
| 1528 | | // try dwarf.commitDeclState( |
| 1529 | | // mod, |
| 1530 | | // decl_index, |
| 1531 | | // // Actual value will be written after relocation. |
| 1532 | | // // For Wasm, this is the offset relative to the code section |
| 1533 | | // // which isn't known until flush(). |
| 1534 | | // 0, |
| 1535 | | // code.len, |
| 1536 | | // &decl_state.?, |
| 1537 | | // ); |
| 1538 | | // } |
| 1539 | | return wasm.finishUpdateDecl(decl_index, code, .function); |
| 1447 | try wasm.zigObjectPtr().?.updateFunc(wasm, mod, func_index, air, liveness); |
| 1540 | 1448 | } |
| 1541 | 1449 | |
| 1542 | 1450 | // Generate code for the Decl, storing it in memory to be later written to |
| ... | ... | @@ -1546,84 +1454,12 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) ! |
| 1546 | 1454 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1547 | 1455 | } |
| 1548 | 1456 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); |
| 1549 | | |
| 1550 | | const tracy = trace(@src()); |
| 1551 | | defer tracy.end(); |
| 1552 | | |
| 1553 | | const decl = mod.declPtr(decl_index); |
| 1554 | | if (decl.val.getFunction(mod)) |_| { |
| 1555 | | return; |
| 1556 | | } else if (decl.val.getExternFunc(mod)) |_| { |
| 1557 | | return; |
| 1558 | | } |
| 1559 | | |
| 1560 | | const gpa = wasm.base.comp.gpa; |
| 1561 | | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1562 | | const atom = wasm.getAtomPtr(atom_index); |
| 1563 | | atom.clear(); |
| 1564 | | |
| 1565 | | if (decl.isExtern(mod)) { |
| 1566 | | const variable = decl.getOwnedVariable(mod).?; |
| 1567 | | const name = mod.intern_pool.stringToSlice(decl.name); |
| 1568 | | const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); |
| 1569 | | return wasm.addOrUpdateImport(name, atom.sym_index, lib_name, null); |
| 1570 | | } |
| 1571 | | const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; |
| 1572 | | |
| 1573 | | var code_writer = std.ArrayList(u8).init(gpa); |
| 1574 | | defer code_writer.deinit(); |
| 1575 | | |
| 1576 | | const res = try codegen.generateSymbol( |
| 1577 | | &wasm.base, |
| 1578 | | decl.srcLoc(mod), |
| 1579 | | .{ .ty = decl.ty, .val = val }, |
| 1580 | | &code_writer, |
| 1581 | | .none, |
| 1582 | | .{ .parent_atom_index = atom.sym_index }, |
| 1583 | | ); |
| 1584 | | |
| 1585 | | const code = switch (res) { |
| 1586 | | .ok => code_writer.items, |
| 1587 | | .fail => |em| { |
| 1588 | | decl.analysis = .codegen_failure; |
| 1589 | | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 1590 | | return; |
| 1591 | | }, |
| 1592 | | }; |
| 1593 | | |
| 1594 | | return wasm.finishUpdateDecl(decl_index, code, .data); |
| 1457 | try wasm.zigObjectPtr().?.updateDecl(wasm, mod, decl_index); |
| 1595 | 1458 | } |
| 1596 | 1459 | |
| 1597 | 1460 | pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void { |
| 1598 | 1461 | if (wasm.llvm_object) |_| return; |
| 1599 | | if (wasm.dwarf) |*dw| { |
| 1600 | | const tracy = trace(@src()); |
| 1601 | | defer tracy.end(); |
| 1602 | | |
| 1603 | | const decl = mod.declPtr(decl_index); |
| 1604 | | const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); |
| 1605 | | |
| 1606 | | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); |
| 1607 | | try dw.updateDeclLineNumber(mod, decl_index); |
| 1608 | | } |
| 1609 | | } |
| 1610 | | |
| 1611 | | fn finishUpdateDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex, code: []const u8, symbol_tag: Symbol.Tag) !void { |
| 1612 | | const gpa = wasm.base.comp.gpa; |
| 1613 | | const mod = wasm.base.comp.module.?; |
| 1614 | | const decl = mod.declPtr(decl_index); |
| 1615 | | const atom_index = wasm.decls.get(decl_index).?; |
| 1616 | | const atom = wasm.getAtomPtr(atom_index); |
| 1617 | | const symbol = &wasm.symbols.items[atom.sym_index]; |
| 1618 | | const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); |
| 1619 | | symbol.name = try wasm.string_table.put(gpa, full_name); |
| 1620 | | symbol.tag = symbol_tag; |
| 1621 | | try atom.code.appendSlice(gpa, code); |
| 1622 | | try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {}); |
| 1623 | | |
| 1624 | | atom.size = @intCast(code.len); |
| 1625 | | if (code.len == 0) return; |
| 1626 | | atom.alignment = decl.getAlignment(mod); |
| 1462 | try wasm.zigObjectPtr().?.updateDeclLineNumber(mod, decl_index); |
| 1627 | 1463 | } |
| 1628 | 1464 | |
| 1629 | 1465 | /// From a given symbol location, returns its `wasm.GlobalType`. |
| ... | ... | @@ -1632,13 +1468,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { |
| 1632 | 1468 | const symbol = loc.getSymbol(wasm); |
| 1633 | 1469 | assert(symbol.tag == .global); |
| 1634 | 1470 | const is_undefined = symbol.isUndefined(); |
| 1635 | | if (loc.file) |file_index| { |
| 1636 | | const obj: Object = wasm.objects.items[file_index]; |
| 1471 | if (wasm.file(loc.file)) |obj_file| { |
| 1637 | 1472 | if (is_undefined) { |
| 1638 | | return obj.findImport(.global, symbol.index).kind.global; |
| 1473 | return obj_file.import(loc.index).kind.global; |
| 1639 | 1474 | } |
| 1640 | | const import_global_count = obj.importedCountByKind(.global); |
| 1641 | | return obj.globals[symbol.index - import_global_count].global_type; |
| 1475 | return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type; |
| 1642 | 1476 | } |
| 1643 | 1477 | if (is_undefined) { |
| 1644 | 1478 | return wasm.imports.get(loc).?.kind.global; |
| ... | ... | @@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { |
| 1652 | 1486 | const symbol = loc.getSymbol(wasm); |
| 1653 | 1487 | assert(symbol.tag == .function); |
| 1654 | 1488 | const is_undefined = symbol.isUndefined(); |
| 1655 | | if (loc.file) |file_index| { |
| 1656 | | const obj: Object = wasm.objects.items[file_index]; |
| 1489 | if (wasm.file(loc.file)) |obj_file| { |
| 1657 | 1490 | if (is_undefined) { |
| 1658 | | const ty_index = obj.findImport(.function, symbol.index).kind.function; |
| 1659 | | return obj.func_types[ty_index]; |
| 1491 | const ty_index = obj_file.import(loc.index).kind.function; |
| 1492 | return obj_file.funcTypes()[ty_index]; |
| 1660 | 1493 | } |
| 1661 | | const import_function_count = obj.importedCountByKind(.function); |
| 1662 | | const type_index = obj.functions[symbol.index - import_function_count].type_index; |
| 1663 | | return obj.func_types[type_index]; |
| 1494 | const type_index = obj_file.function(loc.index).type_index; |
| 1495 | return obj_file.funcTypes()[type_index]; |
| 1664 | 1496 | } |
| 1665 | 1497 | if (is_undefined) { |
| 1666 | 1498 | const ty_index = wasm.imports.get(loc).?.kind.function; |
| ... | ... | @@ -1673,118 +1505,16 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { |
| 1673 | 1505 | /// Returns the symbol index of the local |
| 1674 | 1506 | /// The given `decl` is the parent decl whom owns the constant. |
| 1675 | 1507 | pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { |
| 1676 | | const gpa = wasm.base.comp.gpa; |
| 1677 | | const mod = wasm.base.comp.module.?; |
| 1678 | | assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions |
| 1679 | | const decl = mod.declPtr(decl_index); |
| 1680 | | |
| 1681 | | const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1682 | | const parent_atom = wasm.getAtom(parent_atom_index); |
| 1683 | | const local_index = parent_atom.locals.items.len; |
| 1684 | | const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); |
| 1685 | | const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{ |
| 1686 | | fqn, local_index, |
| 1687 | | }); |
| 1688 | | defer gpa.free(name); |
| 1689 | | |
| 1690 | | switch (try wasm.lowerConst(name, tv, decl.srcLoc(mod))) { |
| 1691 | | .ok => |atom_index| { |
| 1692 | | try wasm.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index); |
| 1693 | | return wasm.getAtom(atom_index).getSymbolIndex().?; |
| 1694 | | }, |
| 1695 | | .fail => |em| { |
| 1696 | | decl.analysis = .codegen_failure; |
| 1697 | | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 1698 | | return error.CodegenFail; |
| 1699 | | }, |
| 1700 | | } |
| 1701 | | } |
| 1702 | | |
| 1703 | | const LowerConstResult = union(enum) { |
| 1704 | | ok: Atom.Index, |
| 1705 | | fail: *Module.ErrorMsg, |
| 1706 | | }; |
| 1707 | | |
| 1708 | | fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult { |
| 1709 | | const gpa = wasm.base.comp.gpa; |
| 1710 | | const mod = wasm.base.comp.module.?; |
| 1711 | | |
| 1712 | | // Create and initialize a new local symbol and atom |
| 1713 | | const atom_index = try wasm.createAtom(); |
| 1714 | | var value_bytes = std.ArrayList(u8).init(gpa); |
| 1715 | | defer value_bytes.deinit(); |
| 1716 | | |
| 1717 | | const code = code: { |
| 1718 | | const atom = wasm.getAtomPtr(atom_index); |
| 1719 | | atom.alignment = tv.ty.abiAlignment(mod); |
| 1720 | | wasm.symbols.items[atom.sym_index] = .{ |
| 1721 | | .name = try wasm.string_table.put(gpa, name), |
| 1722 | | .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1723 | | .tag = .data, |
| 1724 | | .index = undefined, |
| 1725 | | .virtual_address = undefined, |
| 1726 | | }; |
| 1727 | | try wasm.resolved_symbols.putNoClobber(gpa, atom.symbolLoc(), {}); |
| 1728 | | |
| 1729 | | const result = try codegen.generateSymbol( |
| 1730 | | &wasm.base, |
| 1731 | | src_loc, |
| 1732 | | tv, |
| 1733 | | &value_bytes, |
| 1734 | | .none, |
| 1735 | | .{ |
| 1736 | | .parent_atom_index = atom.sym_index, |
| 1737 | | .addend = null, |
| 1738 | | }, |
| 1739 | | ); |
| 1740 | | break :code switch (result) { |
| 1741 | | .ok => value_bytes.items, |
| 1742 | | .fail => |em| { |
| 1743 | | return .{ .fail = em }; |
| 1744 | | }, |
| 1745 | | }; |
| 1746 | | }; |
| 1747 | | |
| 1748 | | const atom = wasm.getAtomPtr(atom_index); |
| 1749 | | atom.size = @intCast(code.len); |
| 1750 | | try atom.code.appendSlice(gpa, code); |
| 1751 | | return .{ .ok = atom_index }; |
| 1508 | return wasm.zigObjectPtr().?.lowerUnnamedConst(wasm, tv, decl_index); |
| 1752 | 1509 | } |
| 1753 | 1510 | |
| 1754 | 1511 | /// Returns the symbol index from a symbol of which its flag is set global, |
| 1755 | 1512 | /// such as an exported or imported symbol. |
| 1756 | 1513 | /// If the symbol does not yet exist, creates a new one symbol instead |
| 1757 | 1514 | /// and then returns the index to it. |
| 1758 | | pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u32 { |
| 1515 | pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index { |
| 1759 | 1516 | _ = lib_name; |
| 1760 | | const gpa = wasm.base.comp.gpa; |
| 1761 | | const name_index = try wasm.string_table.put(gpa, name); |
| 1762 | | const gop = try wasm.globals.getOrPut(gpa, name_index); |
| 1763 | | if (gop.found_existing) { |
| 1764 | | return gop.value_ptr.*.index; |
| 1765 | | } |
| 1766 | | |
| 1767 | | var symbol: Symbol = .{ |
| 1768 | | .name = name_index, |
| 1769 | | .flags = 0, |
| 1770 | | .index = undefined, // index to type will be set after merging function symbols |
| 1771 | | .tag = .function, |
| 1772 | | .virtual_address = undefined, |
| 1773 | | }; |
| 1774 | | symbol.setGlobal(true); |
| 1775 | | symbol.setUndefined(true); |
| 1776 | | |
| 1777 | | const sym_index = if (wasm.symbols_free_list.popOrNull()) |index| index else blk: { |
| 1778 | | const index: u32 = @intCast(wasm.symbols.items.len); |
| 1779 | | try wasm.symbols.ensureUnusedCapacity(gpa, 1); |
| 1780 | | wasm.symbols.items.len += 1; |
| 1781 | | break :blk index; |
| 1782 | | }; |
| 1783 | | wasm.symbols.items[sym_index] = symbol; |
| 1784 | | gop.value_ptr.* = .{ .index = sym_index, .file = null }; |
| 1785 | | try wasm.resolved_symbols.put(gpa, gop.value_ptr.*, {}); |
| 1786 | | try wasm.undefs.putNoClobber(gpa, name_index, gop.value_ptr.*); |
| 1787 | | return sym_index; |
| 1517 | return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name); |
| 1788 | 1518 | } |
| 1789 | 1519 | |
| 1790 | 1520 | /// For a given decl, find the given symbol index's atom, and create a relocation for the type. |
| ... | ... | @@ -1794,42 +1524,7 @@ pub fn getDeclVAddr( |
| 1794 | 1524 | decl_index: InternPool.DeclIndex, |
| 1795 | 1525 | reloc_info: link.File.RelocInfo, |
| 1796 | 1526 | ) !u64 { |
| 1797 | | const target = wasm.base.comp.root_mod.resolved_target.result; |
| 1798 | | const gpa = wasm.base.comp.gpa; |
| 1799 | | const mod = wasm.base.comp.module.?; |
| 1800 | | const decl = mod.declPtr(decl_index); |
| 1801 | | |
| 1802 | | const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1803 | | const target_symbol_index = wasm.getAtom(target_atom_index).sym_index; |
| 1804 | | |
| 1805 | | assert(reloc_info.parent_atom_index != 0); |
| 1806 | | const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; |
| 1807 | | const atom = wasm.getAtomPtr(atom_index); |
| 1808 | | const is_wasm32 = target.cpu.arch == .wasm32; |
| 1809 | | if (decl.ty.zigTypeTag(mod) == .Fn) { |
| 1810 | | assert(reloc_info.addend == 0); // addend not allowed for function relocations |
| 1811 | | // We found a function pointer, so add it to our table, |
| 1812 | | // as function pointers are not allowed to be stored inside the data section. |
| 1813 | | // They are instead stored in a function table which are called by index. |
| 1814 | | try wasm.addTableFunction(target_symbol_index); |
| 1815 | | try atom.relocs.append(gpa, .{ |
| 1816 | | .index = target_symbol_index, |
| 1817 | | .offset = @intCast(reloc_info.offset), |
| 1818 | | .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, |
| 1819 | | }); |
| 1820 | | } else { |
| 1821 | | try atom.relocs.append(gpa, .{ |
| 1822 | | .index = target_symbol_index, |
| 1823 | | .offset = @intCast(reloc_info.offset), |
| 1824 | | .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, |
| 1825 | | .addend = @intCast(reloc_info.addend), |
| 1826 | | }); |
| 1827 | | } |
| 1828 | | // we do not know the final address at this point, |
| 1829 | | // as atom allocation will determine the address and relocations |
| 1830 | | // will calculate and rewrite this. Therefore, we simply return the symbol index |
| 1831 | | // that was targeted. |
| 1832 | | return target_symbol_index; |
| 1527 | return wasm.zigObjectPtr().?.getDeclVAddr(wasm, decl_index, reloc_info); |
| 1833 | 1528 | } |
| 1834 | 1529 | |
| 1835 | 1530 | pub fn lowerAnonDecl( |
| ... | ... | @@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl( |
| 1838 | 1533 | explicit_alignment: Alignment, |
| 1839 | 1534 | src_loc: Module.SrcLoc, |
| 1840 | 1535 | ) !codegen.Result { |
| 1841 | | const gpa = wasm.base.comp.gpa; |
| 1842 | | const gop = try wasm.anon_decls.getOrPut(gpa, decl_val); |
| 1843 | | if (!gop.found_existing) { |
| 1844 | | const mod = wasm.base.comp.module.?; |
| 1845 | | const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); |
| 1846 | | const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) }; |
| 1847 | | var name_buf: [32]u8 = undefined; |
| 1848 | | const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{ |
| 1849 | | @intFromEnum(decl_val), |
| 1850 | | }) catch unreachable; |
| 1851 | | |
| 1852 | | switch (try wasm.lowerConst(name, tv, src_loc)) { |
| 1853 | | .ok => |atom_index| wasm.anon_decls.values()[gop.index] = atom_index, |
| 1854 | | .fail => |em| return .{ .fail = em }, |
| 1855 | | } |
| 1856 | | } |
| 1857 | | |
| 1858 | | const atom = wasm.getAtomPtr(wasm.anon_decls.values()[gop.index]); |
| 1859 | | atom.alignment = switch (atom.alignment) { |
| 1860 | | .none => explicit_alignment, |
| 1861 | | else => switch (explicit_alignment) { |
| 1862 | | .none => atom.alignment, |
| 1863 | | else => atom.alignment.maxStrict(explicit_alignment), |
| 1864 | | }, |
| 1865 | | }; |
| 1866 | | return .ok; |
| 1536 | return wasm.zigObjectPtr().?.lowerAnonDecl(wasm, decl_val, explicit_alignment, src_loc); |
| 1867 | 1537 | } |
| 1868 | 1538 | |
| 1869 | 1539 | pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { |
| 1870 | | const gpa = wasm.base.comp.gpa; |
| 1871 | | const target = wasm.base.comp.root_mod.resolved_target.result; |
| 1872 | | const atom_index = wasm.anon_decls.get(decl_val).?; |
| 1873 | | const target_symbol_index = wasm.getAtom(atom_index).getSymbolIndex().?; |
| 1874 | | |
| 1875 | | const parent_atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; |
| 1876 | | const parent_atom = wasm.getAtomPtr(parent_atom_index); |
| 1877 | | const is_wasm32 = target.cpu.arch == .wasm32; |
| 1878 | | const mod = wasm.base.comp.module.?; |
| 1879 | | const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); |
| 1880 | | if (ty.zigTypeTag(mod) == .Fn) { |
| 1881 | | assert(reloc_info.addend == 0); // addend not allowed for function relocations |
| 1882 | | // We found a function pointer, so add it to our table, |
| 1883 | | // as function pointers are not allowed to be stored inside the data section. |
| 1884 | | // They are instead stored in a function table which are called by index. |
| 1885 | | try wasm.addTableFunction(target_symbol_index); |
| 1886 | | try parent_atom.relocs.append(gpa, .{ |
| 1887 | | .index = target_symbol_index, |
| 1888 | | .offset = @intCast(reloc_info.offset), |
| 1889 | | .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, |
| 1890 | | }); |
| 1891 | | } else { |
| 1892 | | try parent_atom.relocs.append(gpa, .{ |
| 1893 | | .index = target_symbol_index, |
| 1894 | | .offset = @intCast(reloc_info.offset), |
| 1895 | | .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, |
| 1896 | | .addend = @intCast(reloc_info.addend), |
| 1897 | | }); |
| 1898 | | } |
| 1899 | | |
| 1900 | | // we do not know the final address at this point, |
| 1901 | | // as atom allocation will determine the address and relocations |
| 1902 | | // will calculate and rewrite this. Therefore, we simply return the symbol index |
| 1903 | | // that was targeted. |
| 1904 | | return target_symbol_index; |
| 1540 | return wasm.zigObjectPtr().?.getAnonDeclVAddr(wasm, decl_val, reloc_info); |
| 1905 | 1541 | } |
| 1906 | 1542 | |
| 1907 | 1543 | pub fn deleteDeclExport( |
| ... | ... | @@ -1909,19 +1545,8 @@ pub fn deleteDeclExport( |
| 1909 | 1545 | decl_index: InternPool.DeclIndex, |
| 1910 | 1546 | name: InternPool.NullTerminatedString, |
| 1911 | 1547 | ) void { |
| 1912 | | _ = name; |
| 1913 | 1548 | if (wasm.llvm_object) |_| return; |
| 1914 | | const atom_index = wasm.decls.get(decl_index) orelse return; |
| 1915 | | const sym_index = wasm.getAtom(atom_index).sym_index; |
| 1916 | | const loc: SymbolLoc = .{ .file = null, .index = sym_index }; |
| 1917 | | const symbol = loc.getSymbol(wasm); |
| 1918 | | const symbol_name = wasm.string_table.get(symbol.name); |
| 1919 | | log.debug("Deleting export for decl '{s}'", .{symbol_name}); |
| 1920 | | if (wasm.export_names.fetchRemove(loc)) |kv| { |
| 1921 | | assert(wasm.globals.remove(kv.value)); |
| 1922 | | } else { |
| 1923 | | assert(wasm.globals.remove(symbol.name)); |
| 1924 | | } |
| 1549 | return wasm.zigObjectPtr().?.deleteDeclExport(wasm, decl_index, name); |
| 1925 | 1550 | } |
| 1926 | 1551 | |
| 1927 | 1552 | pub fn updateExports( |
| ... | ... | @@ -1934,159 +1559,12 @@ pub fn updateExports( |
| 1934 | 1559 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1935 | 1560 | } |
| 1936 | 1561 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); |
| 1937 | | |
| 1938 | | const decl_index = switch (exported) { |
| 1939 | | .decl_index => |i| i, |
| 1940 | | .value => |val| { |
| 1941 | | _ = val; |
| 1942 | | @panic("TODO: implement Wasm linker code for exporting a constant value"); |
| 1943 | | }, |
| 1944 | | }; |
| 1945 | | const decl = mod.declPtr(decl_index); |
| 1946 | | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1947 | | const atom = wasm.getAtom(atom_index); |
| 1948 | | const atom_sym = atom.symbolLoc().getSymbol(wasm).*; |
| 1949 | | const gpa = mod.gpa; |
| 1950 | | |
| 1951 | | for (exports) |exp| { |
| 1952 | | if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| { |
| 1953 | | try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( |
| 1954 | | gpa, |
| 1955 | | decl.srcLoc(mod), |
| 1956 | | "Unimplemented: ExportOptions.section '{s}'", |
| 1957 | | .{section}, |
| 1958 | | )); |
| 1959 | | continue; |
| 1960 | | } |
| 1961 | | |
| 1962 | | const exported_decl_index = switch (exp.exported) { |
| 1963 | | .value => { |
| 1964 | | try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( |
| 1965 | | gpa, |
| 1966 | | decl.srcLoc(mod), |
| 1967 | | "Unimplemented: exporting a named constant value", |
| 1968 | | .{}, |
| 1969 | | )); |
| 1970 | | continue; |
| 1971 | | }, |
| 1972 | | .decl_index => |i| i, |
| 1973 | | }; |
| 1974 | | const exported_atom_index = try wasm.getOrCreateAtomForDecl(exported_decl_index); |
| 1975 | | const exported_atom = wasm.getAtom(exported_atom_index); |
| 1976 | | const export_name = try wasm.string_table.put(gpa, mod.intern_pool.stringToSlice(exp.opts.name)); |
| 1977 | | const sym_loc = exported_atom.symbolLoc(); |
| 1978 | | const symbol = sym_loc.getSymbol(wasm); |
| 1979 | | symbol.setGlobal(true); |
| 1980 | | symbol.setUndefined(false); |
| 1981 | | symbol.index = atom_sym.index; |
| 1982 | | symbol.tag = atom_sym.tag; |
| 1983 | | symbol.name = atom_sym.name; |
| 1984 | | |
| 1985 | | switch (exp.opts.linkage) { |
| 1986 | | .Internal => { |
| 1987 | | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 1988 | | symbol.setFlag(.WASM_SYM_BINDING_WEAK); |
| 1989 | | }, |
| 1990 | | .Weak => { |
| 1991 | | symbol.setFlag(.WASM_SYM_BINDING_WEAK); |
| 1992 | | }, |
| 1993 | | .Strong => {}, // symbols are strong by default |
| 1994 | | .LinkOnce => { |
| 1995 | | try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( |
| 1996 | | gpa, |
| 1997 | | decl.srcLoc(mod), |
| 1998 | | "Unimplemented: LinkOnce", |
| 1999 | | .{}, |
| 2000 | | )); |
| 2001 | | continue; |
| 2002 | | }, |
| 2003 | | } |
| 2004 | | |
| 2005 | | if (wasm.globals.get(export_name)) |existing_loc| { |
| 2006 | | if (existing_loc.index == atom.sym_index) continue; |
| 2007 | | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; |
| 2008 | | |
| 2009 | | if (!existing_sym.isUndefined()) blk: { |
| 2010 | | if (symbol.isWeak()) { |
| 2011 | | try wasm.discarded.put(gpa, existing_loc, sym_loc); |
| 2012 | | continue; // to-be-exported symbol is weak, so we keep the existing symbol |
| 2013 | | } |
| 2014 | | |
| 2015 | | // new symbol is not weak while existing is, replace existing symbol |
| 2016 | | if (existing_sym.isWeak()) { |
| 2017 | | break :blk; |
| 2018 | | } |
| 2019 | | // When both the to-be-exported symbol and the already existing symbol |
| 2020 | | // are strong symbols, we have a linker error. |
| 2021 | | // In the other case we replace one with the other. |
| 2022 | | try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create( |
| 2023 | | gpa, |
| 2024 | | decl.srcLoc(mod), |
| 2025 | | \\LinkError: symbol '{}' defined multiple times |
| 2026 | | \\ first definition in '{s}' |
| 2027 | | \\ next definition in '{s}' |
| 2028 | | , |
| 2029 | | .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name }, |
| 2030 | | )); |
| 2031 | | continue; |
| 2032 | | } |
| 2033 | | |
| 2034 | | // in this case the existing symbol must be replaced either because it's weak or undefined. |
| 2035 | | try wasm.discarded.put(gpa, existing_loc, sym_loc); |
| 2036 | | _ = wasm.imports.remove(existing_loc); |
| 2037 | | _ = wasm.undefs.swapRemove(existing_sym.name); |
| 2038 | | } |
| 2039 | | |
| 2040 | | // Ensure the symbol will be exported using the given name |
| 2041 | | if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) { |
| 2042 | | try wasm.export_names.put(gpa, sym_loc, export_name); |
| 2043 | | } |
| 2044 | | |
| 2045 | | try wasm.globals.put( |
| 2046 | | gpa, |
| 2047 | | export_name, |
| 2048 | | sym_loc, |
| 2049 | | ); |
| 2050 | | } |
| 1562 | return wasm.zigObjectPtr().?.updateExports(wasm, mod, exported, exports); |
| 2051 | 1563 | } |
| 2052 | 1564 | |
| 2053 | 1565 | pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { |
| 2054 | 1566 | if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); |
| 2055 | | const gpa = wasm.base.comp.gpa; |
| 2056 | | const mod = wasm.base.comp.module.?; |
| 2057 | | const decl = mod.declPtr(decl_index); |
| 2058 | | const atom_index = wasm.decls.get(decl_index).?; |
| 2059 | | const atom = wasm.getAtomPtr(atom_index); |
| 2060 | | atom.prev = null; |
| 2061 | | wasm.symbols_free_list.append(gpa, atom.sym_index) catch {}; |
| 2062 | | _ = wasm.decls.remove(decl_index); |
| 2063 | | wasm.symbols.items[atom.sym_index].tag = .dead; |
| 2064 | | for (atom.locals.items) |local_atom_index| { |
| 2065 | | const local_atom = wasm.getAtom(local_atom_index); |
| 2066 | | const local_symbol = &wasm.symbols.items[local_atom.sym_index]; |
| 2067 | | local_symbol.tag = .dead; // also for any local symbol |
| 2068 | | wasm.symbols_free_list.append(gpa, local_atom.sym_index) catch {}; |
| 2069 | | assert(wasm.resolved_symbols.swapRemove(local_atom.symbolLoc())); |
| 2070 | | assert(wasm.symbol_atom.remove(local_atom.symbolLoc())); |
| 2071 | | } |
| 2072 | | |
| 2073 | | if (decl.isExtern(mod)) { |
| 2074 | | _ = wasm.imports.remove(atom.symbolLoc()); |
| 2075 | | } |
| 2076 | | _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc()); |
| 2077 | | _ = wasm.symbol_atom.remove(atom.symbolLoc()); |
| 2078 | | |
| 2079 | | // if (wasm.dwarf) |*dwarf| { |
| 2080 | | // dwarf.freeDecl(decl_index); |
| 2081 | | // } |
| 2082 | | |
| 2083 | | } |
| 2084 | | |
| 2085 | | /// Appends a new entry to the indirect function table |
| 2086 | | pub fn addTableFunction(wasm: *Wasm, symbol_index: u32) !void { |
| 2087 | | const gpa = wasm.base.comp.gpa; |
| 2088 | | const index: u32 = @intCast(wasm.function_table.count()); |
| 2089 | | try wasm.function_table.put(gpa, .{ .file = null, .index = symbol_index }, index); |
| 1567 | return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index); |
| 2090 | 1568 | } |
| 2091 | 1569 | |
| 2092 | 1570 | /// Assigns indexes to all indirect functions. |
| ... | ... | @@ -2118,203 +1596,6 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 2118 | 1596 | } |
| 2119 | 1597 | } |
| 2120 | 1598 | |
| 2121 | | /// Either creates a new import, or updates one if existing. |
| 2122 | | /// When `type_index` is non-null, we assume an external function. |
| 2123 | | /// In all other cases, a data-symbol will be created instead. |
| 2124 | | pub fn addOrUpdateImport( |
| 2125 | | wasm: *Wasm, |
| 2126 | | /// Name of the import |
| 2127 | | name: []const u8, |
| 2128 | | /// Symbol index that is external |
| 2129 | | symbol_index: u32, |
| 2130 | | /// Optional library name (i.e. `extern "c" fn foo() void` |
| 2131 | | lib_name: ?[:0]const u8, |
| 2132 | | /// The index of the type that represents the function signature |
| 2133 | | /// when the extern is a function. When this is null, a data-symbol |
| 2134 | | /// is asserted instead. |
| 2135 | | type_index: ?u32, |
| 2136 | | ) !void { |
| 2137 | | const gpa = wasm.base.comp.gpa; |
| 2138 | | assert(symbol_index != 0); |
| 2139 | | // For the import name, we use the decl's name, rather than the fully qualified name |
| 2140 | | // Also mangle the name when the lib name is set and not equal to "C" so imports with the same |
| 2141 | | // name but different module can be resolved correctly. |
| 2142 | | const mangle_name = lib_name != null and |
| 2143 | | !std.mem.eql(u8, lib_name.?, "c"); |
| 2144 | | const full_name = if (mangle_name) full_name: { |
| 2145 | | break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? }); |
| 2146 | | } else name; |
| 2147 | | defer if (mangle_name) gpa.free(full_name); |
| 2148 | | |
| 2149 | | const decl_name_index = try wasm.string_table.put(gpa, full_name); |
| 2150 | | const symbol: *Symbol = &wasm.symbols.items[symbol_index]; |
| 2151 | | symbol.setUndefined(true); |
| 2152 | | symbol.setGlobal(true); |
| 2153 | | symbol.name = decl_name_index; |
| 2154 | | if (mangle_name) { |
| 2155 | | // we specified a specific name for the symbol that does not match the import name |
| 2156 | | symbol.setFlag(.WASM_SYM_EXPLICIT_NAME); |
| 2157 | | } |
| 2158 | | const global_gop = try wasm.globals.getOrPut(gpa, decl_name_index); |
| 2159 | | if (!global_gop.found_existing) { |
| 2160 | | const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; |
| 2161 | | global_gop.value_ptr.* = loc; |
| 2162 | | try wasm.resolved_symbols.put(gpa, loc, {}); |
| 2163 | | try wasm.undefs.putNoClobber(gpa, decl_name_index, loc); |
| 2164 | | } else if (global_gop.value_ptr.*.index != symbol_index) { |
| 2165 | | // We are not updating a symbol, but found an existing global |
| 2166 | | // symbol with the same name. This means we always favor the |
| 2167 | | // existing symbol, regardless whether it's defined or not. |
| 2168 | | // We can also skip storing the import as we will not output |
| 2169 | | // this symbol. |
| 2170 | | return wasm.discarded.put( |
| 2171 | | gpa, |
| 2172 | | .{ .file = null, .index = symbol_index }, |
| 2173 | | global_gop.value_ptr.*, |
| 2174 | | ); |
| 2175 | | } |
| 2176 | | |
| 2177 | | if (type_index) |ty_index| { |
| 2178 | | const gop = try wasm.imports.getOrPut(gpa, .{ .index = symbol_index, .file = null }); |
| 2179 | | const module_name = if (lib_name) |l_name| blk: { |
| 2180 | | break :blk l_name; |
| 2181 | | } else wasm.host_name; |
| 2182 | | if (!gop.found_existing) { |
| 2183 | | gop.value_ptr.* = .{ |
| 2184 | | .module_name = try wasm.string_table.put(gpa, module_name), |
| 2185 | | .name = try wasm.string_table.put(gpa, name), |
| 2186 | | .kind = .{ .function = ty_index }, |
| 2187 | | }; |
| 2188 | | } |
| 2189 | | } else { |
| 2190 | | // non-functions will not be imported from the runtime, but only resolved during link-time |
| 2191 | | symbol.tag = .data; |
| 2192 | | } |
| 2193 | | } |
| 2194 | | |
| 2195 | | /// Kind represents the type of an Atom, which is only |
| 2196 | | /// used to parse a decl into an Atom to define in which section |
| 2197 | | /// or segment it should be placed. |
| 2198 | | const Kind = union(enum) { |
| 2199 | | /// Represents the segment the data symbol should |
| 2200 | | /// be inserted into. |
| 2201 | | /// TODO: Add TLS segments |
| 2202 | | data: enum { |
| 2203 | | read_only, |
| 2204 | | uninitialized, |
| 2205 | | initialized, |
| 2206 | | }, |
| 2207 | | function: void, |
| 2208 | | |
| 2209 | | /// Returns the segment name the data kind represents. |
| 2210 | | /// Asserts `kind` has its active tag set to `data`. |
| 2211 | | fn segmentName(kind: Kind) []const u8 { |
| 2212 | | switch (kind.data) { |
| 2213 | | .read_only => return ".rodata.", |
| 2214 | | .uninitialized => return ".bss.", |
| 2215 | | .initialized => return ".data.", |
| 2216 | | } |
| 2217 | | } |
| 2218 | | }; |
| 2219 | | |
| 2220 | | /// Parses an Atom and inserts its metadata into the corresponding sections. |
| 2221 | | fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { |
| 2222 | | const comp = wasm.base.comp; |
| 2223 | | const gpa = comp.gpa; |
| 2224 | | const shared_memory = comp.config.shared_memory; |
| 2225 | | const import_memory = comp.config.import_memory; |
| 2226 | | const atom = wasm.getAtomPtr(atom_index); |
| 2227 | | const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); |
| 2228 | | const do_garbage_collect = wasm.base.gc_sections; |
| 2229 | | |
| 2230 | | if (symbol.isDead() and do_garbage_collect) { |
| 2231 | | // Prevent unreferenced symbols from being parsed. |
| 2232 | | return; |
| 2233 | | } |
| 2234 | | |
| 2235 | | const final_index: u32 = switch (kind) { |
| 2236 | | .function => result: { |
| 2237 | | const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count); |
| 2238 | | const type_index = wasm.atom_types.get(atom_index).?; |
| 2239 | | try wasm.functions.putNoClobber( |
| 2240 | | gpa, |
| 2241 | | .{ .file = null, .index = index }, |
| 2242 | | .{ .func = .{ .type_index = type_index }, .sym_index = atom.sym_index }, |
| 2243 | | ); |
| 2244 | | symbol.tag = .function; |
| 2245 | | symbol.index = index; |
| 2246 | | |
| 2247 | | if (wasm.code_section_index == null) { |
| 2248 | | wasm.code_section_index = @intCast(wasm.segments.items.len); |
| 2249 | | try wasm.segments.append(gpa, .{ |
| 2250 | | .alignment = atom.alignment, |
| 2251 | | .size = atom.size, |
| 2252 | | .offset = 0, |
| 2253 | | .flags = 0, |
| 2254 | | }); |
| 2255 | | } |
| 2256 | | |
| 2257 | | break :result wasm.code_section_index.?; |
| 2258 | | }, |
| 2259 | | .data => result: { |
| 2260 | | const segment_name = try std.mem.concat(gpa, u8, &.{ |
| 2261 | | kind.segmentName(), |
| 2262 | | wasm.string_table.get(symbol.name), |
| 2263 | | }); |
| 2264 | | errdefer gpa.free(segment_name); |
| 2265 | | const segment_info: types.Segment = .{ |
| 2266 | | .name = segment_name, |
| 2267 | | .alignment = atom.alignment, |
| 2268 | | .flags = 0, |
| 2269 | | }; |
| 2270 | | symbol.tag = .data; |
| 2271 | | |
| 2272 | | // when creating an object file, or importing memory and the data belongs in the .bss segment |
| 2273 | | // we set the entire region of it to zeroes. |
| 2274 | | // We do not have to do this when exporting the memory (the default) because the runtime |
| 2275 | | // will do it for us, and we do not emit the bss segment at all. |
| 2276 | | if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) { |
| 2277 | | @memset(atom.code.items, 0); |
| 2278 | | } |
| 2279 | | |
| 2280 | | const should_merge = wasm.base.comp.config.output_mode != .Obj; |
| 2281 | | const gop = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(should_merge)); |
| 2282 | | if (gop.found_existing) { |
| 2283 | | const index = gop.value_ptr.*; |
| 2284 | | wasm.segments.items[index].size += atom.size; |
| 2285 | | |
| 2286 | | symbol.index = @intCast(wasm.segment_info.getIndex(index).?); |
| 2287 | | // segment info already exists, so free its memory |
| 2288 | | gpa.free(segment_name); |
| 2289 | | break :result index; |
| 2290 | | } else { |
| 2291 | | const index: u32 = @intCast(wasm.segments.items.len); |
| 2292 | | var flags: u32 = 0; |
| 2293 | | if (shared_memory) { |
| 2294 | | flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); |
| 2295 | | } |
| 2296 | | try wasm.segments.append(gpa, .{ |
| 2297 | | .alignment = atom.alignment, |
| 2298 | | .size = 0, |
| 2299 | | .offset = 0, |
| 2300 | | .flags = flags, |
| 2301 | | }); |
| 2302 | | gop.value_ptr.* = index; |
| 2303 | | |
| 2304 | | const info_index: u32 = @intCast(wasm.segment_info.count()); |
| 2305 | | try wasm.segment_info.put(gpa, index, segment_info); |
| 2306 | | symbol.index = info_index; |
| 2307 | | break :result index; |
| 2308 | | } |
| 2309 | | }, |
| 2310 | | }; |
| 2311 | | |
| 2312 | | const segment: *Segment = &wasm.segments.items[final_index]; |
| 2313 | | segment.alignment = segment.alignment.max(atom.alignment); |
| 2314 | | |
| 2315 | | try wasm.appendAtomAtIndex(final_index, atom_index); |
| 2316 | | } |
| 2317 | | |
| 2318 | 1599 | /// From a given index, append the given `Atom` at the back of the linked list. |
| 2319 | 1600 | /// Simply inserts it into the map of atoms when it doesn't exist yet. |
| 2320 | 1601 | pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void { |
| ... | ... | @@ -2328,40 +1609,9 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void |
| 2328 | 1609 | } |
| 2329 | 1610 | } |
| 2330 | 1611 | |
| 2331 | | /// Allocates debug atoms into their respective debug sections |
| 2332 | | /// to merge them with maybe-existing debug atoms from object files. |
| 2333 | | fn allocateDebugAtoms(wasm: *Wasm) !void { |
| 2334 | | if (wasm.dwarf == null) return; |
| 2335 | | |
| 2336 | | const allocAtom = struct { |
| 2337 | | fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void { |
| 2338 | | const index = maybe_index.* orelse idx: { |
| 2339 | | const index = @as(u32, @intCast(bin.segments.items.len)); |
| 2340 | | try bin.appendDummySegment(); |
| 2341 | | maybe_index.* = index; |
| 2342 | | break :idx index; |
| 2343 | | }; |
| 2344 | | const atom = bin.getAtomPtr(atom_index); |
| 2345 | | atom.size = @as(u32, @intCast(atom.code.items.len)); |
| 2346 | | bin.symbols.items[atom.sym_index].index = index; |
| 2347 | | try bin.appendAtomAtIndex(index, atom_index); |
| 2348 | | } |
| 2349 | | }.f; |
| 2350 | | |
| 2351 | | try allocAtom(wasm, &wasm.debug_info_index, wasm.debug_info_atom.?); |
| 2352 | | try allocAtom(wasm, &wasm.debug_line_index, wasm.debug_line_atom.?); |
| 2353 | | try allocAtom(wasm, &wasm.debug_loc_index, wasm.debug_loc_atom.?); |
| 2354 | | try allocAtom(wasm, &wasm.debug_str_index, wasm.debug_str_atom.?); |
| 2355 | | try allocAtom(wasm, &wasm.debug_ranges_index, wasm.debug_ranges_atom.?); |
| 2356 | | try allocAtom(wasm, &wasm.debug_abbrev_index, wasm.debug_abbrev_atom.?); |
| 2357 | | try allocAtom(wasm, &wasm.debug_pubnames_index, wasm.debug_pubnames_atom.?); |
| 2358 | | try allocAtom(wasm, &wasm.debug_pubtypes_index, wasm.debug_pubtypes_atom.?); |
| 2359 | | } |
| 2360 | | |
| 2361 | 1612 | fn allocateAtoms(wasm: *Wasm) !void { |
| 2362 | 1613 | // first sort the data segments |
| 2363 | 1614 | try sortDataSegments(wasm); |
| 2364 | | try allocateDebugAtoms(wasm); |
| 2365 | 1615 | |
| 2366 | 1616 | var it = wasm.atoms.iterator(); |
| 2367 | 1617 | while (it.next()) |entry| { |
| ... | ... | @@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 2379 | 1629 | // Ensure we get the original symbol, so we verify the correct symbol on whether |
| 2380 | 1630 | // it is dead or not and ensure an atom is removed when dead. |
| 2381 | 1631 | // This is required as we may have parsed aliases into atoms. |
| 2382 | | const sym = if (symbol_loc.file) |object_index| sym: { |
| 2383 | | const object = wasm.objects.items[object_index]; |
| 2384 | | break :sym object.symtable[symbol_loc.index]; |
| 2385 | | } else wasm.symbols.items[symbol_loc.index]; |
| 1632 | const sym = if (wasm.file(symbol_loc.file)) |obj_file| |
| 1633 | obj_file.symbol(symbol_loc.index).* |
| 1634 | else |
| 1635 | wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)]; |
| 2386 | 1636 | |
| 2387 | 1637 | // Dead symbols must be unlinked from the linked-list to prevent them |
| 2388 | 1638 | // from being emit into the binary. |
| 2389 | 1639 | if (sym.isDead()) { |
| 2390 | | if (entry.value_ptr.* == atom_index and atom.prev != null) { |
| 1640 | if (entry.value_ptr.* == atom_index and atom.prev != .null) { |
| 2391 | 1641 | // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update |
| 2392 | 1642 | // the entry to point it to the previous atom to ensure we do not start with a dead symbol that |
| 2393 | 1643 | // was removed and therefore do not emit any code at all. |
| 2394 | | entry.value_ptr.* = atom.prev.?; |
| 1644 | entry.value_ptr.* = atom.prev; |
| 2395 | 1645 | } |
| 2396 | | atom_index = atom.prev orelse break; |
| 2397 | | atom.prev = null; |
| 1646 | if (atom.prev == .null) break; |
| 1647 | atom_index = atom.prev; |
| 1648 | atom.prev = .null; |
| 2398 | 1649 | continue; |
| 2399 | 1650 | } |
| 2400 | 1651 | offset = @intCast(atom.alignment.forward(offset)); |
| ... | ... | @@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 2406 | 1657 | atom.size, |
| 2407 | 1658 | }); |
| 2408 | 1659 | offset += atom.size; |
| 2409 | | atom_index = atom.prev orelse break; |
| 1660 | if (atom.prev == .null) break; |
| 1661 | atom_index = atom.prev; |
| 2410 | 1662 | } |
| 2411 | 1663 | segment.size = @intCast(segment.alignment.forward(offset)); |
| 2412 | 1664 | } |
| ... | ... | @@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { |
| 2428 | 1680 | |
| 2429 | 1681 | const atom = wasm.getAtom(atom_index); |
| 2430 | 1682 | const merge_segment = wasm.base.comp.config.output_mode != .Obj; |
| 2431 | | const segment_info = if (atom.file) |object_index| blk: { |
| 2432 | | break :blk wasm.objects.items[object_index].segment_info; |
| 2433 | | } else wasm.segment_info.values(); |
| 1683 | const segment_info = if (atom.file != .null) |
| 1684 | wasm.file(atom.file).?.segmentInfo() |
| 1685 | else |
| 1686 | wasm.segment_info.values(); |
| 2434 | 1687 | const segment_name = segment_info[symbol.index].outputName(merge_segment); |
| 2435 | 1688 | const segment_index = wasm.data_segments.get(segment_name).?; |
| 2436 | 1689 | const segment = wasm.segments.items[segment_index]; |
| ... | ... | @@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 2486 | 1739 | /// contain any parameters. |
| 2487 | 1740 | fn setupInitFunctions(wasm: *Wasm) !void { |
| 2488 | 1741 | const gpa = wasm.base.comp.gpa; |
| 2489 | | for (wasm.objects.items, 0..) |object, file_index| { |
| 1742 | // There's no constructors for Zig so we can simply search through linked object files only. |
| 1743 | for (wasm.objects.items) |file_index| { |
| 1744 | const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object; |
| 2490 | 1745 | try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len); |
| 2491 | 1746 | for (object.init_funcs) |init_func| { |
| 2492 | 1747 | const symbol = object.symtable[init_func.symbol_index]; |
| 2493 | 1748 | const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { |
| 2494 | | const imp: types.Import = object.findImport(.function, symbol.index); |
| 1749 | const imp: types.Import = object.findImport(symbol); |
| 2495 | 1750 | break :ty object.func_types[imp.kind.function]; |
| 2496 | 1751 | } else ty: { |
| 2497 | | const func_index = symbol.index - object.importedCountByKind(.function); |
| 1752 | const func_index = symbol.index - object.imported_functions_count; |
| 2498 | 1753 | const func = object.functions[func_index]; |
| 2499 | 1754 | break :ty object.func_types[func.type_index]; |
| 2500 | 1755 | }; |
| 2501 | 1756 | if (ty.params.len != 0) { |
| 2502 | | log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| 2503 | | return error.InvalidInitFunc; |
| 1757 | try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| 2504 | 1758 | } |
| 2505 | 1759 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); |
| 2506 | 1760 | wasm.init_funcs.appendAssumeCapacity(.{ |
| 2507 | | .index = init_func.symbol_index, |
| 2508 | | .file = @as(u16, @intCast(file_index)), |
| 1761 | .index = @enumFromInt(init_func.symbol_index), |
| 1762 | .file = file_index, |
| 2509 | 1763 | .priority = init_func.priority, |
| 2510 | 1764 | }); |
| 2511 | | try wasm.mark(.{ .index = init_func.symbol_index, .file = @intCast(file_index) }); |
| 1765 | try wasm.mark(.{ .index = @enumFromInt(init_func.symbol_index), .file = file_index }); |
| 2512 | 1766 | } |
| 2513 | 1767 | } |
| 2514 | 1768 | |
| ... | ... | @@ -2521,34 +1775,6 @@ fn setupInitFunctions(wasm: *Wasm) !void { |
| 2521 | 1775 | } |
| 2522 | 1776 | } |
| 2523 | 1777 | |
| 2524 | | /// Generates an atom containing the global error set' size. |
| 2525 | | /// This will only be generated if the symbol exists. |
| 2526 | | fn setupErrorsLen(wasm: *Wasm) !void { |
| 2527 | | const gpa = wasm.base.comp.gpa; |
| 2528 | | const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return; |
| 2529 | | |
| 2530 | | const errors_len = wasm.base.comp.module.?.global_error_set.count(); |
| 2531 | | // overwrite existing atom if it already exists (maybe the error set has increased) |
| 2532 | | // if not, allcoate a new atom. |
| 2533 | | const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: { |
| 2534 | | const atom = wasm.getAtomPtr(index); |
| 2535 | | atom.deinit(gpa); |
| 2536 | | break :blk index; |
| 2537 | | } else new_atom: { |
| 2538 | | const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); |
| 2539 | | try wasm.symbol_atom.put(gpa, loc, atom_index); |
| 2540 | | try wasm.managed_atoms.append(gpa, undefined); |
| 2541 | | break :new_atom atom_index; |
| 2542 | | }; |
| 2543 | | const atom = wasm.getAtomPtr(atom_index); |
| 2544 | | atom.* = Atom.empty; |
| 2545 | | atom.sym_index = loc.index; |
| 2546 | | atom.size = 2; |
| 2547 | | try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little); |
| 2548 | | |
| 2549 | | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 2550 | | } |
| 2551 | | |
| 2552 | 1778 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| 2553 | 1779 | /// Loops over all constructors found in `init_funcs` and calls them |
| 2554 | 1780 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| ... | ... | @@ -2609,8 +1835,7 @@ fn createSyntheticFunction( |
| 2609 | 1835 | function_body: *std.ArrayList(u8), |
| 2610 | 1836 | ) !void { |
| 2611 | 1837 | const gpa = wasm.base.comp.gpa; |
| 2612 | | const loc = wasm.findGlobalSymbol(symbol_name) orelse |
| 2613 | | try wasm.createSyntheticSymbol(symbol_name, .function); |
| 1838 | const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol? |
| 2614 | 1839 | const symbol = loc.getSymbol(wasm); |
| 2615 | 1840 | if (symbol.isDead()) { |
| 2616 | 1841 | return; |
| ... | ... | @@ -2620,32 +1845,21 @@ fn createSyntheticFunction( |
| 2620 | 1845 | const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count())); |
| 2621 | 1846 | try wasm.functions.putNoClobber( |
| 2622 | 1847 | gpa, |
| 2623 | | .{ .file = null, .index = func_index }, |
| 1848 | .{ .file = .null, .index = func_index }, |
| 2624 | 1849 | .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index }, |
| 2625 | 1850 | ); |
| 2626 | 1851 | symbol.index = func_index; |
| 2627 | 1852 | |
| 2628 | 1853 | // create the atom that will be output into the final binary |
| 2629 | | const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); |
| 2630 | | const atom = try wasm.managed_atoms.addOne(gpa); |
| 2631 | | atom.* = .{ |
| 2632 | | .size = @as(u32, @intCast(function_body.items.len)), |
| 2633 | | .offset = 0, |
| 2634 | | .sym_index = loc.index, |
| 2635 | | .file = null, |
| 2636 | | .alignment = .@"1", |
| 2637 | | .prev = null, |
| 2638 | | .code = function_body.moveToUnmanaged(), |
| 2639 | | .original_offset = 0, |
| 2640 | | }; |
| 1854 | const atom_index = try wasm.createAtom(loc.index, .null); |
| 1855 | const atom = wasm.getAtomPtr(atom_index); |
| 1856 | atom.size = @intCast(function_body.items.len); |
| 1857 | atom.code = function_body.moveToUnmanaged(); |
| 2641 | 1858 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); |
| 2642 | | try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index); |
| 2643 | 1859 | } |
| 2644 | 1860 | |
| 2645 | 1861 | /// Unlike `createSyntheticFunction` this function is to be called by |
| 2646 | | /// the codegeneration backend. This will not allocate the created Atom yet, |
| 2647 | | /// but will instead be appended to `synthetic_functions` list and will be |
| 2648 | | /// parsed at the end of code generation. |
| 1862 | /// the codegeneration backend. This will not allocate the created Atom yet. |
| 2649 | 1863 | /// Returns the index of the symbol. |
| 2650 | 1864 | pub fn createFunction( |
| 2651 | 1865 | wasm: *Wasm, |
| ... | ... | @@ -2653,37 +1867,8 @@ pub fn createFunction( |
| 2653 | 1867 | func_ty: std.wasm.Type, |
| 2654 | 1868 | function_body: *std.ArrayList(u8), |
| 2655 | 1869 | relocations: *std.ArrayList(Relocation), |
| 2656 | | ) !u32 { |
| 2657 | | const gpa = wasm.base.comp.gpa; |
| 2658 | | const loc = try wasm.createSyntheticSymbol(symbol_name, .function); |
| 2659 | | |
| 2660 | | const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); |
| 2661 | | const atom = try wasm.managed_atoms.addOne(gpa); |
| 2662 | | atom.* = .{ |
| 2663 | | .size = @intCast(function_body.items.len), |
| 2664 | | .offset = 0, |
| 2665 | | .sym_index = loc.index, |
| 2666 | | .file = null, |
| 2667 | | .alignment = .@"1", |
| 2668 | | .prev = null, |
| 2669 | | .code = function_body.moveToUnmanaged(), |
| 2670 | | .relocs = relocations.moveToUnmanaged(), |
| 2671 | | .original_offset = 0, |
| 2672 | | }; |
| 2673 | | const symbol = loc.getSymbol(wasm); |
| 2674 | | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported |
| 2675 | | |
| 2676 | | const section_index = wasm.code_section_index orelse idx: { |
| 2677 | | const index = @as(u32, @intCast(wasm.segments.items.len)); |
| 2678 | | try wasm.appendDummySegment(); |
| 2679 | | break :idx index; |
| 2680 | | }; |
| 2681 | | try wasm.appendAtomAtIndex(section_index, atom_index); |
| 2682 | | try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index); |
| 2683 | | try wasm.atom_types.put(gpa, atom_index, try wasm.putOrGetFuncType(func_ty)); |
| 2684 | | try wasm.synthetic_functions.append(gpa, atom_index); |
| 2685 | | |
| 2686 | | return loc.index; |
| 1870 | ) !Symbol.Index { |
| 1871 | return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); |
| 2687 | 1872 | } |
| 2688 | 1873 | |
| 2689 | 1874 | /// If required, sets the function index in the `start` section. |
| ... | ... | @@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2700 | 1885 | |
| 2701 | 1886 | if (!shared_memory) return; |
| 2702 | 1887 | |
| 1888 | // ensure function is marked as we must emit it |
| 1889 | wasm.findGlobalSymbol("__wasm_init_tls").?.getSymbol(wasm).mark(); |
| 1890 | |
| 2703 | 1891 | var function_body = std.ArrayList(u8).init(gpa); |
| 2704 | 1892 | defer function_body.deinit(); |
| 2705 | 1893 | const writer = function_body.writer(); |
| ... | ... | @@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2748 | 1936 | if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| { |
| 2749 | 1937 | try writer.writeByte(std.wasm.opcode(.call)); |
| 2750 | 1938 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); |
| 1939 | loc.getSymbol(wasm).mark(); |
| 2751 | 1940 | } |
| 2752 | 1941 | |
| 2753 | 1942 | try writer.writeByte(std.wasm.opcode(.end)); |
| ... | ... | @@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2762 | 1951 | fn setupImports(wasm: *Wasm) !void { |
| 2763 | 1952 | const gpa = wasm.base.comp.gpa; |
| 2764 | 1953 | log.debug("Merging imports", .{}); |
| 2765 | | var discarded_it = wasm.discarded.keyIterator(); |
| 2766 | | while (discarded_it.next()) |discarded| { |
| 2767 | | if (discarded.file == null) { |
| 2768 | | // remove an import if it was resolved |
| 2769 | | if (wasm.imports.remove(discarded.*)) { |
| 2770 | | log.debug("Removed symbol '{s}' as an import", .{ |
| 2771 | | discarded.getName(wasm), |
| 2772 | | }); |
| 2773 | | } |
| 2774 | | } |
| 2775 | | } |
| 2776 | | |
| 2777 | 1954 | for (wasm.resolved_symbols.keys()) |symbol_loc| { |
| 2778 | | const file_index = symbol_loc.file orelse { |
| 2779 | | // imports generated by Zig code are already in the `import` section |
| 1955 | const obj_file = wasm.file(symbol_loc.file) orelse { |
| 1956 | // Synthetic symbols will already exist in the `import` section |
| 2780 | 1957 | continue; |
| 2781 | 1958 | }; |
| 2782 | 1959 | |
| ... | ... | @@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void { |
| 2789 | 1966 | } |
| 2790 | 1967 | |
| 2791 | 1968 | log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)}); |
| 2792 | | const object = wasm.objects.items[file_index]; |
| 2793 | | const import = object.findImport(symbol.tag.externalType(), symbol.index); |
| 1969 | const import = obj_file.import(symbol_loc.index); |
| 2794 | 1970 | |
| 2795 | 1971 | // We copy the import to a new import to ensure the names contain references |
| 2796 | 1972 | // to the internal string table, rather than of the object file. |
| 2797 | 1973 | const new_imp: types.Import = .{ |
| 2798 | | .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)), |
| 2799 | | .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)), |
| 1974 | .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)), |
| 1975 | .name = try wasm.string_table.put(gpa, obj_file.string(import.name)), |
| 2800 | 1976 | .kind = import.kind, |
| 2801 | 1977 | }; |
| 2802 | 1978 | // TODO: De-duplicate imports when they contain the same names and type |
| ... | ... | @@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void { |
| 2847 | 2023 | defer removed_duplicates.deinit(); |
| 2848 | 2024 | |
| 2849 | 2025 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2850 | | if (sym_loc.file == null) { |
| 2851 | | // Zig code-generated symbols are already within the sections and do not |
| 2852 | | // require to be merged |
| 2026 | const obj_file = wasm.file(sym_loc.file) orelse { |
| 2027 | // Synthetic symbols already live in the corresponding sections. |
| 2853 | 2028 | continue; |
| 2854 | | } |
| 2855 | | |
| 2856 | | const object = &wasm.objects.items[sym_loc.file.?]; |
| 2857 | | const symbol = &object.symtable[sym_loc.index]; |
| 2029 | }; |
| 2858 | 2030 | |
| 2859 | | if (symbol.isDead() or |
| 2860 | | symbol.isUndefined() or |
| 2861 | | (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) |
| 2862 | | { |
| 2031 | const symbol = obj_file.symbol(sym_loc.index); |
| 2032 | if (symbol.isDead() or symbol.isUndefined()) { |
| 2863 | 2033 | // Skip undefined symbols as they go in the `import` section |
| 2864 | | // Also skip symbols that do not need to have a section merged. |
| 2865 | 2034 | continue; |
| 2866 | 2035 | } |
| 2867 | 2036 | |
| 2868 | | const offset = object.importedCountByKind(symbol.tag.externalType()); |
| 2869 | | const index = symbol.index - offset; |
| 2870 | 2037 | switch (symbol.tag) { |
| 2871 | 2038 | .function => { |
| 2872 | 2039 | const gop = try wasm.functions.getOrPut( |
| ... | ... | @@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void { |
| 2877 | 2044 | // We found an alias to the same function, discard this symbol in favor of |
| 2878 | 2045 | // the original symbol and point the discard function to it. This ensures |
| 2879 | 2046 | // we only emit a single function, instead of duplicates. |
| 2880 | | symbol.unmark(); |
| 2881 | | try wasm.discarded.putNoClobber( |
| 2882 | | gpa, |
| 2883 | | sym_loc, |
| 2884 | | .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index }, |
| 2885 | | ); |
| 2886 | | try removed_duplicates.append(sym_loc); |
| 2887 | | continue; |
| 2047 | // we favor keeping the global over a local. |
| 2048 | const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index }; |
| 2049 | const original_sym = original_loc.getSymbol(wasm); |
| 2050 | if (original_sym.isLocal() and symbol.isGlobal()) { |
| 2051 | original_sym.unmark(); |
| 2052 | try wasm.discarded.put(gpa, original_loc, sym_loc); |
| 2053 | try removed_duplicates.append(original_loc); |
| 2054 | } else { |
| 2055 | symbol.unmark(); |
| 2056 | try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc); |
| 2057 | try removed_duplicates.append(sym_loc); |
| 2058 | continue; |
| 2059 | } |
| 2888 | 2060 | } |
| 2889 | | gop.value_ptr.* = .{ .func = object.functions[index], .sym_index = sym_loc.index }; |
| 2061 | gop.value_ptr.* = .{ .func = obj_file.function(sym_loc.index), .sym_index = sym_loc.index }; |
| 2890 | 2062 | symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count; |
| 2891 | 2063 | }, |
| 2892 | 2064 | .global => { |
| 2893 | | const original_global = object.globals[index]; |
| 2065 | const index = symbol.index - obj_file.importedFunctions(); |
| 2066 | const original_global = obj_file.globals()[index]; |
| 2894 | 2067 | symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count; |
| 2895 | 2068 | try wasm.wasm_globals.append(gpa, original_global); |
| 2896 | 2069 | }, |
| 2897 | 2070 | .table => { |
| 2898 | | const original_table = object.tables[index]; |
| 2071 | const index = symbol.index - obj_file.importedFunctions(); |
| 2072 | // assert it's a regular relocatable object file as `ZigObject` will never |
| 2073 | // contain a table. |
| 2074 | const original_table = obj_file.object.tables[index]; |
| 2899 | 2075 | symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count; |
| 2900 | 2076 | try wasm.tables.append(gpa, original_table); |
| 2901 | 2077 | }, |
| 2902 | | else => unreachable, |
| 2078 | .dead, .undefined => unreachable, |
| 2079 | else => {}, |
| 2903 | 2080 | } |
| 2904 | 2081 | } |
| 2905 | 2082 | |
| 2906 | 2083 | // For any removed duplicates, remove them from the resolved symbols list |
| 2907 | 2084 | for (removed_duplicates.items) |sym_loc| { |
| 2908 | 2085 | assert(wasm.resolved_symbols.swapRemove(sym_loc)); |
| 2086 | gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)}); |
| 2909 | 2087 | } |
| 2910 | 2088 | |
| 2911 | 2089 | log.debug("Merged ({d}) functions", .{wasm.functions.count()}); |
| ... | ... | @@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void { |
| 2926 | 2104 | defer dirty.deinit(); |
| 2927 | 2105 | |
| 2928 | 2106 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2929 | | if (sym_loc.file == null) { |
| 2107 | const obj_file = wasm.file(sym_loc.file) orelse { |
| 2930 | 2108 | // zig code-generated symbols are already present in final type section |
| 2931 | 2109 | continue; |
| 2932 | | } |
| 2933 | | const object = wasm.objects.items[sym_loc.file.?]; |
| 2934 | | const symbol = object.symtable[sym_loc.index]; |
| 2110 | }; |
| 2111 | |
| 2112 | const symbol = obj_file.symbol(sym_loc.index); |
| 2935 | 2113 | if (symbol.tag != .function or symbol.isDead()) { |
| 2936 | 2114 | // Only functions have types. Only retrieve the type of referenced functions. |
| 2937 | 2115 | continue; |
| ... | ... | @@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void { |
| 2940 | 2118 | if (symbol.isUndefined()) { |
| 2941 | 2119 | log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); |
| 2942 | 2120 | const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue; |
| 2943 | | const original_type = object.func_types[import.kind.function]; |
| 2121 | const original_type = obj_file.funcTypes()[import.kind.function]; |
| 2944 | 2122 | import.kind.function = try wasm.putOrGetFuncType(original_type); |
| 2945 | 2123 | } else if (!dirty.contains(symbol.index)) { |
| 2946 | 2124 | log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)}); |
| 2947 | 2125 | const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; |
| 2948 | | func.type_index = try wasm.putOrGetFuncType(object.func_types[func.type_index]); |
| 2126 | func.type_index = try wasm.putOrGetFuncType(obj_file.funcTypes()[func.type_index]); |
| 2949 | 2127 | dirty.putAssumeCapacityNoClobber(symbol.index, {}); |
| 2950 | 2128 | } |
| 2951 | 2129 | } |
| 2952 | 2130 | log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len}); |
| 2953 | 2131 | } |
| 2954 | 2132 | |
| 2955 | | fn setupExports(wasm: *Wasm) !void { |
| 2956 | | const comp = wasm.base.comp; |
| 2957 | | const gpa = comp.gpa; |
| 2958 | | if (comp.config.output_mode == .Obj) return; |
| 2959 | | log.debug("Building exports from symbols", .{}); |
| 2960 | | |
| 2133 | fn checkExportNames(wasm: *Wasm) !void { |
| 2961 | 2134 | const force_exp_names = wasm.export_symbol_names; |
| 2962 | 2135 | if (force_exp_names.len > 0) { |
| 2963 | 2136 | var failed_exports = false; |
| 2964 | 2137 | |
| 2965 | 2138 | for (force_exp_names) |exp_name| { |
| 2966 | 2139 | const loc = wasm.findGlobalSymbol(exp_name) orelse { |
| 2967 | | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 2140 | try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name}); |
| 2968 | 2141 | failed_exports = true; |
| 2969 | 2142 | continue; |
| 2970 | 2143 | }; |
| ... | ... | @@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void { |
| 2974 | 2147 | } |
| 2975 | 2148 | |
| 2976 | 2149 | if (failed_exports) { |
| 2977 | | return error.MissingSymbol; |
| 2150 | return error.FlushFailure; |
| 2978 | 2151 | } |
| 2979 | 2152 | } |
| 2153 | } |
| 2154 | |
| 2155 | fn setupExports(wasm: *Wasm) !void { |
| 2156 | const comp = wasm.base.comp; |
| 2157 | const gpa = comp.gpa; |
| 2158 | if (comp.config.output_mode == .Obj) return; |
| 2159 | log.debug("Building exports from symbols", .{}); |
| 2980 | 2160 | |
| 2981 | 2161 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2982 | 2162 | const symbol = sym_loc.getSymbol(wasm); |
| 2983 | 2163 | if (!symbol.isExported(comp.config.rdynamic)) continue; |
| 2984 | 2164 | |
| 2985 | 2165 | const sym_name = sym_loc.getName(wasm); |
| 2986 | | const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: { |
| 2987 | | if (sym_loc.file == null) break :blk symbol.name; |
| 2988 | | break :blk try wasm.string_table.put(gpa, sym_name); |
| 2989 | | }; |
| 2166 | const export_name = if (sym_loc.file == .null) |
| 2167 | symbol.name |
| 2168 | else |
| 2169 | try wasm.string_table.put(gpa, sym_name); |
| 2990 | 2170 | const exp: types.Export = if (symbol.tag == .data) exp: { |
| 2991 | 2171 | const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); |
| 2992 | 2172 | try wasm.wasm_globals.append(gpa, .{ |
| ... | ... | @@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void { |
| 3020 | 2200 | const entry_name = wasm.entry_name orelse return; |
| 3021 | 2201 | |
| 3022 | 2202 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 3023 | | log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); |
| 3024 | | return error.MissingSymbol; |
| 2203 | try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); |
| 2204 | return error.FlushFailure; |
| 3025 | 2205 | }; |
| 3026 | 2206 | |
| 3027 | 2207 | const symbol = symbol_loc.getSymbol(wasm); |
| 3028 | 2208 | if (symbol.tag != .function) { |
| 3029 | | log.err("Entry symbol '{s}' is not a function", .{entry_name}); |
| 3030 | | return error.InvalidEntryKind; |
| 2209 | try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name}); |
| 2210 | return error.FlushFailure; |
| 3031 | 2211 | } |
| 3032 | 2212 | |
| 3033 | 2213 | // Ensure the symbol is exported so host environment can access it |
| ... | ... | @@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3055 | 2235 | |
| 3056 | 2236 | const is_obj = comp.config.output_mode == .Obj; |
| 3057 | 2237 | |
| 2238 | const stack_ptr = if (wasm.findGlobalSymbol("__stack_pointer")) |loc| index: { |
| 2239 | const sym = loc.getSymbol(wasm); |
| 2240 | break :index sym.index - wasm.imported_globals_count; |
| 2241 | } else null; |
| 2242 | |
| 3058 | 2243 | if (place_stack_first and !is_obj) { |
| 3059 | 2244 | memory_ptr = stack_alignment.forward(memory_ptr); |
| 3060 | 2245 | memory_ptr += wasm.base.stack_size; |
| 3061 | 2246 | // We always put the stack pointer global at index 0 |
| 3062 | | wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); |
| 2247 | if (stack_ptr) |index| { |
| 2248 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); |
| 2249 | } |
| 3063 | 2250 | } |
| 3064 | 2251 | |
| 3065 | 2252 | var offset: u32 = @as(u32, @intCast(memory_ptr)); |
| ... | ... | @@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3098 | 2285 | memory_ptr = mem.alignForward(u64, memory_ptr, 4); |
| 3099 | 2286 | const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); |
| 3100 | 2287 | const sym = loc.getSymbol(wasm); |
| 2288 | sym.mark(); |
| 3101 | 2289 | sym.virtual_address = @as(u32, @intCast(memory_ptr)); |
| 3102 | 2290 | memory_ptr += 4; |
| 3103 | 2291 | } |
| ... | ... | @@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3105 | 2293 | if (!place_stack_first and !is_obj) { |
| 3106 | 2294 | memory_ptr = stack_alignment.forward(memory_ptr); |
| 3107 | 2295 | memory_ptr += wasm.base.stack_size; |
| 3108 | | wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); |
| 2296 | if (stack_ptr) |index| { |
| 2297 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); |
| 2298 | } |
| 3109 | 2299 | } |
| 3110 | 2300 | |
| 3111 | 2301 | // One of the linked object files has a reference to the __heap_base symbol. |
| ... | ... | @@ -3121,16 +2311,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3121 | 2311 | |
| 3122 | 2312 | if (wasm.initial_memory) |initial_memory| { |
| 3123 | 2313 | if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { |
| 3124 | | log.err("Initial memory must be {d}-byte aligned", .{page_size}); |
| 3125 | | return error.MissAlignment; |
| 2314 | try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size}); |
| 3126 | 2315 | } |
| 3127 | 2316 | if (memory_ptr > initial_memory) { |
| 3128 | | log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3129 | | return error.MemoryTooSmall; |
| 2317 | try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3130 | 2318 | } |
| 3131 | 2319 | if (initial_memory > max_memory_allowed) { |
| 3132 | | log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); |
| 3133 | | return error.MemoryTooBig; |
| 2320 | try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); |
| 3134 | 2321 | } |
| 3135 | 2322 | memory_ptr = initial_memory; |
| 3136 | 2323 | } |
| ... | ... | @@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3147 | 2334 | |
| 3148 | 2335 | if (wasm.max_memory) |max_memory| { |
| 3149 | 2336 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { |
| 3150 | | log.err("Maximum memory must be {d}-byte aligned", .{page_size}); |
| 3151 | | return error.MissAlignment; |
| 2337 | try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size}); |
| 3152 | 2338 | } |
| 3153 | 2339 | if (memory_ptr > max_memory) { |
| 3154 | | log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3155 | | return error.MemoryTooSmall; |
| 2340 | try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3156 | 2341 | } |
| 3157 | 2342 | if (max_memory > max_memory_allowed) { |
| 3158 | | log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed}); |
| 3159 | | return error.MemoryTooBig; |
| 2343 | try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed}); |
| 3160 | 2344 | } |
| 3161 | 2345 | wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); |
| 3162 | 2346 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); |
| ... | ... | @@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3170 | 2354 | /// From a given object's index and the index of the segment, returns the corresponding |
| 3171 | 2355 | /// index of the segment within the final data section. When the segment does not yet |
| 3172 | 2356 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. |
| 3173 | | pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 { |
| 2357 | pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 { |
| 3174 | 2358 | const comp = wasm.base.comp; |
| 3175 | 2359 | const gpa = comp.gpa; |
| 3176 | | const object: Object = wasm.objects.items[object_index]; |
| 3177 | | const symbol = object.symtable[symbol_index]; |
| 2360 | const obj_file = wasm.file(file_index).?; |
| 2361 | const symbol = obj_file.symbols()[@intFromEnum(symbol_index)]; |
| 3178 | 2362 | const index: u32 = @intCast(wasm.segments.items.len); |
| 3179 | 2363 | const shared_memory = comp.config.shared_memory; |
| 3180 | 2364 | |
| 3181 | 2365 | switch (symbol.tag) { |
| 3182 | 2366 | .data => { |
| 3183 | | const segment_info = object.segment_info[symbol.index]; |
| 2367 | const segment_info = obj_file.segmentInfo()[symbol.index]; |
| 3184 | 2368 | const merge_segment = comp.config.output_mode != .Obj; |
| 3185 | 2369 | const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); |
| 3186 | 2370 | if (!result.found_existing) { |
| ... | ... | @@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 |
| 3209 | 2393 | break :blk index; |
| 3210 | 2394 | }, |
| 3211 | 2395 | .section => { |
| 3212 | | const section_name = object.string_table.get(symbol.name); |
| 2396 | const section_name = obj_file.symbolName(symbol_index); |
| 3213 | 2397 | if (mem.eql(u8, section_name, ".debug_info")) { |
| 3214 | 2398 | return wasm.debug_info_index orelse blk: { |
| 3215 | 2399 | wasm.debug_info_index = index; |
| ... | ... | @@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 |
| 3257 | 2441 | wasm.debug_str_index = index; |
| 3258 | 2442 | try wasm.appendDummySegment(); |
| 3259 | 2443 | break :blk index; |
| 3260 | | }; |
| 3261 | | } else { |
| 3262 | | log.warn("found unknown section '{s}'", .{section_name}); |
| 3263 | | return error.UnexpectedValue; |
| 3264 | | } |
| 3265 | | }, |
| 3266 | | else => unreachable, |
| 3267 | | } |
| 3268 | | } |
| 3269 | | |
| 3270 | | /// Appends a new segment with default field values |
| 3271 | | fn appendDummySegment(wasm: *Wasm) !void { |
| 3272 | | const gpa = wasm.base.comp.gpa; |
| 3273 | | try wasm.segments.append(gpa, .{ |
| 3274 | | .alignment = .@"1", |
| 3275 | | .size = 0, |
| 3276 | | .offset = 0, |
| 3277 | | .flags = 0, |
| 3278 | | }); |
| 3279 | | } |
| 3280 | | |
| 3281 | | /// Returns the symbol index of the error name table. |
| 3282 | | /// |
| 3283 | | /// When the symbol does not yet exist, it will create a new one instead. |
| 3284 | | pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 3285 | | if (wasm.error_table_symbol) |symbol| { |
| 3286 | | return symbol; |
| 3287 | | } |
| 3288 | | |
| 3289 | | // no error was referenced yet, so create a new symbol and atom for it |
| 3290 | | // and then return said symbol's index. The final table will be populated |
| 3291 | | // during `flush` when we know all possible error names. |
| 3292 | | |
| 3293 | | const gpa = wasm.base.comp.gpa; |
| 3294 | | const atom_index = try wasm.createAtom(); |
| 3295 | | const atom = wasm.getAtomPtr(atom_index); |
| 3296 | | const slice_ty = Type.slice_const_u8_sentinel_0; |
| 3297 | | const mod = wasm.base.comp.module.?; |
| 3298 | | atom.alignment = slice_ty.abiAlignment(mod); |
| 3299 | | const sym_index = atom.sym_index; |
| 3300 | | |
| 3301 | | const sym_name = try wasm.string_table.put(gpa, "__zig_err_name_table"); |
| 3302 | | const symbol = &wasm.symbols.items[sym_index]; |
| 3303 | | symbol.* = .{ |
| 3304 | | .name = sym_name, |
| 3305 | | .tag = .data, |
| 3306 | | .flags = 0, |
| 3307 | | .index = 0, |
| 3308 | | .virtual_address = undefined, |
| 3309 | | }; |
| 3310 | | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 3311 | | symbol.mark(); |
| 3312 | | |
| 3313 | | try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {}); |
| 3314 | | |
| 3315 | | log.debug("Error name table was created with symbol index: ({d})", .{sym_index}); |
| 3316 | | wasm.error_table_symbol = sym_index; |
| 3317 | | return sym_index; |
| 3318 | | } |
| 3319 | | |
| 3320 | | /// Populates the error name table, when `error_table_symbol` is not null. |
| 3321 | | /// |
| 3322 | | /// This creates a table that consists of pointers and length to each error name. |
| 3323 | | /// The table is what is being pointed to within the runtime bodies that are generated. |
| 3324 | | fn populateErrorNameTable(wasm: *Wasm) !void { |
| 3325 | | const gpa = wasm.base.comp.gpa; |
| 3326 | | const symbol_index = wasm.error_table_symbol orelse return; |
| 3327 | | const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; |
| 3328 | | |
| 3329 | | // Rather than creating a symbol for each individual error name, |
| 3330 | | // we create a symbol for the entire region of error names. We then calculate |
| 3331 | | // the pointers into the list using addends which are appended to the relocation. |
| 3332 | | const names_atom_index = try wasm.createAtom(); |
| 3333 | | const names_atom = wasm.getAtomPtr(names_atom_index); |
| 3334 | | names_atom.alignment = .@"1"; |
| 3335 | | const sym_name = try wasm.string_table.put(gpa, "__zig_err_names"); |
| 3336 | | const names_symbol = &wasm.symbols.items[names_atom.sym_index]; |
| 3337 | | names_symbol.* = .{ |
| 3338 | | .name = sym_name, |
| 3339 | | .tag = .data, |
| 3340 | | .flags = 0, |
| 3341 | | .index = 0, |
| 3342 | | .virtual_address = undefined, |
| 3343 | | }; |
| 3344 | | names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 3345 | | names_symbol.mark(); |
| 3346 | | |
| 3347 | | log.debug("Populating error names", .{}); |
| 3348 | | |
| 3349 | | // Addend for each relocation to the table |
| 3350 | | var addend: u32 = 0; |
| 3351 | | const mod = wasm.base.comp.module.?; |
| 3352 | | for (mod.global_error_set.keys()) |error_name_nts| { |
| 3353 | | const atom = wasm.getAtomPtr(atom_index); |
| 3354 | | |
| 3355 | | const error_name = mod.intern_pool.stringToSlice(error_name_nts); |
| 3356 | | const len = @as(u32, @intCast(error_name.len + 1)); // names are 0-termianted |
| 3357 | | |
| 3358 | | const slice_ty = Type.slice_const_u8_sentinel_0; |
| 3359 | | const offset = @as(u32, @intCast(atom.code.items.len)); |
| 3360 | | // first we create the data for the slice of the name |
| 3361 | | try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated |
| 3362 | | try atom.code.writer(gpa).writeInt(u32, len - 1, .little); |
| 3363 | | // create relocation to the error name |
| 3364 | | try atom.relocs.append(gpa, .{ |
| 3365 | | .index = names_atom.sym_index, |
| 3366 | | .relocation_type = .R_WASM_MEMORY_ADDR_I32, |
| 3367 | | .offset = offset, |
| 3368 | | .addend = @as(i32, @intCast(addend)), |
| 3369 | | }); |
| 3370 | | atom.size += @as(u32, @intCast(slice_ty.abiSize(mod))); |
| 3371 | | addend += len; |
| 3372 | | |
| 3373 | | // as we updated the error name table, we now store the actual name within the names atom |
| 3374 | | try names_atom.code.ensureUnusedCapacity(gpa, len); |
| 3375 | | names_atom.code.appendSliceAssumeCapacity(error_name); |
| 3376 | | names_atom.code.appendAssumeCapacity(0); |
| 3377 | | |
| 3378 | | log.debug("Populated error name: '{s}'", .{error_name}); |
| 2444 | }; |
| 2445 | } else { |
| 2446 | var err = try wasm.addErrorWithNotes(1); |
| 2447 | try err.addMsg(wasm, "found unknown section '{s}'", .{section_name}); |
| 2448 | try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()}); |
| 2449 | return error.UnexpectedValue; |
| 2450 | } |
| 2451 | }, |
| 2452 | else => unreachable, |
| 3379 | 2453 | } |
| 3380 | | names_atom.size = addend; |
| 3381 | | |
| 3382 | | const name_loc = names_atom.symbolLoc(); |
| 3383 | | try wasm.resolved_symbols.put(gpa, name_loc, {}); |
| 3384 | | try wasm.symbol_atom.put(gpa, name_loc, names_atom_index); |
| 3385 | | |
| 3386 | | // link the atoms with the rest of the binary so they can be allocated |
| 3387 | | // and relocations will be performed. |
| 3388 | | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 3389 | | try wasm.parseAtom(names_atom_index, .{ .data = .read_only }); |
| 3390 | 2454 | } |
| 3391 | 2455 | |
| 3392 | | /// From a given index variable, creates a new debug section. |
| 3393 | | /// This initializes the index, appends a new segment, |
| 3394 | | /// and finally, creates a managed `Atom`. |
| 3395 | | pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index { |
| 3396 | | const gpa = wasm.base.comp.gpa; |
| 3397 | | const new_index: u32 = @intCast(wasm.segments.items.len); |
| 3398 | | index.* = new_index; |
| 3399 | | try wasm.appendDummySegment(); |
| 3400 | | |
| 3401 | | const atom_index = try wasm.createAtom(); |
| 3402 | | const atom = wasm.getAtomPtr(atom_index); |
| 3403 | | wasm.symbols.items[atom.sym_index] = .{ |
| 3404 | | .tag = .section, |
| 3405 | | .name = try wasm.string_table.put(gpa, name), |
| 3406 | | .index = 0, |
| 3407 | | .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 3408 | | }; |
| 3409 | | |
| 3410 | | atom.alignment = .@"1"; // debug sections are always 1-byte-aligned |
| 3411 | | return atom_index; |
| 3412 | | } |
| 3413 | | |
| 3414 | | fn resetState(wasm: *Wasm) void { |
| 2456 | /// Appends a new segment with default field values |
| 2457 | fn appendDummySegment(wasm: *Wasm) !void { |
| 3415 | 2458 | const gpa = wasm.base.comp.gpa; |
| 3416 | | |
| 3417 | | for (wasm.segment_info.values()) |segment_info| { |
| 3418 | | gpa.free(segment_info.name); |
| 3419 | | } |
| 3420 | | |
| 3421 | | var atom_it = wasm.decls.valueIterator(); |
| 3422 | | while (atom_it.next()) |atom_index| { |
| 3423 | | const atom = wasm.getAtomPtr(atom_index.*); |
| 3424 | | atom.prev = null; |
| 3425 | | |
| 3426 | | for (atom.locals.items) |local_atom_index| { |
| 3427 | | const local_atom = wasm.getAtomPtr(local_atom_index); |
| 3428 | | local_atom.prev = null; |
| 3429 | | } |
| 3430 | | } |
| 3431 | | |
| 3432 | | wasm.functions.clearRetainingCapacity(); |
| 3433 | | wasm.exports.clearRetainingCapacity(); |
| 3434 | | wasm.segments.clearRetainingCapacity(); |
| 3435 | | wasm.segment_info.clearRetainingCapacity(); |
| 3436 | | wasm.data_segments.clearRetainingCapacity(); |
| 3437 | | wasm.atoms.clearRetainingCapacity(); |
| 3438 | | wasm.symbol_atom.clearRetainingCapacity(); |
| 3439 | | wasm.code_section_index = null; |
| 3440 | | wasm.debug_info_index = null; |
| 3441 | | wasm.debug_line_index = null; |
| 3442 | | wasm.debug_loc_index = null; |
| 3443 | | wasm.debug_str_index = null; |
| 3444 | | wasm.debug_ranges_index = null; |
| 3445 | | wasm.debug_abbrev_index = null; |
| 3446 | | wasm.debug_pubnames_index = null; |
| 3447 | | wasm.debug_pubtypes_index = null; |
| 2459 | try wasm.segments.append(gpa, .{ |
| 2460 | .alignment = .@"1", |
| 2461 | .size = 0, |
| 2462 | .offset = 0, |
| 2463 | .flags = 0, |
| 2464 | }); |
| 3448 | 2465 | } |
| 3449 | 2466 | |
| 3450 | 2467 | pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 3451 | 2468 | const comp = wasm.base.comp; |
| 3452 | 2469 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 3453 | | const use_llvm = comp.config.use_llvm; |
| 3454 | 2470 | |
| 3455 | 2471 | if (use_lld) { |
| 3456 | 2472 | return wasm.linkWithLLD(arena, prog_node); |
| 3457 | | } else if (use_llvm) { |
| 3458 | | return wasm.linkWithZld(arena, prog_node); |
| 3459 | | } else { |
| 3460 | | return wasm.flushModule(arena, prog_node); |
| 3461 | 2473 | } |
| 2474 | return wasm.flushModule(arena, prog_node); |
| 3462 | 2475 | } |
| 3463 | 2476 | |
| 3464 | 2477 | /// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary. |
| 3465 | | fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 2478 | pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 3466 | 2479 | const tracy = trace(@src()); |
| 3467 | 2480 | defer tracy.end(); |
| 3468 | 2481 | |
| 3469 | 2482 | const comp = wasm.base.comp; |
| 3470 | | const shared_memory = comp.config.shared_memory; |
| 3471 | | const import_memory = comp.config.import_memory; |
| 2483 | if (wasm.llvm_object) |llvm_object| { |
| 2484 | try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); |
| 2485 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 2486 | if (use_lld) return; |
| 2487 | } |
| 2488 | |
| 2489 | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 2490 | sub_prog_node.activate(); |
| 2491 | defer sub_prog_node.end(); |
| 3472 | 2492 | |
| 3473 | 2493 | const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. |
| 3474 | 2494 | const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); |
| 3475 | | const opt_zcu = comp.module; |
| 3476 | | const use_llvm = comp.config.use_llvm; |
| 3477 | | |
| 3478 | | // If there is no Zig code to compile, then we should skip flushing the output file because it |
| 3479 | | // will not be part of the linker line anyway. |
| 3480 | | const module_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { |
| 3481 | | assert(use_llvm); // `linkWithZld` should never be called when the Wasm backend is used |
| 3482 | | try wasm.flushModule(arena, prog_node); |
| 3483 | | |
| 2495 | const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: { |
| 3484 | 2496 | if (fs.path.dirname(full_out_path)) |dirname| { |
| 3485 | | break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? }); |
| 2497 | break :blk try fs.path.join(arena, &.{ dirname, path }); |
| 3486 | 2498 | } else { |
| 3487 | | break :blk wasm.base.zcu_object_sub_path.?; |
| 2499 | break :blk path; |
| 3488 | 2500 | } |
| 3489 | 2501 | } else null; |
| 3490 | 2502 | |
| 3491 | | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 3492 | | sub_prog_node.activate(); |
| 3493 | | defer sub_prog_node.end(); |
| 3494 | | |
| 3495 | | const compiler_rt_path: ?[]const u8 = blk: { |
| 3496 | | if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; |
| 3497 | | if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; |
| 3498 | | break :blk null; |
| 3499 | | }; |
| 3500 | | |
| 3501 | | const id_symlink_basename = "zld.id"; |
| 3502 | | |
| 3503 | | var man: Cache.Manifest = undefined; |
| 3504 | | defer if (!wasm.base.disable_lld_caching) man.deinit(); |
| 3505 | | var digest: [Cache.hex_digest_len]u8 = undefined; |
| 3506 | | |
| 3507 | | const objects = comp.objects; |
| 3508 | | |
| 3509 | | // NOTE: The following section must be maintained to be equal |
| 3510 | | // as the section defined in `linkWithLLD` |
| 3511 | | if (!wasm.base.disable_lld_caching) { |
| 3512 | | man = comp.cache_parent.obtain(); |
| 3513 | | |
| 3514 | | // We are about to obtain this lock, so here we give other processes a chance first. |
| 3515 | | wasm.base.releaseLock(); |
| 3516 | | |
| 3517 | | comptime assert(Compilation.link_hash_implementation_version == 12); |
| 3518 | | |
| 3519 | | for (objects) |obj| { |
| 3520 | | _ = try man.addFile(obj.path, null); |
| 3521 | | man.hash.add(obj.must_link); |
| 3522 | | } |
| 3523 | | for (comp.c_object_table.keys()) |key| { |
| 3524 | | _ = try man.addFile(key.status.success.object_path, null); |
| 3525 | | } |
| 3526 | | try man.addOptionalFile(module_obj_path); |
| 3527 | | try man.addOptionalFile(compiler_rt_path); |
| 3528 | | man.hash.addOptionalBytes(wasm.entry_name); |
| 3529 | | man.hash.add(wasm.base.stack_size); |
| 3530 | | man.hash.add(wasm.base.build_id); |
| 3531 | | man.hash.add(import_memory); |
| 3532 | | man.hash.add(shared_memory); |
| 3533 | | man.hash.add(wasm.import_table); |
| 3534 | | man.hash.add(wasm.export_table); |
| 3535 | | man.hash.addOptional(wasm.initial_memory); |
| 3536 | | man.hash.addOptional(wasm.max_memory); |
| 3537 | | man.hash.addOptional(wasm.global_base); |
| 3538 | | man.hash.addListOfBytes(wasm.export_symbol_names); |
| 3539 | | // strip does not need to go into the linker hash because it is part of the hash namespace |
| 3540 | | |
| 3541 | | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| 3542 | | _ = try man.hit(); |
| 3543 | | digest = man.final(); |
| 3544 | | |
| 3545 | | var prev_digest_buf: [digest.len]u8 = undefined; |
| 3546 | | const prev_digest: []u8 = Cache.readSmallFile( |
| 3547 | | directory.handle, |
| 3548 | | id_symlink_basename, |
| 3549 | | &prev_digest_buf, |
| 3550 | | ) catch |err| blk: { |
| 3551 | | log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); |
| 3552 | | // Handle this as a cache miss. |
| 3553 | | break :blk prev_digest_buf[0..0]; |
| 3554 | | }; |
| 3555 | | if (mem.eql(u8, prev_digest, &digest)) { |
| 3556 | | log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); |
| 3557 | | // Hot diggity dog! The output binary is already there. |
| 3558 | | wasm.base.lock = man.toOwnedLock(); |
| 3559 | | return; |
| 3560 | | } |
| 3561 | | log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); |
| 3562 | | |
| 3563 | | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 3564 | | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| 3565 | | error.FileNotFound => {}, |
| 3566 | | else => |e| return e, |
| 3567 | | }; |
| 3568 | | } |
| 3569 | | |
| 3570 | 2503 | // Positional arguments to the linker such as object files and static archives. |
| 3571 | 2504 | var positionals = std.ArrayList([]const u8).init(arena); |
| 3572 | | try positionals.ensureUnusedCapacity(objects.len); |
| 2505 | try positionals.ensureUnusedCapacity(comp.objects.len); |
| 3573 | 2506 | |
| 3574 | 2507 | const target = comp.root_mod.resolved_target.result; |
| 3575 | 2508 | const output_mode = comp.config.output_mode; |
| ... | ... | @@ -3578,6 +2511,10 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3578 | 2511 | const link_libcpp = comp.config.link_libcpp; |
| 3579 | 2512 | const wasi_exec_model = comp.config.wasi_exec_model; |
| 3580 | 2513 | |
| 2514 | if (wasm.zigObjectPtr()) |zig_object| { |
| 2515 | try zig_object.flushModule(wasm); |
| 2516 | } |
| 2517 | |
| 3581 | 2518 | // When the target os is WASI, we allow linking with WASI-LIBC |
| 3582 | 2519 | if (target.os.tag == .wasi) { |
| 3583 | 2520 | const is_exe_or_dyn_lib = output_mode == .Exe or |
| ... | ... | @@ -3609,7 +2546,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3609 | 2546 | try positionals.append(path); |
| 3610 | 2547 | } |
| 3611 | 2548 | |
| 3612 | | for (objects) |object| { |
| 2549 | for (comp.objects) |object| { |
| 3613 | 2550 | try positionals.append(object.path); |
| 3614 | 2551 | } |
| 3615 | 2552 | |
| ... | ... | @@ -3622,171 +2559,35 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3622 | 2559 | |
| 3623 | 2560 | try wasm.parseInputFiles(positionals.items); |
| 3624 | 2561 | |
| 3625 | | for (wasm.objects.items, 0..) |_, object_index| { |
| 3626 | | try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index))); |
| 3627 | | } |
| 3628 | | |
| 3629 | | var emit_features_count: u32 = 0; |
| 3630 | | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 3631 | | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 3632 | | try wasm.resolveSymbolsInArchives(); |
| 3633 | | try wasm.resolveLazySymbols(); |
| 3634 | | try wasm.checkUndefinedSymbols(); |
| 3635 | | |
| 3636 | | try wasm.setupInitFunctions(); |
| 3637 | | try wasm.setupStart(); |
| 3638 | | |
| 3639 | | try wasm.markReferences(); |
| 3640 | | try wasm.setupImports(); |
| 3641 | | try wasm.mergeSections(); |
| 3642 | | try wasm.mergeTypes(); |
| 3643 | | try wasm.allocateAtoms(); |
| 3644 | | try wasm.setupMemory(); |
| 3645 | | wasm.allocateVirtualAddresses(); |
| 3646 | | wasm.mapFunctionTable(); |
| 3647 | | try wasm.initializeCallCtorsFunction(); |
| 3648 | | try wasm.setupInitMemoryFunction(); |
| 3649 | | try wasm.setupTLSRelocationsFunction(); |
| 3650 | | try wasm.initializeTLSFunction(); |
| 3651 | | try wasm.setupStartSection(); |
| 3652 | | try wasm.setupExports(); |
| 3653 | | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 3654 | | |
| 3655 | | if (!wasm.base.disable_lld_caching) { |
| 3656 | | // Update the file with the digest. If it fails we can continue; it only |
| 3657 | | // means that the next invocation will have an unnecessary cache miss. |
| 3658 | | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { |
| 3659 | | log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); |
| 3660 | | }; |
| 3661 | | // Again failure here only means an unnecessary cache miss. |
| 3662 | | man.writeManifest() catch |err| { |
| 3663 | | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); |
| 3664 | | }; |
| 3665 | | // We hang on to this lock so that the output file path can be used without |
| 3666 | | // other processes clobbering it. |
| 3667 | | wasm.base.lock = man.toOwnedLock(); |
| 3668 | | } |
| 3669 | | } |
| 3670 | | |
| 3671 | | pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 3672 | | const tracy = trace(@src()); |
| 3673 | | defer tracy.end(); |
| 3674 | | |
| 3675 | | const comp = wasm.base.comp; |
| 3676 | | |
| 3677 | | if (wasm.llvm_object) |llvm_object| { |
| 3678 | | try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); |
| 3679 | | return; |
| 3680 | | } |
| 3681 | | |
| 3682 | | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 3683 | | sub_prog_node.activate(); |
| 3684 | | defer sub_prog_node.end(); |
| 3685 | | |
| 3686 | | // ensure the error names table is populated when an error name is referenced |
| 3687 | | try wasm.populateErrorNameTable(); |
| 3688 | | |
| 3689 | | const objects = comp.objects; |
| 3690 | | |
| 3691 | | // Positional arguments to the linker such as object files and static archives. |
| 3692 | | var positionals = std.ArrayList([]const u8).init(arena); |
| 3693 | | try positionals.ensureUnusedCapacity(objects.len); |
| 3694 | | |
| 3695 | | for (objects) |object| { |
| 3696 | | positionals.appendAssumeCapacity(object.path); |
| 3697 | | } |
| 3698 | | |
| 3699 | | for (comp.c_object_table.keys()) |c_object| { |
| 3700 | | try positionals.append(c_object.status.success.object_path); |
| 2562 | if (wasm.zig_object_index != .null) { |
| 2563 | try wasm.resolveSymbolsInObject(wasm.zig_object_index); |
| 3701 | 2564 | } |
| 3702 | | |
| 3703 | | if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path); |
| 3704 | | if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path); |
| 3705 | | |
| 3706 | | try wasm.parseInputFiles(positionals.items); |
| 3707 | | |
| 3708 | | for (wasm.objects.items, 0..) |_, object_index| { |
| 3709 | | try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index))); |
| 2565 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 2566 | for (wasm.objects.items) |object_index| { |
| 2567 | try wasm.resolveSymbolsInObject(object_index); |
| 3710 | 2568 | } |
| 2569 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3711 | 2570 | |
| 3712 | 2571 | var emit_features_count: u32 = 0; |
| 3713 | 2572 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 3714 | 2573 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 3715 | 2574 | try wasm.resolveSymbolsInArchives(); |
| 2575 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3716 | 2576 | try wasm.resolveLazySymbols(); |
| 3717 | 2577 | try wasm.checkUndefinedSymbols(); |
| 2578 | try wasm.checkExportNames(); |
| 3718 | 2579 | |
| 3719 | | // When we finish/error we reset the state of the linker |
| 3720 | | // So we can rebuild the binary file on each incremental update |
| 3721 | | defer wasm.resetState(); |
| 3722 | 2580 | try wasm.setupInitFunctions(); |
| 2581 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3723 | 2582 | try wasm.setupStart(); |
| 2583 | |
| 3724 | 2584 | try wasm.markReferences(); |
| 3725 | | try wasm.setupErrorsLen(); |
| 3726 | 2585 | try wasm.setupImports(); |
| 3727 | | if (comp.module) |mod| { |
| 3728 | | var decl_it = wasm.decls.iterator(); |
| 3729 | | while (decl_it.next()) |entry| { |
| 3730 | | const decl = mod.declPtr(entry.key_ptr.*); |
| 3731 | | if (decl.isExtern(mod)) continue; |
| 3732 | | const atom_index = entry.value_ptr.*; |
| 3733 | | const atom = wasm.getAtomPtr(atom_index); |
| 3734 | | if (decl.ty.zigTypeTag(mod) == .Fn) { |
| 3735 | | try wasm.parseAtom(atom_index, .function); |
| 3736 | | } else if (decl.getOwnedVariable(mod)) |variable| { |
| 3737 | | if (variable.is_const) { |
| 3738 | | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 3739 | | } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) { |
| 3740 | | // for safe build modes, we store the atom in the data segment, |
| 3741 | | // whereas for unsafe build modes we store it in bss. |
| 3742 | | const decl_namespace = mod.namespacePtr(decl.src_namespace); |
| 3743 | | const optimize_mode = decl_namespace.file_scope.mod.optimize_mode; |
| 3744 | | const is_initialized = switch (optimize_mode) { |
| 3745 | | .Debug, .ReleaseSafe => true, |
| 3746 | | .ReleaseFast, .ReleaseSmall => false, |
| 3747 | | }; |
| 3748 | | try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized }); |
| 3749 | | } else { |
| 3750 | | // when the decl is all zeroes, we store the atom in the bss segment, |
| 3751 | | // in all other cases it will be in the data segment. |
| 3752 | | const is_zeroes = for (atom.code.items) |byte| { |
| 3753 | | if (byte != 0) break false; |
| 3754 | | } else true; |
| 3755 | | try wasm.parseAtom(atom_index, .{ .data = if (is_zeroes) .uninitialized else .initialized }); |
| 3756 | | } |
| 3757 | | } else { |
| 3758 | | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 3759 | | } |
| 3760 | | |
| 3761 | | // also parse atoms for a decl's locals |
| 3762 | | for (atom.locals.items) |local_atom_index| { |
| 3763 | | try wasm.parseAtom(local_atom_index, .{ .data = .read_only }); |
| 3764 | | } |
| 3765 | | } |
| 3766 | | // parse anonymous declarations |
| 3767 | | for (wasm.anon_decls.keys(), wasm.anon_decls.values()) |decl_val, atom_index| { |
| 3768 | | const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); |
| 3769 | | if (ty.zigTypeTag(mod) == .Fn) { |
| 3770 | | try wasm.parseAtom(atom_index, .function); |
| 3771 | | } else { |
| 3772 | | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 3773 | | } |
| 3774 | | } |
| 3775 | | |
| 3776 | | // also parse any backend-generated functions |
| 3777 | | for (wasm.synthetic_functions.items) |atom_index| { |
| 3778 | | try wasm.parseAtom(atom_index, .function); |
| 3779 | | } |
| 3780 | | |
| 3781 | | if (wasm.dwarf) |*dwarf| { |
| 3782 | | try dwarf.flushModule(comp.module.?); |
| 3783 | | } |
| 3784 | | } |
| 3785 | | |
| 3786 | 2586 | try wasm.mergeSections(); |
| 3787 | 2587 | try wasm.mergeTypes(); |
| 3788 | 2588 | try wasm.allocateAtoms(); |
| 3789 | 2589 | try wasm.setupMemory(); |
| 2590 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3790 | 2591 | wasm.allocateVirtualAddresses(); |
| 3791 | 2592 | wasm.mapFunctionTable(); |
| 3792 | 2593 | try wasm.initializeCallCtorsFunction(); |
| ... | ... | @@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) |
| 3796 | 2597 | try wasm.setupStartSection(); |
| 3797 | 2598 | try wasm.setupExports(); |
| 3798 | 2599 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2600 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3799 | 2601 | } |
| 3800 | 2602 | |
| 3801 | 2603 | /// Writes the WebAssembly in-memory module to the file |
| ... | ... | @@ -4021,7 +2823,9 @@ fn writeToFile( |
| 4021 | 2823 | try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count()))); |
| 4022 | 2824 | var symbol_it = wasm.function_table.keyIterator(); |
| 4023 | 2825 | while (symbol_it.next()) |symbol_loc_ptr| { |
| 4024 | | const sym = symbol_loc_ptr.*.getSymbol(wasm); |
| 2826 | const sym = symbol_loc_ptr.getSymbol(wasm); |
| 2827 | std.debug.assert(sym.isAlive()); |
| 2828 | std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count); |
| 4025 | 2829 | try leb.writeULEB128(binary_writer, sym.index); |
| 4026 | 2830 | } |
| 4027 | 2831 | |
| ... | ... | @@ -4124,8 +2928,8 @@ fn writeToFile( |
| 4124 | 2928 | try binary_writer.writeAll(atom.code.items); |
| 4125 | 2929 | |
| 4126 | 2930 | current_offset += atom.size; |
| 4127 | | if (atom.prev) |prev| { |
| 4128 | | atom_index = prev; |
| 2931 | if (atom.prev != .null) { |
| 2932 | atom_index = atom.prev; |
| 4129 | 2933 | } else { |
| 4130 | 2934 | // also pad with zeroes when last atom to ensure |
| 4131 | 2935 | // segments are aligned. |
| ... | ... | @@ -4191,19 +2995,9 @@ fn writeToFile( |
| 4191 | 2995 | }) catch unreachable; |
| 4192 | 2996 | try emitBuildIdSection(&binary_bytes, str); |
| 4193 | 2997 | }, |
| 4194 | | else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}), |
| 2998 | else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}), |
| 4195 | 2999 | } |
| 4196 | 3000 | |
| 4197 | | // if (wasm.dwarf) |*dwarf| { |
| 4198 | | // const mod = comp.module.?; |
| 4199 | | // try dwarf.writeDbgAbbrev(); |
| 4200 | | // // for debug info and ranges, the address is always 0, |
| 4201 | | // // as locations are always offsets relative to 'code' section. |
| 4202 | | // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); |
| 4203 | | // try dwarf.writeDbgAranges(0, code_section_size); |
| 4204 | | // try dwarf.writeDbgLineHeader(); |
| 4205 | | // } |
| 4206 | | |
| 4207 | 3001 | var debug_bytes = std.ArrayList(u8).init(gpa); |
| 4208 | 3002 | defer debug_bytes.deinit(); |
| 4209 | 3003 | |
| ... | ... | @@ -4229,7 +3023,8 @@ fn writeToFile( |
| 4229 | 3023 | while (true) { |
| 4230 | 3024 | atom.resolveRelocs(wasm); |
| 4231 | 3025 | try debug_bytes.appendSlice(atom.code.items); |
| 4232 | | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; |
| 3026 | if (atom.prev == .null) break; |
| 3027 | atom = wasm.getAtomPtr(atom.prev); |
| 4233 | 3028 | } |
| 4234 | 3029 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); |
| 4235 | 3030 | debug_bytes.clearRetainingCapacity(); |
| ... | ... | @@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: |
| 5004 | 3799 | try leb.writeULEB128(writer, @intFromEnum(symbol.tag)); |
| 5005 | 3800 | try leb.writeULEB128(writer, symbol.flags); |
| 5006 | 3801 | |
| 5007 | | const sym_name = if (wasm.export_names.get(sym_loc)) |exp_name| wasm.string_table.get(exp_name) else sym_loc.getName(wasm); |
| 3802 | const sym_name = sym_loc.getName(wasm); |
| 5008 | 3803 | switch (symbol.tag) { |
| 5009 | 3804 | .data => { |
| 5010 | 3805 | try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len))); |
| ... | ... | @@ -5098,7 +3893,7 @@ fn emitCodeRelocations( |
| 5098 | 3893 | size_offset += getULEB128Size(atom.size); |
| 5099 | 3894 | for (atom.relocs.items) |relocation| { |
| 5100 | 3895 | count += 1; |
| 5101 | | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index }; |
| 3896 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) }; |
| 5102 | 3897 | const symbol_index = symbol_table.get(sym_loc).?; |
| 5103 | 3898 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); |
| 5104 | 3899 | const offset = atom.offset + relocation.offset + size_offset; |
| ... | ... | @@ -5109,7 +3904,8 @@ fn emitCodeRelocations( |
| 5109 | 3904 | } |
| 5110 | 3905 | log.debug("Emit relocation: {}", .{relocation}); |
| 5111 | 3906 | } |
| 5112 | | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; |
| 3907 | if (atom.prev == .null) break; |
| 3908 | atom = wasm.getAtomPtr(atom.prev); |
| 5113 | 3909 | } |
| 5114 | 3910 | if (count == 0) return; |
| 5115 | 3911 | var buf: [5]u8 = undefined; |
| ... | ... | @@ -5145,10 +3941,7 @@ fn emitDataRelocations( |
| 5145 | 3941 | size_offset += getULEB128Size(atom.size); |
| 5146 | 3942 | for (atom.relocs.items) |relocation| { |
| 5147 | 3943 | count += 1; |
| 5148 | | const sym_loc: SymbolLoc = .{ |
| 5149 | | .file = atom.file, |
| 5150 | | .index = relocation.index, |
| 5151 | | }; |
| 3944 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) }; |
| 5152 | 3945 | const symbol_index = symbol_table.get(sym_loc).?; |
| 5153 | 3946 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); |
| 5154 | 3947 | const offset = atom.offset + relocation.offset + size_offset; |
| ... | ... | @@ -5159,7 +3952,8 @@ fn emitDataRelocations( |
| 5159 | 3952 | } |
| 5160 | 3953 | log.debug("Emit relocation: {}", .{relocation}); |
| 5161 | 3954 | } |
| 5162 | | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; |
| 3955 | if (atom.prev == .null) break; |
| 3956 | atom = wasm.getAtomPtr(atom.prev); |
| 5163 | 3957 | } |
| 5164 | 3958 | } |
| 5165 | 3959 | if (count == 0) return; |
| ... | ... | @@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { |
| 5185 | 3979 | return false; |
| 5186 | 3980 | } |
| 5187 | 3981 | |
| 5188 | | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 5189 | | var index: u32 = 0; |
| 5190 | | while (index < wasm.func_types.items.len) : (index += 1) { |
| 5191 | | if (wasm.func_types.items[index].eql(func_type)) return index; |
| 5192 | | } |
| 5193 | | return null; |
| 5194 | | } |
| 5195 | | |
| 5196 | 3982 | /// Searches for a matching function signature. When no matching signature is found, |
| 5197 | 3983 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 5198 | 3984 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 5199 | 3985 | if (wasm.getTypeIndex(func_type)) |index| { |
| 5200 | 3986 | return index; |
| 5201 | 3987 | } |
| 5202 | | const gpa = wasm.base.comp.gpa; |
| 5203 | 3988 | |
| 5204 | 3989 | // functype does not exist. |
| 3990 | const gpa = wasm.base.comp.gpa; |
| 5205 | 3991 | const index: u32 = @intCast(wasm.func_types.items.len); |
| 5206 | 3992 | const params = try gpa.dupe(std.wasm.Valtype, func_type.params); |
| 5207 | 3993 | errdefer gpa.free(params); |
| ... | ... | @@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 5218 | 4004 | /// Asserts declaration has an associated `Atom`. |
| 5219 | 4005 | /// Returns the index into the list of types. |
| 5220 | 4006 | pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 { |
| 5221 | | const gpa = wasm.base.comp.gpa; |
| 5222 | | const atom_index = wasm.decls.get(decl_index).?; |
| 5223 | | const index = try wasm.putOrGetFuncType(func_type); |
| 5224 | | try wasm.atom_types.put(gpa, atom_index, index); |
| 5225 | | return index; |
| 4007 | return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, decl_index, func_type); |
| 4008 | } |
| 4009 | |
| 4010 | /// Returns the symbol index of the error name table. |
| 4011 | /// |
| 4012 | /// When the symbol does not yet exist, it will create a new one instead. |
| 4013 | pub fn getErrorTableSymbol(wasm_file: *Wasm) !u32 { |
| 4014 | const sym_index = try wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file); |
| 4015 | return @intFromEnum(sym_index); |
| 4016 | } |
| 4017 | |
| 4018 | /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`. |
| 4019 | /// When the index was not found, a new `Atom` will be created, and its index will be returned. |
| 4020 | /// The newly created Atom is empty with default fields as specified by `Atom.empty`. |
| 4021 | pub fn getOrCreateAtomForDecl(wasm_file: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index { |
| 4022 | return wasm_file.zigObjectPtr().?.getOrCreateAtomForDecl(wasm_file, decl_index); |
| 5226 | 4023 | } |
| 5227 | 4024 | |
| 5228 | 4025 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, |
| ... | ... | @@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void { |
| 5244 | 4041 | // Debug sections may require to be parsed and marked when it contains |
| 5245 | 4042 | // relocations to alive symbols. |
| 5246 | 4043 | if (sym.tag == .section and comp.config.debug_format != .strip) { |
| 5247 | | const file = sym_loc.file orelse continue; // Incremental debug info is done independently |
| 5248 | | const object = &wasm.objects.items[file]; |
| 5249 | | const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm); |
| 5250 | | const atom = wasm.getAtom(atom_index); |
| 5251 | | const atom_sym = atom.symbolLoc().getSymbol(wasm); |
| 5252 | | atom_sym.mark(); |
| 4044 | const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently |
| 4045 | _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index); |
| 4046 | sym.mark(); |
| 5253 | 4047 | } |
| 5254 | 4048 | } |
| 5255 | 4049 | } |
| ... | ... | @@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void { |
| 5265 | 4059 | return; |
| 5266 | 4060 | } |
| 5267 | 4061 | symbol.mark(); |
| 4062 | gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)}); |
| 5268 | 4063 | if (symbol.isUndefined()) { |
| 5269 | 4064 | // undefined symbols do not have an associated `Atom` and therefore also |
| 5270 | 4065 | // do not contain relocations. |
| 5271 | 4066 | return; |
| 5272 | 4067 | } |
| 5273 | 4068 | |
| 5274 | | const atom_index = if (loc.file) |file_index| idx: { |
| 5275 | | const object = &wasm.objects.items[file_index]; |
| 5276 | | const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm); |
| 5277 | | break :idx atom_index; |
| 5278 | | } else wasm.symbol_atom.get(loc) orelse return; |
| 4069 | const atom_index = if (wasm.file(loc.file)) |obj_file| |
| 4070 | try obj_file.parseSymbolIntoAtom(wasm, loc.index) |
| 4071 | else |
| 4072 | wasm.symbol_atom.get(loc) orelse return; |
| 5279 | 4073 | |
| 5280 | 4074 | const atom = wasm.getAtom(atom_index); |
| 5281 | 4075 | for (atom.relocs.items) |reloc| { |
| 5282 | | const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file }; |
| 4076 | const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file }; |
| 5283 | 4077 | try wasm.mark(target_loc.finalLoc(wasm)); |
| 5284 | 4078 | } |
| 5285 | 4079 | } |
| ... | ... | @@ -5290,3 +4084,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 |
| 5290 | 4084 | .command => "_start", |
| 5291 | 4085 | }; |
| 5292 | 4086 | } |
| 4087 | |
| 4088 | const ErrorWithNotes = struct { |
| 4089 | /// Allocated index in comp.link_errors array. |
| 4090 | index: usize, |
| 4091 | |
| 4092 | /// Next available note slot. |
| 4093 | note_slot: usize = 0, |
| 4094 | |
| 4095 | pub fn addMsg( |
| 4096 | err: ErrorWithNotes, |
| 4097 | wasm_file: *const Wasm, |
| 4098 | comptime format: []const u8, |
| 4099 | args: anytype, |
| 4100 | ) error{OutOfMemory}!void { |
| 4101 | const comp = wasm_file.base.comp; |
| 4102 | const gpa = comp.gpa; |
| 4103 | const err_msg = &comp.link_errors.items[err.index]; |
| 4104 | err_msg.msg = try std.fmt.allocPrint(gpa, format, args); |
| 4105 | } |
| 4106 | |
| 4107 | pub fn addNote( |
| 4108 | err: *ErrorWithNotes, |
| 4109 | wasm_file: *const Wasm, |
| 4110 | comptime format: []const u8, |
| 4111 | args: anytype, |
| 4112 | ) error{OutOfMemory}!void { |
| 4113 | const comp = wasm_file.base.comp; |
| 4114 | const gpa = comp.gpa; |
| 4115 | const err_msg = &comp.link_errors.items[err.index]; |
| 4116 | err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; |
| 4117 | err.note_slot += 1; |
| 4118 | } |
| 4119 | }; |
| 4120 | |
| 4121 | pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| 4122 | const comp = wasm.base.comp; |
| 4123 | const gpa = comp.gpa; |
| 4124 | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 4125 | return wasm.addErrorWithNotesAssumeCapacity(note_count); |
| 4126 | } |
| 4127 | |
| 4128 | pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void { |
| 4129 | const err = try wasm.addErrorWithNotes(0); |
| 4130 | try err.addMsg(wasm, fmt, args); |
| 4131 | } |
| 4132 | |
| 4133 | fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| 4134 | const comp = wasm.base.comp; |
| 4135 | const gpa = comp.gpa; |
| 4136 | const index = comp.link_errors.items.len; |
| 4137 | const err = comp.link_errors.addOneAssumeCapacity(); |
| 4138 | err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) }; |
| 4139 | return .{ .index = index }; |
| 4140 | } |