| ... | @@ -1,67 +1,78 @@ | ... | @@ -1,67 +1,78 @@ |
| 1 | const Wasm = @This(); | 1 | const Wasm = @This(); |
| 2 | | 2 | |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const builtin = @import("builtin"); | 4 | |
| 5 | const mem = std.mem; | | |
| 6 | const Allocator = std.mem.Allocator; | | |
| 7 | const assert = std.debug.assert; | 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 | const fs = std.fs; | 9 | const fs = std.fs; |
| 9 | const leb = std.leb; | 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 | const link = @import("../link.zig"); | 11 | const link = @import("../link.zig"); |
| 20 | const lldMain = @import("../main.zig").lldMain; | 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 | const trace = @import("../tracy.zig").trace; | 16 | const trace = @import("../tracy.zig").trace; |
| 22 | const build_options = @import("build_options"); | 17 | const types = @import("Wasm/types.zig"); |
| 23 | const wasi_libc = @import("../wasi_libc.zig"); | 18 | const wasi_libc = @import("../wasi_libc.zig"); |
| 24 | const Cache = std.Build.Cache; | 19 | |
| 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; | | |
| 29 | const Air = @import("../Air.zig"); | 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 | const Liveness = @import("../Liveness.zig"); | 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 | const Object = @import("Wasm/Object.zig"); | 32 | const Object = @import("Wasm/Object.zig"); |
| 33 | const Archive = @import("Wasm/Archive.zig"); | 33 | const Symbol = @import("Wasm/Symbol.zig"); |
| 34 | const types = @import("Wasm/types.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 | pub const Relocation = types.Relocation; | 39 | pub const Relocation = types.Relocation; |
| 36 | | 40 | |
| 37 | pub const base_tag: link.File.Tag = .wasm; | 41 | pub const base_tag: link.File.Tag = .wasm; |
| 38 | | 42 | |
| 39 | base: link.File, | 43 | base: link.File, |
| | 44 | /// Symbol name of the entry function to export |
| 40 | entry_name: ?[]const u8, | 45 | entry_name: ?[]const u8, |
| | 46 | /// When true, will allow undefined symbols |
| 41 | import_symbols: bool, | 47 | import_symbols: bool, |
| | 48 | /// List of *global* symbol names to export to the host environment. |
| 42 | export_symbol_names: []const []const u8, | 49 | export_symbol_names: []const []const u8, |
| | 50 | /// When defined, sets the start of the data section. |
| 43 | global_base: ?u64, | 51 | global_base: ?u64, |
| | 52 | /// When defined, sets the initial memory size of the memory. |
| 44 | initial_memory: ?u64, | 53 | initial_memory: ?u64, |
| | 54 | /// When defined, sets the maximum memory size of the memory. |
| 45 | max_memory: ?u64, | 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 | /// Output name of the file | 60 | /// Output name of the file |
| 47 | name: []const u8, | 61 | name: []const u8, |
| 48 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 62 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 49 | llvm_object: ?*LlvmObject = null, | 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 | /// When importing objects from the host environment, a name must be supplied. | 69 | /// When importing objects from the host environment, a name must be supplied. |
| 51 | /// LLVM uses "env" by default when none is given. This would be a good default for Zig | 70 | /// LLVM uses "env" by default when none is given. This would be a good default for Zig |
| 52 | /// to support existing code. | 71 | /// to support existing code. |
| 53 | /// TODO: Allow setting this through a flag? | 72 | /// TODO: Allow setting this through a flag? |
| 54 | host_name: []const u8 = "env", | 73 | host_name: []const u8 = "env", |
| 55 | /// List of all `Decl` that are currently alive. | 74 | /// List of symbols generated by the linker. |
| 56 | /// Each index maps to the corresponding `Atom.Index`. | 75 | synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 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) = .{}, | | |
| 65 | /// Maps atoms to their segment index | 76 | /// Maps atoms to their segment index |
| 66 | atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, | 77 | atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, |
| 67 | /// List of all atoms. | 78 | /// List of all atoms. |
| ... | @@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, | ... | @@ -107,8 +118,6 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 107 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, | 118 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, |
| 108 | /// Deduplicated string table for strings used by symbols, imports and exports. | 119 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 109 | string_table: StringTable = .{}, | 120 | string_table: StringTable = .{}, |
| 110 | /// Debug information for wasm | | |
| 111 | dwarf: ?Dwarf = null, | | |
| 112 | | 121 | |
| 113 | // Output sections | 122 | // Output sections |
| 114 | /// Output type section | 123 | /// Output type section |
| ... | @@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, | ... | @@ -116,7 +125,10 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, |
| 116 | /// Output function section where the key is the original | 125 | /// Output function section where the key is the original |
| 117 | /// function index and the value is function. | 126 | /// function index and the value is function. |
| 118 | /// This allows us to map multiple symbols to the same function. | 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 | /// Output global section | 132 | /// Output global section |
| 121 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, | 133 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, |
| 122 | /// Memory section | 134 | /// Memory section |
| ... | @@ -143,7 +155,7 @@ entry: ?u32 = null, | ... | @@ -143,7 +155,7 @@ entry: ?u32 = null, |
| 143 | function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, | 155 | function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 144 | | 156 | |
| 145 | /// All object files and their data which are linked into the final binary | 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 | /// All archive files that are lazy loaded. | 159 | /// All archive files that are lazy loaded. |
| 148 | /// e.g. when an undefined symbol references a symbol from the archive. | 160 | /// e.g. when an undefined symbol references a symbol from the archive. |
| 149 | archives: std.ArrayListUnmanaged(Archive) = .{}, | 161 | archives: std.ArrayListUnmanaged(Archive) = .{}, |
| ... | @@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{}, | ... | @@ -165,40 +177,6 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{}, |
| 165 | /// data of a symbol, such as its size, or its offset to perform a relocation. | 177 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 166 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. | 178 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. |
| 167 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{}, | 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 | pub const Alignment = types.Alignment; | 181 | pub const Alignment = types.Alignment; |
| 204 | | 182 | |
| ... | @@ -226,39 +204,33 @@ pub const Segment = struct { | ... | @@ -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 | pub const SymbolLoc = struct { | 207 | pub const SymbolLoc = struct { |
| 234 | /// The index of the symbol within the specified file | 208 | /// The index of the symbol within the specified file |
| 235 | index: u32, | 209 | index: Symbol.Index, |
| 236 | /// The index of the object file where the symbol resides. | 210 | /// The index of the object file where the symbol resides. |
| 237 | /// When this is `null` the symbol comes from a non-object file. | 211 | file: File.Index, |
| 238 | file: ?u16, | | |
| 239 | | 212 | |
| 240 | /// From a given location, returns the corresponding symbol in the wasm binary | 213 | /// From a given location, returns the corresponding symbol in the wasm binary |
| 241 | pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol { | 214 | pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol { |
| 242 | if (wasm_bin.discarded.get(loc)) |new_loc| { | 215 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 243 | return new_loc.getSymbol(wasm_bin); | 216 | return new_loc.getSymbol(wasm_file); |
| 244 | } | 217 | } |
| 245 | if (loc.file) |object_index| { | 218 | if (wasm_file.file(loc.file)) |obj_file| { |
| 246 | const object = wasm_bin.objects.items[object_index]; | 219 | return obj_file.symbol(loc.index); |
| 247 | return &object.symtable[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 | /// From a given location, returns the name of the symbol. | 224 | /// From a given location, returns the name of the symbol. |
| 253 | pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 { | 225 | pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 { |
| 254 | if (wasm_bin.discarded.get(loc)) |new_loc| { | 226 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 255 | return new_loc.getName(wasm_bin); | 227 | return new_loc.getName(wasm_file); |
| 256 | } | 228 | } |
| 257 | if (loc.file) |object_index| { | 229 | if (wasm_file.file(loc.file)) |obj_file| { |
| 258 | const object = wasm_bin.objects.items[object_index]; | 230 | return obj_file.symbolName(loc.index); |
| 259 | return object.string_table.get(object.symtable[loc.index].name); | | |
| 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 | /// From a given symbol location, returns the final location. | 236 | /// From a given symbol location, returns the final location. |
| ... | @@ -266,9 +238,9 @@ pub const SymbolLoc = struct { | ... | @@ -266,9 +238,9 @@ pub const SymbolLoc = struct { |
| 266 | /// in a different file, this will return said location. | 238 | /// in a different file, this will return said location. |
| 267 | /// If the symbol wasn't replaced by another, this will return | 239 | /// If the symbol wasn't replaced by another, this will return |
| 268 | /// the given location itwasm. | 240 | /// the given location itwasm. |
| 269 | pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { | 241 | pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc { |
| 270 | if (wasm_bin.discarded.get(loc)) |new_loc| { | 242 | if (wasm_file.discarded.get(loc)) |new_loc| { |
| 271 | return new_loc.finalLoc(wasm_bin); | 243 | return new_loc.finalLoc(wasm_file); |
| 272 | } | 244 | } |
| 273 | return loc; | 245 | return loc; |
| 274 | } | 246 | } |
| ... | @@ -280,9 +252,9 @@ pub const InitFuncLoc = struct { | ... | @@ -280,9 +252,9 @@ pub const InitFuncLoc = struct { |
| 280 | /// object file index in the list of objects. | 252 | /// object file index in the list of objects. |
| 281 | /// Unlike `SymbolLoc` this cannot be `null` as we never define | 253 | /// Unlike `SymbolLoc` this cannot be `null` as we never define |
| 282 | /// our own ctors. | 254 | /// our own ctors. |
| 283 | file: u16, | 255 | file: File.Index, |
| 284 | /// Symbol index within the corresponding object file. | 256 | /// Symbol index within the corresponding object file. |
| 285 | index: u32, | 257 | index: Symbol.Index, |
| 286 | /// The priority in which the constructor must be called. | 258 | /// The priority in which the constructor must be called. |
| 287 | priority: u32, | 259 | priority: u32, |
| 288 | | 260 | |
| ... | @@ -459,7 +431,7 @@ pub fn createEmpty( | ... | @@ -459,7 +431,7 @@ pub fn createEmpty( |
| 459 | // can be passed to LLD. | 431 | // can be passed to LLD. |
| 460 | const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; | 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 | .truncate = true, | 435 | .truncate = true, |
| 464 | .read = true, | 436 | .read = true, |
| 465 | .mode = if (fs.has_executable_bit) | 437 | .mode = if (fs.has_executable_bit) |
| ... | @@ -470,7 +442,6 @@ pub fn createEmpty( | ... | @@ -470,7 +442,6 @@ pub fn createEmpty( |
| 470 | else | 442 | else |
| 471 | 0, | 443 | 0, |
| 472 | }); | 444 | }); |
| 473 | wasm.base.file = file; | | |
| 474 | wasm.name = sub_path; | 445 | wasm.name = sub_path; |
| 475 | | 446 | |
| 476 | // create stack pointer symbol | 447 | // create stack pointer symbol |
| ... | @@ -550,6 +521,7 @@ pub fn createEmpty( | ... | @@ -550,6 +521,7 @@ pub fn createEmpty( |
| 550 | const symbol = loc.getSymbol(wasm); | 521 | const symbol = loc.getSymbol(wasm); |
| 551 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 522 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 552 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | 523 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| | 524 | symbol.mark(); |
| 553 | try wasm.wasm_globals.append(gpa, .{ | 525 | try wasm.wasm_globals.append(gpa, .{ |
| 554 | .global_type = .{ .valtype = .i32, .mutable = true }, | 526 | .global_type = .{ .valtype = .i32, .mutable = true }, |
| 555 | .init = .{ .i32_const = undefined }, | 527 | .init = .{ .i32_const = undefined }, |
| ... | @@ -560,6 +532,7 @@ pub fn createEmpty( | ... | @@ -560,6 +532,7 @@ pub fn createEmpty( |
| 560 | const symbol = loc.getSymbol(wasm); | 532 | const symbol = loc.getSymbol(wasm); |
| 561 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 533 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 562 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | 534 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| | 535 | symbol.mark(); |
| 563 | try wasm.wasm_globals.append(gpa, .{ | 536 | try wasm.wasm_globals.append(gpa, .{ |
| 564 | .global_type = .{ .valtype = .i32, .mutable = false }, | 537 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| 565 | .init = .{ .i32_const = undefined }, | 538 | .init = .{ .i32_const = undefined }, |
| ... | @@ -570,6 +543,7 @@ pub fn createEmpty( | ... | @@ -570,6 +543,7 @@ pub fn createEmpty( |
| 570 | const symbol = loc.getSymbol(wasm); | 543 | const symbol = loc.getSymbol(wasm); |
| 571 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 544 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 572 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | 545 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| | 546 | symbol.mark(); |
| 573 | try wasm.wasm_globals.append(gpa, .{ | 547 | try wasm.wasm_globals.append(gpa, .{ |
| 574 | .global_type = .{ .valtype = .i32, .mutable = false }, | 548 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| 575 | .init = .{ .i32_const = undefined }, | 549 | .init = .{ .i32_const = undefined }, |
| ... | @@ -582,9 +556,64 @@ pub fn createEmpty( | ... | @@ -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 | return wasm; | 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 | /// For a given name, creates a new global synthetic symbol. | 617 | /// For a given name, creates a new global synthetic symbol. |
| 589 | /// Leaves index undefined and the default flags (0). | 618 | /// Leaves index undefined and the default flags (0). |
| 590 | fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc { | 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,10 +623,10 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 594 | } | 623 | } |
| 595 | | 624 | |
| 596 | fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc { | 625 | fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc { |
| 597 | const sym_index = @as(u32, @intCast(wasm.symbols.items.len)); | 626 | const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len); |
| 598 | const loc: SymbolLoc = .{ .index = sym_index, .file = null }; | 627 | const loc: SymbolLoc = .{ .index = sym_index, .file = .null }; |
| 599 | const gpa = wasm.base.comp.gpa; | 628 | const gpa = wasm.base.comp.gpa; |
| 600 | try wasm.symbols.append(gpa, .{ | 629 | try wasm.synthetic_symbols.append(gpa, .{ |
| 601 | .name = name_offset, | 630 | .name = name_offset, |
| 602 | .flags = 0, | 631 | .flags = 0, |
| 603 | .tag = tag, | 632 | .tag = tag, |
| ... | @@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) ! | ... | @@ -609,24 +638,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) ! |
| 609 | return loc; | 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 | fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { | 641 | fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { |
| 631 | for (files) |path| { | 642 | for (files) |path| { |
| 632 | if (try wasm.parseObjectFile(path)) continue; | 643 | if (try wasm.parseObjectFile(path)) continue; |
| ... | @@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { | ... | @@ -639,56 +650,43 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { |
| 639 | /// file and parsed successfully. Returns false when file is not an object file. | 650 | /// file and parsed successfully. Returns false when file is not an object file. |
| 640 | /// May return an error instead when parsing failed. | 651 | /// May return an error instead when parsing failed. |
| 641 | fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { | 652 | fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { |
| 642 | const file = try fs.cwd().openFile(path, .{}); | 653 | const obj_file = try fs.cwd().openFile(path, .{}); |
| 643 | errdefer file.close(); | 654 | errdefer obj_file.close(); |
| 644 | | 655 | |
| 645 | const gpa = wasm.base.comp.gpa; | 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 | error.InvalidMagicByte, error.NotObjectFile => return false, | 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 | errdefer object.deinit(gpa); | 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 | return true; | 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 | /// Creates a new empty `Atom` and returns its `Atom.Index` | 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 | const gpa = wasm.base.comp.gpa; | 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 | const atom = try wasm.managed_atoms.addOne(gpa); | 677 | const atom = try wasm.managed_atoms.addOne(gpa); |
| 679 | atom.* = Atom.empty; | 678 | atom.* = .{ .file = file_index, .sym_index = sym_index }; |
| 680 | atom.sym_index = try wasm.allocateSymbol(); | 679 | try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index); |
| 681 | try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index); | | |
| 682 | | 680 | |
| 683 | return index; | 681 | return index; |
| 684 | } | 682 | } |
| 685 | | 683 | |
| 686 | pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { | 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 | pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { | 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 | /// Parses an archive file and will then parse each object file | 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,11 +700,11 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { |
| 702 | fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { | 700 | fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 703 | const gpa = wasm.base.comp.gpa; | 701 | const gpa = wasm.base.comp.gpa; |
| 704 | | 702 | |
| 705 | const file = try fs.cwd().openFile(path, .{}); | 703 | const archive_file = try fs.cwd().openFile(path, .{}); |
| 706 | errdefer file.close(); | 704 | errdefer archive_file.close(); |
| 707 | | 705 | |
| 708 | var archive: Archive = .{ | 706 | var archive: Archive = .{ |
| 709 | .file = file, | 707 | .file = archive_file, |
| 710 | .name = path, | 708 | .name = path, |
| 711 | }; | 709 | }; |
| 712 | archive.parse(gpa) catch |err| switch (err) { | 710 | archive.parse(gpa) catch |err| switch (err) { |
| ... | @@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { | ... | @@ -714,7 +712,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 714 | archive.deinit(gpa); | 712 | archive.deinit(gpa); |
| 715 | return false; | 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 | if (!force_load) { | 723 | if (!force_load) { |
| ... | @@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { | ... | @@ -736,8 +739,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 736 | } | 739 | } |
| 737 | | 740 | |
| 738 | for (offsets.keys()) |file_offset| { | 741 | for (offsets.keys()) |file_offset| { |
| 739 | const object = try wasm.objects.addOne(gpa); | 742 | var object = archive.parseObject(wasm, file_offset) catch |e| { |
| 740 | object.* = try archive.parseObject(gpa, file_offset); | 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 | return true; | 753 | return true; |
| ... | @@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool { | ... | @@ -752,18 +762,15 @@ fn requiresTLSReloc(wasm: *const Wasm) bool { |
| 752 | return false; | 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 | const gpa = wasm.base.comp.gpa; | 766 | const gpa = wasm.base.comp.gpa; |
| 757 | const object: Object = wasm.objects.items[object_index]; | 767 | const obj_file = wasm.file(file_index).?; |
| 758 | log.debug("Resolving symbols in object: '{s}'", .{object.name}); | 768 | log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()}); |
| 759 | | 769 | |
| 760 | for (object.symtable, 0..) |symbol, i| { | 770 | for (obj_file.symbols(), 0..) |symbol, i| { |
| 761 | const sym_index = @as(u32, @intCast(i)); | 771 | const sym_index: Symbol.Index = @enumFromInt(i); |
| 762 | const location: SymbolLoc = .{ | 772 | const location: SymbolLoc = .{ .file = file_index, .index = sym_index }; |
| 763 | .file = object_index, | 773 | const sym_name = obj_file.string(symbol.name); |
| 764 | .index = sym_index, | | |
| 765 | }; | | |
| 766 | const sym_name = object.string_table.get(symbol.name); | | |
| 767 | if (mem.eql(u8, sym_name, "__indirect_function_table")) { | 774 | if (mem.eql(u8, sym_name, "__indirect_function_table")) { |
| 768 | continue; | 775 | continue; |
| 769 | } | 776 | } |
| ... | @@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -771,9 +778,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 771 | | 778 | |
| 772 | if (symbol.isLocal()) { | 779 | if (symbol.isLocal()) { |
| 773 | if (symbol.isUndefined()) { | 780 | if (symbol.isUndefined()) { |
| 774 | log.err("Local symbols are not allowed to reference imports", .{}); | 781 | var err = try wasm.addErrorWithNotes(1); |
| 775 | log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name }); | 782 | try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{}); |
| 776 | return error.UndefinedLocal; | 783 | try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() }); |
| 777 | } | 784 | } |
| 778 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); | 785 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); |
| 779 | continue; | 786 | continue; |
| ... | @@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -792,10 +799,12 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 792 | | 799 | |
| 793 | const existing_loc = maybe_existing.value_ptr.*; | 800 | const existing_loc = maybe_existing.value_ptr.*; |
| 794 | const existing_sym: *Symbol = existing_loc.getSymbol(wasm); | 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: { | 804 | const existing_file_path = if (existing_file) |existing_obj_file| |
| 797 | break :blk wasm.objects.items[file].name; | 805 | existing_obj_file.path() |
| 798 | } else wasm.name; | 806 | else |
| | 807 | wasm.name; |
| 799 | | 808 | |
| 800 | if (!existing_sym.isUndefined()) outer: { | 809 | if (!existing_sym.isUndefined()) outer: { |
| 801 | if (!symbol.isUndefined()) inner: { | 810 | if (!symbol.isUndefined()) inner: { |
| ... | @@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -806,10 +815,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 806 | break :outer; // existing is weak, while new one isn't. Replace it. | 815 | break :outer; // existing is weak, while new one isn't. Replace it. |
| 807 | } | 816 | } |
| 808 | // both are defined and weak, we have a symbol collision. | 817 | // both are defined and weak, we have a symbol collision. |
| 809 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | 818 | var err = try wasm.addErrorWithNotes(2); |
| 810 | log.err(" first definition in '{s}'", .{existing_file_path}); | 819 | try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name}); |
| 811 | log.err(" next definition in '{s}'", .{object.name}); | 820 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 812 | return error.SymbolCollision; | 821 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 813 | } | 822 | } |
| 814 | | 823 | |
| 815 | try wasm.discarded.put(gpa, location, existing_loc); | 824 | try wasm.discarded.put(gpa, location, existing_loc); |
| ... | @@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -817,35 +826,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 817 | } | 826 | } |
| 818 | | 827 | |
| 819 | if (symbol.tag != existing_sym.tag) { | 828 | if (symbol.tag != existing_sym.tag) { |
| 820 | log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) }); | 829 | var err = try wasm.addErrorWithNotes(2); |
| 821 | log.err(" first definition in '{s}'", .{existing_file_path}); | 830 | try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) }); |
| 822 | log.err(" next definition in '{s}'", .{object.name}); | 831 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 823 | return error.SymbolMismatchingType; | 832 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 824 | } | 833 | } |
| 825 | | 834 | |
| 826 | if (existing_sym.isUndefined() and symbol.isUndefined()) { | 835 | if (existing_sym.isUndefined() and symbol.isUndefined()) { |
| 827 | // only verify module/import name for function symbols | 836 | // only verify module/import name for function symbols |
| 828 | if (symbol.tag == .function) { | 837 | if (symbol.tag == .function) { |
| 829 | const existing_name = if (existing_loc.file) |file_index| blk: { | 838 | const existing_name = if (existing_file) |existing_obj| blk: { |
| 830 | const obj = wasm.objects.items[file_index]; | 839 | const imp = existing_obj.import(existing_loc.index); |
| 831 | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; | 840 | break :blk existing_obj.string(imp.module_name); |
| 832 | break :blk obj.string_table.get(name_index); | | |
| 833 | } else blk: { | 841 | } else blk: { |
| 834 | const name_index = wasm.imports.get(existing_loc).?.module_name; | 842 | const name_index = wasm.imports.get(existing_loc).?.module_name; |
| 835 | break :blk wasm.string_table.get(name_index); | 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; | 846 | const imp = obj_file.import(sym_index); |
| 839 | const module_name = object.string_table.get(module_index); | 847 | const module_name = obj_file.string(imp.module_name); |
| 840 | if (!mem.eql(u8, existing_name, module_name)) { | 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 | sym_name, | 851 | sym_name, |
| 843 | existing_name, | 852 | existing_name, |
| 844 | module_name, | 853 | module_name, |
| 845 | }); | 854 | }); |
| 846 | log.err(" first definition in '{s}'", .{existing_file_path}); | 855 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 847 | log.err(" next definition in '{s}'", .{object.name}); | 856 | try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()}); |
| 848 | return error.ModuleNameMismatch; | | |
| 849 | } | 857 | } |
| 850 | } | 858 | } |
| 851 | | 859 | |
| ... | @@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -858,10 +866,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 858 | const existing_ty = wasm.getGlobalType(existing_loc); | 866 | const existing_ty = wasm.getGlobalType(existing_loc); |
| 859 | const new_ty = wasm.getGlobalType(location); | 867 | const new_ty = wasm.getGlobalType(location); |
| 860 | if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) { | 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}); | 869 | var err = try wasm.addErrorWithNotes(2); |
| 862 | log.err(" first definition in '{s}'", .{existing_file_path}); | 870 | try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name}); |
| 863 | log.err(" next definition in '{s}'", .{object.name}); | 871 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 864 | return error.GlobalTypeMismatch; | 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,11 +877,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 869 | const existing_ty = wasm.getFunctionSignature(existing_loc); | 877 | const existing_ty = wasm.getFunctionSignature(existing_loc); |
| 870 | const new_ty = wasm.getFunctionSignature(location); | 878 | const new_ty = wasm.getFunctionSignature(location); |
| 871 | if (!existing_ty.eql(new_ty)) { | 879 | if (!existing_ty.eql(new_ty)) { |
| 872 | log.err("symbol '{s}' mismatching function signatures.", .{sym_name}); | 880 | var err = try wasm.addErrorWithNotes(3); |
| 873 | log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty }); | 881 | try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name}); |
| 874 | log.err(" first definition in '{s}'", .{existing_file_path}); | 882 | try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty }); |
| 875 | log.err(" next definition in '{s}'", .{object.name}); | 883 | try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path}); |
| 876 | return error.FunctionSignatureMismatch; | 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,7 +896,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 888 | // simply overwrite with the new symbol | 896 | // simply overwrite with the new symbol |
| 889 | log.debug("Overwriting symbol '{s}'", .{sym_name}); | 897 | log.debug("Overwriting symbol '{s}'", .{sym_name}); |
| 890 | log.debug(" old definition in '{s}'", .{existing_file_path}); | 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 | try wasm.discarded.putNoClobber(gpa, existing_loc, location); | 900 | try wasm.discarded.putNoClobber(gpa, existing_loc, location); |
| 893 | maybe_existing.value_ptr.* = location; | 901 | maybe_existing.value_ptr.* = location; |
| 894 | try wasm.globals.put(gpa, sym_name_index, location); | 902 | try wasm.globals.put(gpa, sym_name_index, location); |
| ... | @@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { | ... | @@ -920,10 +928,16 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 920 | // Symbol is found in unparsed object file within current archive. | 928 | // Symbol is found in unparsed object file within current archive. |
| 921 | // Parse object and and resolve symbols again before we check remaining | 929 | // Parse object and and resolve symbols again before we check remaining |
| 922 | // undefined symbols. | 930 | // undefined symbols. |
| 923 | const object_file_index: u16 = @intCast(wasm.objects.items.len); | 931 | var object = archive.parseObject(wasm, offset.items[0]) catch |e| { |
| 924 | const object = try archive.parseObject(gpa, offset.items[0]); | 932 | var err_note = try wasm.addErrorWithNotes(1); |
| 925 | try wasm.objects.append(gpa, object); | 933 | try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)}); |
| 926 | try wasm.resolveSymbolsInObject(object_file_index); | 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 | // continue loop for any remaining undefined symbols that still exist | 942 | // continue loop for any remaining undefined symbols that still exist |
| 929 | // after resolving last object file | 943 | // after resolving last object file |
| ... | @@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { | ... | @@ -953,6 +967,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 953 | if (!wasm.hasPassiveInitializationSegments()) { | 967 | if (!wasm.hasPassiveInitializationSegments()) { |
| 954 | return; | 968 | return; |
| 955 | } | 969 | } |
| | 970 | const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function); |
| | 971 | sym_loc.getSymbol(wasm).mark(); |
| 956 | | 972 | |
| 957 | const flag_address: u32 = if (shared_memory) address: { | 973 | const flag_address: u32 = if (shared_memory) address: { |
| 958 | // when we have passive initialization segments and shared memory | 974 | // when we have passive initialization segments and shared memory |
| ... | @@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { | ... | @@ -1115,7 +1131,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { |
| 1115 | return; | 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 | var function_body = std.ArrayList(u8).init(gpa); | 1136 | var function_body = std.ArrayList(u8).init(gpa); |
| 1120 | defer function_body.deinit(); | 1137 | defer function_body.deinit(); |
| 1121 | const writer = function_body.writer(); | 1138 | const writer = function_body.writer(); |
| ... | @@ -1185,9 +1202,10 @@ fn validateFeatures( | ... | @@ -1185,9 +1202,10 @@ fn validateFeatures( |
| 1185 | | 1202 | |
| 1186 | // extract all the used, disallowed and required features from each | 1203 | // extract all the used, disallowed and required features from each |
| 1187 | // linked object file so we can test them. | 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 | for (object.features) |feature| { | 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 | switch (feature.prefix) { | 1209 | switch (feature.prefix) { |
| 1192 | .used => { | 1210 | .used => { |
| 1193 | used[@intFromEnum(feature.tag)] = value; | 1211 | used[@intFromEnum(feature.tag)] = value; |
| ... | @@ -1218,29 +1236,30 @@ fn validateFeatures( | ... | @@ -1218,29 +1236,30 @@ fn validateFeatures( |
| 1218 | allowed[used_index] = is_enabled; | 1236 | allowed[used_index] = is_enabled; |
| 1219 | emit_features_count.* += @intFromBool(is_enabled); | 1237 | emit_features_count.* += @intFromBool(is_enabled); |
| 1220 | } else if (is_enabled and !allowed[used_index]) { | 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))}); | 1239 | var err = try wasm.addErrorWithNotes(1); |
| 1222 | log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name}); | 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 | valid_feature_set = false; | 1242 | valid_feature_set = false; |
| 1224 | } | 1243 | } |
| 1225 | } | 1244 | } |
| 1226 | | 1245 | |
| 1227 | if (!valid_feature_set) { | 1246 | if (!valid_feature_set) { |
| 1228 | return error.InvalidFeatureSet; | 1247 | return error.FlushFailure; |
| 1229 | } | 1248 | } |
| 1230 | | 1249 | |
| 1231 | if (shared_memory) { | 1250 | if (shared_memory) { |
| 1232 | const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; | 1251 | const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; |
| 1233 | if (@as(u1, @truncate(disallowed_feature)) != 0) { | 1252 | if (@as(u1, @truncate(disallowed_feature)) != 0) { |
| 1234 | log.err( | 1253 | try wasm.addErrorWithoutNotes( |
| 1235 | "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", | 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 | valid_feature_set = false; | 1257 | valid_feature_set = false; |
| 1239 | } | 1258 | } |
| 1240 | | 1259 | |
| 1241 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { | 1260 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 1242 | if (!allowed[@intFromEnum(feature)]) { | 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,21 +1267,23 @@ fn validateFeatures( |
| 1248 | if (has_tls) { | 1267 | if (has_tls) { |
| 1249 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { | 1268 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 1250 | if (!allowed[@intFromEnum(feature)]) { | 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 | // For each linked object, validate the required and disallowed features | 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 | var object_used_features = [_]bool{false} ** known_features_count; | 1276 | var object_used_features = [_]bool{false} ** known_features_count; |
| | 1277 | const object = wasm.files.items(.data)[@intFromEnum(file_index)].object; |
| 1258 | for (object.features) |feature| { | 1278 | for (object.features) |feature| { |
| 1259 | if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set. | 1279 | if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set. |
| 1260 | // from here a feature is always used | 1280 | // from here a feature is always used |
| 1261 | const disallowed_feature = disallowed[@intFromEnum(feature.tag)]; | 1281 | const disallowed_feature = disallowed[@intFromEnum(feature.tag)]; |
| 1262 | if (@as(u1, @truncate(disallowed_feature)) != 0) { | 1282 | if (@as(u1, @truncate(disallowed_feature)) != 0) { |
| 1263 | log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag}); | 1283 | var err = try wasm.addErrorWithNotes(2); |
| 1264 | log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name}); | 1284 | try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag}); |
| 1265 | log.err(" used in '{s}'", .{object.name}); | 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 | valid_feature_set = false; | 1287 | valid_feature_set = false; |
| 1267 | } | 1288 | } |
| 1268 | | 1289 | |
| ... | @@ -1273,16 +1294,17 @@ fn validateFeatures( | ... | @@ -1273,16 +1294,17 @@ fn validateFeatures( |
| 1273 | for (required, 0..) |required_feature, feature_index| { | 1294 | for (required, 0..) |required_feature, feature_index| { |
| 1274 | const is_required = @as(u1, @truncate(required_feature)) != 0; | 1295 | const is_required = @as(u1, @truncate(required_feature)) != 0; |
| 1275 | if (is_required and !object_used_features[feature_index]) { | 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))}); | 1297 | var err = try wasm.addErrorWithNotes(2); |
| 1277 | log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name}); | 1298 | try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))}); |
| 1278 | log.err(" missing in '{s}'", .{object.name}); | 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 | valid_feature_set = false; | 1301 | valid_feature_set = false; |
| 1280 | } | 1302 | } |
| 1281 | } | 1303 | } |
| 1282 | } | 1304 | } |
| 1283 | | 1305 | |
| 1284 | if (!valid_feature_set) { | 1306 | if (!valid_feature_set) { |
| 1285 | return error.InvalidFeatureSet; | 1307 | return error.FlushFailure; |
| 1286 | } | 1308 | } |
| 1287 | | 1309 | |
| 1288 | to_emit.* = allowed; | 1310 | to_emit.* = allowed; |
| ... | @@ -1329,13 +1351,6 @@ fn resolveLazySymbols(wasm: *Wasm) !void { | ... | @@ -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 | // Tries to find a global symbol by its name. Returns null when not found, | 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,16 +1370,18 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 1355 | const symbol = undef.getSymbol(wasm); | 1370 | const symbol = undef.getSymbol(wasm); |
| 1356 | if (symbol.tag == .data) { | 1371 | if (symbol.tag == .data) { |
| 1357 | found_undefined_symbols = true; | 1372 | found_undefined_symbols = true; |
| 1358 | const file_name = if (undef.file) |file_index| name: { | 1373 | const file_name = if (wasm.file(undef.file)) |obj_file| |
| 1359 | break :name wasm.objects.items[file_index].name; | 1374 | obj_file.path() |
| 1360 | } else wasm.name; | 1375 | else |
| | 1376 | wasm.name; |
| 1361 | const symbol_name = undef.getName(wasm); | 1377 | const symbol_name = undef.getName(wasm); |
| 1362 | log.err("could not resolve undefined symbol '{s}'", .{symbol_name}); | 1378 | var err = try wasm.addErrorWithNotes(1); |
| 1363 | log.err(" defined in '{s}'", .{file_name}); | 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 | if (found_undefined_symbols) { | 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,54 +1395,28 @@ pub fn deinit(wasm: *Wasm) void { |
| 1378 | for (wasm.segment_info.values()) |segment_info| { | 1395 | for (wasm.segment_info.values()) |segment_info| { |
| 1379 | gpa.free(segment_info.name); | 1396 | gpa.free(segment_info.name); |
| 1380 | } | 1397 | } |
| 1381 | for (wasm.objects.items) |*object| { | 1398 | if (wasm.zigObjectPtr()) |zig_obj| { |
| 1382 | object.deinit(gpa); | 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 | for (wasm.archives.items) |*archive| { | 1405 | for (wasm.archives.items) |*archive| { |
| 1386 | archive.deinit(gpa); | 1406 | archive.deinit(gpa); |
| 1387 | } | 1407 | } |
| 1388 | | 1408 | |
| 1389 | // For decls and anon decls we free the memory of its atoms. | 1409 | if (wasm.findGlobalSymbol("__wasm_init_tls")) |loc| { |
| 1390 | // The memory of atoms parsed from object files is managed by | 1410 | const atom = wasm.symbol_atom.get(loc).?; |
| 1391 | // the object file itself, and therefore we can skip those. | 1411 | wasm.getAtomPtr(atom).deinit(gpa); |
| 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); | | |
| 1416 | } | 1412 | } |
| 1417 | | 1413 | |
| 1418 | wasm.decls.deinit(gpa); | 1414 | wasm.synthetic_symbols.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); | | |
| 1423 | wasm.globals.deinit(gpa); | 1415 | wasm.globals.deinit(gpa); |
| 1424 | wasm.resolved_symbols.deinit(gpa); | 1416 | wasm.resolved_symbols.deinit(gpa); |
| 1425 | wasm.undefs.deinit(gpa); | 1417 | wasm.undefs.deinit(gpa); |
| 1426 | wasm.discarded.deinit(gpa); | 1418 | wasm.discarded.deinit(gpa); |
| 1427 | wasm.symbol_atom.deinit(gpa); | 1419 | wasm.symbol_atom.deinit(gpa); |
| 1428 | wasm.export_names.deinit(gpa); | | |
| 1429 | wasm.atoms.deinit(gpa); | 1420 | wasm.atoms.deinit(gpa); |
| 1430 | wasm.managed_atoms.deinit(gpa); | 1421 | wasm.managed_atoms.deinit(gpa); |
| 1431 | wasm.segments.deinit(gpa); | 1422 | wasm.segments.deinit(gpa); |
| ... | @@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -1445,33 +1436,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 1445 | wasm.exports.deinit(gpa); | 1436 | wasm.exports.deinit(gpa); |
| 1446 | | 1437 | |
| 1447 | wasm.string_table.deinit(gpa); | 1438 | wasm.string_table.deinit(gpa); |
| 1448 | wasm.synthetic_functions.deinit(gpa); | 1439 | wasm.files.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; | | |
| 1475 | } | 1440 | } |
| 1476 | | 1441 | |
| 1477 | pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 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,64 +1444,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: |
| 1479 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 1444 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1480 | } | 1445 | } |
| 1481 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness); | 1446 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness); |
| 1482 | | 1447 | try wasm.zigObjectPtr().?.updateFunc(wasm, mod, func_index, air, liveness); |
| 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); | | |
| 1540 | } | 1448 | } |
| 1541 | | 1449 | |
| 1542 | // Generate code for the Decl, storing it in memory to be later written to | 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,84 +1454,12 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) ! |
| 1546 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 1454 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1547 | } | 1455 | } |
| 1548 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); | 1456 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); |
| 1549 | | 1457 | try wasm.zigObjectPtr().?.updateDecl(wasm, mod, decl_index); |
| 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); | | |
| 1595 | } | 1458 | } |
| 1596 | | 1459 | |
| 1597 | pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void { | 1460 | pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void { |
| 1598 | if (wasm.llvm_object) |_| return; | 1461 | if (wasm.llvm_object) |_| return; |
| 1599 | if (wasm.dwarf) |*dw| { | 1462 | try wasm.zigObjectPtr().?.updateDeclLineNumber(mod, decl_index); |
| 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); | | |
| 1627 | } | 1463 | } |
| 1628 | | 1464 | |
| 1629 | /// From a given symbol location, returns its `wasm.GlobalType`. | 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,13 +1468,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { |
| 1632 | const symbol = loc.getSymbol(wasm); | 1468 | const symbol = loc.getSymbol(wasm); |
| 1633 | assert(symbol.tag == .global); | 1469 | assert(symbol.tag == .global); |
| 1634 | const is_undefined = symbol.isUndefined(); | 1470 | const is_undefined = symbol.isUndefined(); |
| 1635 | if (loc.file) |file_index| { | 1471 | if (wasm.file(loc.file)) |obj_file| { |
| 1636 | const obj: Object = wasm.objects.items[file_index]; | | |
| 1637 | if (is_undefined) { | 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); | 1475 | return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type; |
| 1641 | return obj.globals[symbol.index - import_global_count].global_type; | | |
| 1642 | } | 1476 | } |
| 1643 | if (is_undefined) { | 1477 | if (is_undefined) { |
| 1644 | return wasm.imports.get(loc).?.kind.global; | 1478 | return wasm.imports.get(loc).?.kind.global; |
| ... | @@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { | ... | @@ -1652,15 +1486,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { |
| 1652 | const symbol = loc.getSymbol(wasm); | 1486 | const symbol = loc.getSymbol(wasm); |
| 1653 | assert(symbol.tag == .function); | 1487 | assert(symbol.tag == .function); |
| 1654 | const is_undefined = symbol.isUndefined(); | 1488 | const is_undefined = symbol.isUndefined(); |
| 1655 | if (loc.file) |file_index| { | 1489 | if (wasm.file(loc.file)) |obj_file| { |
| 1656 | const obj: Object = wasm.objects.items[file_index]; | | |
| 1657 | if (is_undefined) { | 1490 | if (is_undefined) { |
| 1658 | const ty_index = obj.findImport(.function, symbol.index).kind.function; | 1491 | const ty_index = obj_file.import(loc.index).kind.function; |
| 1659 | return obj.func_types[ty_index]; | 1492 | return obj_file.funcTypes()[ty_index]; |
| 1660 | } | 1493 | } |
| 1661 | const import_function_count = obj.importedCountByKind(.function); | 1494 | const type_index = obj_file.function(loc.index).type_index; |
| 1662 | const type_index = obj.functions[symbol.index - import_function_count].type_index; | 1495 | return obj_file.funcTypes()[type_index]; |
| 1663 | return obj.func_types[type_index]; | | |
| 1664 | } | 1496 | } |
| 1665 | if (is_undefined) { | 1497 | if (is_undefined) { |
| 1666 | const ty_index = wasm.imports.get(loc).?.kind.function; | 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,118 +1505,16 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { |
| 1673 | /// Returns the symbol index of the local | 1505 | /// Returns the symbol index of the local |
| 1674 | /// The given `decl` is the parent decl whom owns the constant. | 1506 | /// The given `decl` is the parent decl whom owns the constant. |
| 1675 | pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { | 1507 | pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { |
| 1676 | const gpa = wasm.base.comp.gpa; | 1508 | return wasm.zigObjectPtr().?.lowerUnnamedConst(wasm, tv, decl_index); |
| 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 }; | | |
| 1752 | } | 1509 | } |
| 1753 | | 1510 | |
| 1754 | /// Returns the symbol index from a symbol of which its flag is set global, | 1511 | /// Returns the symbol index from a symbol of which its flag is set global, |
| 1755 | /// such as an exported or imported symbol. | 1512 | /// such as an exported or imported symbol. |
| 1756 | /// If the symbol does not yet exist, creates a new one symbol instead | 1513 | /// If the symbol does not yet exist, creates a new one symbol instead |
| 1757 | /// and then returns the index to it. | 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 | _ = lib_name; | 1516 | _ = lib_name; |
| 1760 | const gpa = wasm.base.comp.gpa; | 1517 | return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name); |
| 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; | | |
| 1788 | } | 1518 | } |
| 1789 | | 1519 | |
| 1790 | /// For a given decl, find the given symbol index's atom, and create a relocation for the type. | 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,42 +1524,7 @@ pub fn getDeclVAddr( |
| 1794 | decl_index: InternPool.DeclIndex, | 1524 | decl_index: InternPool.DeclIndex, |
| 1795 | reloc_info: link.File.RelocInfo, | 1525 | reloc_info: link.File.RelocInfo, |
| 1796 | ) !u64 { | 1526 | ) !u64 { |
| 1797 | const target = wasm.base.comp.root_mod.resolved_target.result; | 1527 | return wasm.zigObjectPtr().?.getDeclVAddr(wasm, decl_index, reloc_info); |
| 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; | | |
| 1833 | } | 1528 | } |
| 1834 | | 1529 | |
| 1835 | pub fn lowerAnonDecl( | 1530 | pub fn lowerAnonDecl( |
| ... | @@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl( | ... | @@ -1838,70 +1533,11 @@ pub fn lowerAnonDecl( |
| 1838 | explicit_alignment: Alignment, | 1533 | explicit_alignment: Alignment, |
| 1839 | src_loc: Module.SrcLoc, | 1534 | src_loc: Module.SrcLoc, |
| 1840 | ) !codegen.Result { | 1535 | ) !codegen.Result { |
| 1841 | const gpa = wasm.base.comp.gpa; | 1536 | return wasm.zigObjectPtr().?.lowerAnonDecl(wasm, decl_val, explicit_alignment, src_loc); |
| 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; | | |
| 1867 | } | 1537 | } |
| 1868 | | 1538 | |
| 1869 | pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { | 1539 | pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { |
| 1870 | const gpa = wasm.base.comp.gpa; | 1540 | return wasm.zigObjectPtr().?.getAnonDeclVAddr(wasm, decl_val, reloc_info); |
| 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; | | |
| 1905 | } | 1541 | } |
| 1906 | | 1542 | |
| 1907 | pub fn deleteDeclExport( | 1543 | pub fn deleteDeclExport( |
| ... | @@ -1909,19 +1545,8 @@ pub fn deleteDeclExport( | ... | @@ -1909,19 +1545,8 @@ pub fn deleteDeclExport( |
| 1909 | decl_index: InternPool.DeclIndex, | 1545 | decl_index: InternPool.DeclIndex, |
| 1910 | name: InternPool.NullTerminatedString, | 1546 | name: InternPool.NullTerminatedString, |
| 1911 | ) void { | 1547 | ) void { |
| 1912 | _ = name; | | |
| 1913 | if (wasm.llvm_object) |_| return; | 1548 | if (wasm.llvm_object) |_| return; |
| 1914 | const atom_index = wasm.decls.get(decl_index) orelse return; | 1549 | return wasm.zigObjectPtr().?.deleteDeclExport(wasm, decl_index, name); |
| 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 | } | | |
| 1925 | } | 1550 | } |
| 1926 | | 1551 | |
| 1927 | pub fn updateExports( | 1552 | pub fn updateExports( |
| ... | @@ -1934,159 +1559,12 @@ pub fn updateExports( | ... | @@ -1934,159 +1559,12 @@ pub fn updateExports( |
| 1934 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 1559 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1935 | } | 1560 | } |
| 1936 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); | 1561 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); |
| 1937 | | 1562 | return wasm.zigObjectPtr().?.updateExports(wasm, mod, exported, exports); |
| 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 | } | | |
| 2051 | } | 1563 | } |
| 2052 | | 1564 | |
| 2053 | pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { | 1565 | pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { |
| 2054 | if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); | 1566 | if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); |
| 2055 | const gpa = wasm.base.comp.gpa; | 1567 | return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index); |
| 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); | | |
| 2090 | } | 1568 | } |
| 2091 | | 1569 | |
| 2092 | /// Assigns indexes to all indirect functions. | 1570 | /// Assigns indexes to all indirect functions. |
| ... | @@ -2118,203 +1596,6 @@ fn mapFunctionTable(wasm: *Wasm) void { | ... | @@ -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 | /// From a given index, append the given `Atom` at the back of the linked list. | 1599 | /// From a given index, append the given `Atom` at the back of the linked list. |
| 2319 | /// Simply inserts it into the map of atoms when it doesn't exist yet. | 1600 | /// Simply inserts it into the map of atoms when it doesn't exist yet. |
| 2320 | pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void { | 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,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 | fn allocateAtoms(wasm: *Wasm) !void { | 1612 | fn allocateAtoms(wasm: *Wasm) !void { |
| 2362 | // first sort the data segments | 1613 | // first sort the data segments |
| 2363 | try sortDataSegments(wasm); | 1614 | try sortDataSegments(wasm); |
| 2364 | try allocateDebugAtoms(wasm); | | |
| 2365 | | 1615 | |
| 2366 | var it = wasm.atoms.iterator(); | 1616 | var it = wasm.atoms.iterator(); |
| 2367 | while (it.next()) |entry| { | 1617 | while (it.next()) |entry| { |
| ... | @@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -2379,22 +1629,23 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 2379 | // Ensure we get the original symbol, so we verify the correct symbol on whether | 1629 | // Ensure we get the original symbol, so we verify the correct symbol on whether |
| 2380 | // it is dead or not and ensure an atom is removed when dead. | 1630 | // it is dead or not and ensure an atom is removed when dead. |
| 2381 | // This is required as we may have parsed aliases into atoms. | 1631 | // This is required as we may have parsed aliases into atoms. |
| 2382 | const sym = if (symbol_loc.file) |object_index| sym: { | 1632 | const sym = if (wasm.file(symbol_loc.file)) |obj_file| |
| 2383 | const object = wasm.objects.items[object_index]; | 1633 | obj_file.symbol(symbol_loc.index).* |
| 2384 | break :sym object.symtable[symbol_loc.index]; | 1634 | else |
| 2385 | } else wasm.symbols.items[symbol_loc.index]; | 1635 | wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)]; |
| 2386 | | 1636 | |
| 2387 | // Dead symbols must be unlinked from the linked-list to prevent them | 1637 | // Dead symbols must be unlinked from the linked-list to prevent them |
| 2388 | // from being emit into the binary. | 1638 | // from being emit into the binary. |
| 2389 | if (sym.isDead()) { | 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 | // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update | 1641 | // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update |
| 2392 | // the entry to point it to the previous atom to ensure we do not start with a dead symbol that | 1642 | // the entry to point it to the previous atom to ensure we do not start with a dead symbol that |
| 2393 | // was removed and therefore do not emit any code at all. | 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; | 1646 | if (atom.prev == .null) break; |
| 2397 | atom.prev = null; | 1647 | atom_index = atom.prev; |
| | 1648 | atom.prev = .null; |
| 2398 | continue; | 1649 | continue; |
| 2399 | } | 1650 | } |
| 2400 | offset = @intCast(atom.alignment.forward(offset)); | 1651 | offset = @intCast(atom.alignment.forward(offset)); |
| ... | @@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -2406,7 +1657,8 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 2406 | atom.size, | 1657 | atom.size, |
| 2407 | }); | 1658 | }); |
| 2408 | offset += atom.size; | 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 | segment.size = @intCast(segment.alignment.forward(offset)); | 1663 | segment.size = @intCast(segment.alignment.forward(offset)); |
| 2412 | } | 1664 | } |
| ... | @@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { | ... | @@ -2428,9 +1680,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { |
| 2428 | | 1680 | |
| 2429 | const atom = wasm.getAtom(atom_index); | 1681 | const atom = wasm.getAtom(atom_index); |
| 2430 | const merge_segment = wasm.base.comp.config.output_mode != .Obj; | 1682 | const merge_segment = wasm.base.comp.config.output_mode != .Obj; |
| 2431 | const segment_info = if (atom.file) |object_index| blk: { | 1683 | const segment_info = if (atom.file != .null) |
| 2432 | break :blk wasm.objects.items[object_index].segment_info; | 1684 | wasm.file(atom.file).?.segmentInfo() |
| 2433 | } else wasm.segment_info.values(); | 1685 | else |
| | 1686 | wasm.segment_info.values(); |
| 2434 | const segment_name = segment_info[symbol.index].outputName(merge_segment); | 1687 | const segment_name = segment_info[symbol.index].outputName(merge_segment); |
| 2435 | const segment_index = wasm.data_segments.get(segment_name).?; | 1688 | const segment_index = wasm.data_segments.get(segment_name).?; |
| 2436 | const segment = wasm.segments.items[segment_index]; | 1689 | const segment = wasm.segments.items[segment_index]; |
| ... | @@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -2486,29 +1739,30 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 2486 | /// contain any parameters. | 1739 | /// contain any parameters. |
| 2487 | fn setupInitFunctions(wasm: *Wasm) !void { | 1740 | fn setupInitFunctions(wasm: *Wasm) !void { |
| 2488 | const gpa = wasm.base.comp.gpa; | 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 | try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len); | 1745 | try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len); |
| 2491 | for (object.init_funcs) |init_func| { | 1746 | for (object.init_funcs) |init_func| { |
| 2492 | const symbol = object.symtable[init_func.symbol_index]; | 1747 | const symbol = object.symtable[init_func.symbol_index]; |
| 2493 | const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { | 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 | break :ty object.func_types[imp.kind.function]; | 1750 | break :ty object.func_types[imp.kind.function]; |
| 2496 | } else ty: { | 1751 | } else ty: { |
| 2497 | const func_index = symbol.index - object.importedCountByKind(.function); | 1752 | const func_index = symbol.index - object.imported_functions_count; |
| 2498 | const func = object.functions[func_index]; | 1753 | const func = object.functions[func_index]; |
| 2499 | break :ty object.func_types[func.type_index]; | 1754 | break :ty object.func_types[func.type_index]; |
| 2500 | }; | 1755 | }; |
| 2501 | if (ty.params.len != 0) { | 1756 | if (ty.params.len != 0) { |
| 2502 | log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); | 1757 | try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| 2503 | return error.InvalidInitFunc; | | |
| 2504 | } | 1758 | } |
| 2505 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); | 1759 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); |
| 2506 | wasm.init_funcs.appendAssumeCapacity(.{ | 1760 | wasm.init_funcs.appendAssumeCapacity(.{ |
| 2507 | .index = init_func.symbol_index, | 1761 | .index = @enumFromInt(init_func.symbol_index), |
| 2508 | .file = @as(u16, @intCast(file_index)), | 1762 | .file = file_index, |
| 2509 | .priority = init_func.priority, | 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,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 | /// Creates a function body for the `__wasm_call_ctors` symbol. | 1778 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| 2553 | /// Loops over all constructors found in `init_funcs` and calls them | 1779 | /// Loops over all constructors found in `init_funcs` and calls them |
| 2554 | /// respectively based on their priority which was sorted by `setupInitFunctions`. | 1780 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| ... | @@ -2609,8 +1835,7 @@ fn createSyntheticFunction( | ... | @@ -2609,8 +1835,7 @@ fn createSyntheticFunction( |
| 2609 | function_body: *std.ArrayList(u8), | 1835 | function_body: *std.ArrayList(u8), |
| 2610 | ) !void { | 1836 | ) !void { |
| 2611 | const gpa = wasm.base.comp.gpa; | 1837 | const gpa = wasm.base.comp.gpa; |
| 2612 | const loc = wasm.findGlobalSymbol(symbol_name) orelse | 1838 | const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol? |
| 2613 | try wasm.createSyntheticSymbol(symbol_name, .function); | | |
| 2614 | const symbol = loc.getSymbol(wasm); | 1839 | const symbol = loc.getSymbol(wasm); |
| 2615 | if (symbol.isDead()) { | 1840 | if (symbol.isDead()) { |
| 2616 | return; | 1841 | return; |
| ... | @@ -2620,32 +1845,21 @@ fn createSyntheticFunction( | ... | @@ -2620,32 +1845,21 @@ fn createSyntheticFunction( |
| 2620 | const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count())); | 1845 | const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count())); |
| 2621 | try wasm.functions.putNoClobber( | 1846 | try wasm.functions.putNoClobber( |
| 2622 | gpa, | 1847 | gpa, |
| 2623 | .{ .file = null, .index = func_index }, | 1848 | .{ .file = .null, .index = func_index }, |
| 2624 | .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index }, | 1849 | .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index }, |
| 2625 | ); | 1850 | ); |
| 2626 | symbol.index = func_index; | 1851 | symbol.index = func_index; |
| 2627 | | 1852 | |
| 2628 | // create the atom that will be output into the final binary | 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)); | 1854 | const atom_index = try wasm.createAtom(loc.index, .null); |
| 2630 | const atom = try wasm.managed_atoms.addOne(gpa); | 1855 | const atom = wasm.getAtomPtr(atom_index); |
| 2631 | atom.* = .{ | 1856 | atom.size = @intCast(function_body.items.len); |
| 2632 | .size = @as(u32, @intCast(function_body.items.len)), | 1857 | atom.code = function_body.moveToUnmanaged(); |
| 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 | }; | | |
| 2641 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); | 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 | /// Unlike `createSyntheticFunction` this function is to be called by | 1861 | /// Unlike `createSyntheticFunction` this function is to be called by |
| 2646 | /// the codegeneration backend. This will not allocate the created Atom yet, | 1862 | /// 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. | | |
| 2649 | /// Returns the index of the symbol. | 1863 | /// Returns the index of the symbol. |
| 2650 | pub fn createFunction( | 1864 | pub fn createFunction( |
| 2651 | wasm: *Wasm, | 1865 | wasm: *Wasm, |
| ... | @@ -2653,37 +1867,8 @@ pub fn createFunction( | ... | @@ -2653,37 +1867,8 @@ pub fn createFunction( |
| 2653 | func_ty: std.wasm.Type, | 1867 | func_ty: std.wasm.Type, |
| 2654 | function_body: *std.ArrayList(u8), | 1868 | function_body: *std.ArrayList(u8), |
| 2655 | relocations: *std.ArrayList(Relocation), | 1869 | relocations: *std.ArrayList(Relocation), |
| 2656 | ) !u32 { | 1870 | ) !Symbol.Index { |
| 2657 | const gpa = wasm.base.comp.gpa; | 1871 | return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); |
| 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; | | |
| 2687 | } | 1872 | } |
| 2688 | | 1873 | |
| 2689 | /// If required, sets the function index in the `start` section. | 1874 | /// If required, sets the function index in the `start` section. |
| ... | @@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { | ... | @@ -2700,6 +1885,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2700 | | 1885 | |
| 2701 | if (!shared_memory) return; | 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 | var function_body = std.ArrayList(u8).init(gpa); | 1891 | var function_body = std.ArrayList(u8).init(gpa); |
| 2704 | defer function_body.deinit(); | 1892 | defer function_body.deinit(); |
| 2705 | const writer = function_body.writer(); | 1893 | const writer = function_body.writer(); |
| ... | @@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { | ... | @@ -2748,6 +1936,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2748 | if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| { | 1936 | if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| { |
| 2749 | try writer.writeByte(std.wasm.opcode(.call)); | 1937 | try writer.writeByte(std.wasm.opcode(.call)); |
| 2750 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); | 1938 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); |
| | 1939 | loc.getSymbol(wasm).mark(); |
| 2751 | } | 1940 | } |
| 2752 | | 1941 | |
| 2753 | try writer.writeByte(std.wasm.opcode(.end)); | 1942 | try writer.writeByte(std.wasm.opcode(.end)); |
| ... | @@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { | ... | @@ -2762,21 +1951,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2762 | fn setupImports(wasm: *Wasm) !void { | 1951 | fn setupImports(wasm: *Wasm) !void { |
| 2763 | const gpa = wasm.base.comp.gpa; | 1952 | const gpa = wasm.base.comp.gpa; |
| 2764 | log.debug("Merging imports", .{}); | 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 | for (wasm.resolved_symbols.keys()) |symbol_loc| { | 1954 | for (wasm.resolved_symbols.keys()) |symbol_loc| { |
| 2778 | const file_index = symbol_loc.file orelse { | 1955 | const obj_file = wasm.file(symbol_loc.file) orelse { |
| 2779 | // imports generated by Zig code are already in the `import` section | 1956 | // Synthetic symbols will already exist in the `import` section |
| 2780 | continue; | 1957 | continue; |
| 2781 | }; | 1958 | }; |
| 2782 | | 1959 | |
| ... | @@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void { | ... | @@ -2789,14 +1966,13 @@ fn setupImports(wasm: *Wasm) !void { |
| 2789 | } | 1966 | } |
| 2790 | | 1967 | |
| 2791 | log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)}); | 1968 | log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)}); |
| 2792 | const object = wasm.objects.items[file_index]; | 1969 | const import = obj_file.import(symbol_loc.index); |
| 2793 | const import = object.findImport(symbol.tag.externalType(), symbol.index); | | |
| 2794 | | 1970 | |
| 2795 | // We copy the import to a new import to ensure the names contain references | 1971 | // We copy the import to a new import to ensure the names contain references |
| 2796 | // to the internal string table, rather than of the object file. | 1972 | // to the internal string table, rather than of the object file. |
| 2797 | const new_imp: types.Import = .{ | 1973 | const new_imp: types.Import = .{ |
| 2798 | .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)), | 1974 | .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)), |
| 2799 | .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)), | 1975 | .name = try wasm.string_table.put(gpa, obj_file.string(import.name)), |
| 2800 | .kind = import.kind, | 1976 | .kind = import.kind, |
| 2801 | }; | 1977 | }; |
| 2802 | // TODO: De-duplicate imports when they contain the same names and type | 1978 | // TODO: De-duplicate imports when they contain the same names and type |
| ... | @@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void { | ... | @@ -2847,26 +2023,17 @@ fn mergeSections(wasm: *Wasm) !void { |
| 2847 | defer removed_duplicates.deinit(); | 2023 | defer removed_duplicates.deinit(); |
| 2848 | | 2024 | |
| 2849 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 2025 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2850 | if (sym_loc.file == null) { | 2026 | const obj_file = wasm.file(sym_loc.file) orelse { |
| 2851 | // Zig code-generated symbols are already within the sections and do not | 2027 | // Synthetic symbols already live in the corresponding sections. |
| 2852 | // require to be merged | | |
| 2853 | continue; | 2028 | continue; |
| 2854 | } | 2029 | }; |
| 2855 | | | |
| 2856 | const object = &wasm.objects.items[sym_loc.file.?]; | | |
| 2857 | const symbol = &object.symtable[sym_loc.index]; | | |
| 2858 | | 2030 | |
| 2859 | if (symbol.isDead() or | 2031 | const symbol = obj_file.symbol(sym_loc.index); |
| 2860 | symbol.isUndefined() or | 2032 | if (symbol.isDead() or symbol.isUndefined()) { |
| 2861 | (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) | | |
| 2862 | { | | |
| 2863 | // Skip undefined symbols as they go in the `import` section | 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 | continue; | 2034 | continue; |
| 2866 | } | 2035 | } |
| 2867 | | 2036 | |
| 2868 | const offset = object.importedCountByKind(symbol.tag.externalType()); | | |
| 2869 | const index = symbol.index - offset; | | |
| 2870 | switch (symbol.tag) { | 2037 | switch (symbol.tag) { |
| 2871 | .function => { | 2038 | .function => { |
| 2872 | const gop = try wasm.functions.getOrPut( | 2039 | const gop = try wasm.functions.getOrPut( |
| ... | @@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void { | ... | @@ -2877,35 +2044,46 @@ fn mergeSections(wasm: *Wasm) !void { |
| 2877 | // We found an alias to the same function, discard this symbol in favor of | 2044 | // We found an alias to the same function, discard this symbol in favor of |
| 2878 | // the original symbol and point the discard function to it. This ensures | 2045 | // the original symbol and point the discard function to it. This ensures |
| 2879 | // we only emit a single function, instead of duplicates. | 2046 | // we only emit a single function, instead of duplicates. |
| 2880 | symbol.unmark(); | 2047 | // we favor keeping the global over a local. |
| 2881 | try wasm.discarded.putNoClobber( | 2048 | const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index }; |
| 2882 | gpa, | 2049 | const original_sym = original_loc.getSymbol(wasm); |
| 2883 | sym_loc, | 2050 | if (original_sym.isLocal() and symbol.isGlobal()) { |
| 2884 | .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index }, | 2051 | original_sym.unmark(); |
| 2885 | ); | 2052 | try wasm.discarded.put(gpa, original_loc, sym_loc); |
| 2886 | try removed_duplicates.append(sym_loc); | 2053 | try removed_duplicates.append(original_loc); |
| 2887 | continue; | 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 | symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count; | 2062 | symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count; |
| 2891 | }, | 2063 | }, |
| 2892 | .global => { | 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 | symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count; | 2067 | symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count; |
| 2895 | try wasm.wasm_globals.append(gpa, original_global); | 2068 | try wasm.wasm_globals.append(gpa, original_global); |
| 2896 | }, | 2069 | }, |
| 2897 | .table => { | 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 | symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count; | 2075 | symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count; |
| 2900 | try wasm.tables.append(gpa, original_table); | 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 | // For any removed duplicates, remove them from the resolved symbols list | 2083 | // For any removed duplicates, remove them from the resolved symbols list |
| 2907 | for (removed_duplicates.items) |sym_loc| { | 2084 | for (removed_duplicates.items) |sym_loc| { |
| 2908 | assert(wasm.resolved_symbols.swapRemove(sym_loc)); | 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 | log.debug("Merged ({d}) functions", .{wasm.functions.count()}); | 2089 | log.debug("Merged ({d}) functions", .{wasm.functions.count()}); |
| ... | @@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void { | ... | @@ -2926,12 +2104,12 @@ fn mergeTypes(wasm: *Wasm) !void { |
| 2926 | defer dirty.deinit(); | 2104 | defer dirty.deinit(); |
| 2927 | | 2105 | |
| 2928 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 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 | // zig code-generated symbols are already present in final type section | 2108 | // zig code-generated symbols are already present in final type section |
| 2931 | continue; | 2109 | continue; |
| 2932 | } | 2110 | }; |
| 2933 | const object = wasm.objects.items[sym_loc.file.?]; | 2111 | |
| 2934 | const symbol = object.symtable[sym_loc.index]; | 2112 | const symbol = obj_file.symbol(sym_loc.index); |
| 2935 | if (symbol.tag != .function or symbol.isDead()) { | 2113 | if (symbol.tag != .function or symbol.isDead()) { |
| 2936 | // Only functions have types. Only retrieve the type of referenced functions. | 2114 | // Only functions have types. Only retrieve the type of referenced functions. |
| 2937 | continue; | 2115 | continue; |
| ... | @@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void { | ... | @@ -2940,31 +2118,26 @@ fn mergeTypes(wasm: *Wasm) !void { |
| 2940 | if (symbol.isUndefined()) { | 2118 | if (symbol.isUndefined()) { |
| 2941 | log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); | 2119 | log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); |
| 2942 | const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue; | 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 | import.kind.function = try wasm.putOrGetFuncType(original_type); | 2122 | import.kind.function = try wasm.putOrGetFuncType(original_type); |
| 2945 | } else if (!dirty.contains(symbol.index)) { | 2123 | } else if (!dirty.contains(symbol.index)) { |
| 2946 | log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)}); | 2124 | log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)}); |
| 2947 | const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; | 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 | dirty.putAssumeCapacityNoClobber(symbol.index, {}); | 2127 | dirty.putAssumeCapacityNoClobber(symbol.index, {}); |
| 2950 | } | 2128 | } |
| 2951 | } | 2129 | } |
| 2952 | log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len}); | 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 { | 2133 | fn checkExportNames(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 | | | |
| 2961 | const force_exp_names = wasm.export_symbol_names; | 2134 | const force_exp_names = wasm.export_symbol_names; |
| 2962 | if (force_exp_names.len > 0) { | 2135 | if (force_exp_names.len > 0) { |
| 2963 | var failed_exports = false; | 2136 | var failed_exports = false; |
| 2964 | | 2137 | |
| 2965 | for (force_exp_names) |exp_name| { | 2138 | for (force_exp_names) |exp_name| { |
| 2966 | const loc = wasm.findGlobalSymbol(exp_name) orelse { | 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 | failed_exports = true; | 2141 | failed_exports = true; |
| 2969 | continue; | 2142 | continue; |
| 2970 | }; | 2143 | }; |
| ... | @@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -2974,19 +2147,26 @@ fn setupExports(wasm: *Wasm) !void { |
| 2974 | } | 2147 | } |
| 2975 | | 2148 | |
| 2976 | if (failed_exports) { | 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 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 2161 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 2982 | const symbol = sym_loc.getSymbol(wasm); | 2162 | const symbol = sym_loc.getSymbol(wasm); |
| 2983 | if (!symbol.isExported(comp.config.rdynamic)) continue; | 2163 | if (!symbol.isExported(comp.config.rdynamic)) continue; |
| 2984 | | 2164 | |
| 2985 | const sym_name = sym_loc.getName(wasm); | 2165 | const sym_name = sym_loc.getName(wasm); |
| 2986 | const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: { | 2166 | const export_name = if (sym_loc.file == .null) |
| 2987 | if (sym_loc.file == null) break :blk symbol.name; | 2167 | symbol.name |
| 2988 | break :blk try wasm.string_table.put(gpa, sym_name); | 2168 | else |
| 2989 | }; | 2169 | try wasm.string_table.put(gpa, sym_name); |
| 2990 | const exp: types.Export = if (symbol.tag == .data) exp: { | 2170 | const exp: types.Export = if (symbol.tag == .data) exp: { |
| 2991 | const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); | 2171 | const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); |
| 2992 | try wasm.wasm_globals.append(gpa, .{ | 2172 | try wasm.wasm_globals.append(gpa, .{ |
| ... | @@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void { | ... | @@ -3020,14 +2200,14 @@ fn setupStart(wasm: *Wasm) !void { |
| 3020 | const entry_name = wasm.entry_name orelse return; | 2200 | const entry_name = wasm.entry_name orelse return; |
| 3021 | | 2201 | |
| 3022 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { | 2202 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 3023 | log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); | 2203 | try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); |
| 3024 | return error.MissingSymbol; | 2204 | return error.FlushFailure; |
| 3025 | }; | 2205 | }; |
| 3026 | | 2206 | |
| 3027 | const symbol = symbol_loc.getSymbol(wasm); | 2207 | const symbol = symbol_loc.getSymbol(wasm); |
| 3028 | if (symbol.tag != .function) { | 2208 | if (symbol.tag != .function) { |
| 3029 | log.err("Entry symbol '{s}' is not a function", .{entry_name}); | 2209 | try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name}); |
| 3030 | return error.InvalidEntryKind; | 2210 | return error.FlushFailure; |
| 3031 | } | 2211 | } |
| 3032 | | 2212 | |
| 3033 | // Ensure the symbol is exported so host environment can access it | 2213 | // Ensure the symbol is exported so host environment can access it |
| ... | @@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3055,11 +2235,18 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3055 | | 2235 | |
| 3056 | const is_obj = comp.config.output_mode == .Obj; | 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 | if (place_stack_first and !is_obj) { | 2243 | if (place_stack_first and !is_obj) { |
| 3059 | memory_ptr = stack_alignment.forward(memory_ptr); | 2244 | memory_ptr = stack_alignment.forward(memory_ptr); |
| 3060 | memory_ptr += wasm.base.stack_size; | 2245 | memory_ptr += wasm.base.stack_size; |
| 3061 | // We always put the stack pointer global at index 0 | 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 | var offset: u32 = @as(u32, @intCast(memory_ptr)); | 2252 | var offset: u32 = @as(u32, @intCast(memory_ptr)); |
| ... | @@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3098,6 +2285,7 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3098 | memory_ptr = mem.alignForward(u64, memory_ptr, 4); | 2285 | memory_ptr = mem.alignForward(u64, memory_ptr, 4); |
| 3099 | const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); | 2286 | const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); |
| 3100 | const sym = loc.getSymbol(wasm); | 2287 | const sym = loc.getSymbol(wasm); |
| | 2288 | sym.mark(); |
| 3101 | sym.virtual_address = @as(u32, @intCast(memory_ptr)); | 2289 | sym.virtual_address = @as(u32, @intCast(memory_ptr)); |
| 3102 | memory_ptr += 4; | 2290 | memory_ptr += 4; |
| 3103 | } | 2291 | } |
| ... | @@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3105,7 +2293,9 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3105 | if (!place_stack_first and !is_obj) { | 2293 | if (!place_stack_first and !is_obj) { |
| 3106 | memory_ptr = stack_alignment.forward(memory_ptr); | 2294 | memory_ptr = stack_alignment.forward(memory_ptr); |
| 3107 | memory_ptr += wasm.base.stack_size; | 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 | // One of the linked object files has a reference to the __heap_base symbol. | 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,16 +2311,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3121 | | 2311 | |
| 3122 | if (wasm.initial_memory) |initial_memory| { | 2312 | if (wasm.initial_memory) |initial_memory| { |
| 3123 | if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { | 2313 | if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { |
| 3124 | log.err("Initial memory must be {d}-byte aligned", .{page_size}); | 2314 | try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size}); |
| 3125 | return error.MissAlignment; | | |
| 3126 | } | 2315 | } |
| 3127 | if (memory_ptr > initial_memory) { | 2316 | if (memory_ptr > initial_memory) { |
| 3128 | log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); | 2317 | try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3129 | return error.MemoryTooSmall; | | |
| 3130 | } | 2318 | } |
| 3131 | if (initial_memory > max_memory_allowed) { | 2319 | if (initial_memory > max_memory_allowed) { |
| 3132 | log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); | 2320 | try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); |
| 3133 | return error.MemoryTooBig; | | |
| 3134 | } | 2321 | } |
| 3135 | memory_ptr = initial_memory; | 2322 | memory_ptr = initial_memory; |
| 3136 | } | 2323 | } |
| ... | @@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3147,16 +2334,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3147 | | 2334 | |
| 3148 | if (wasm.max_memory) |max_memory| { | 2335 | if (wasm.max_memory) |max_memory| { |
| 3149 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { | 2336 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { |
| 3150 | log.err("Maximum memory must be {d}-byte aligned", .{page_size}); | 2337 | try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size}); |
| 3151 | return error.MissAlignment; | | |
| 3152 | } | 2338 | } |
| 3153 | if (memory_ptr > max_memory) { | 2339 | if (memory_ptr > max_memory) { |
| 3154 | log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr}); | 2340 | try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr}); |
| 3155 | return error.MemoryTooSmall; | | |
| 3156 | } | 2341 | } |
| 3157 | if (max_memory > max_memory_allowed) { | 2342 | if (max_memory > max_memory_allowed) { |
| 3158 | log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed}); | 2343 | try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed}); |
| 3159 | return error.MemoryTooBig; | | |
| 3160 | } | 2344 | } |
| 3161 | wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); | 2345 | wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); |
| 3162 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); | 2346 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); |
| ... | @@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3170,17 +2354,17 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3170 | /// From a given object's index and the index of the segment, returns the corresponding | 2354 | /// From a given object's index and the index of the segment, returns the corresponding |
| 3171 | /// index of the segment within the final data section. When the segment does not yet | 2355 | /// index of the segment within the final data section. When the segment does not yet |
| 3172 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | 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 | const comp = wasm.base.comp; | 2358 | const comp = wasm.base.comp; |
| 3175 | const gpa = comp.gpa; | 2359 | const gpa = comp.gpa; |
| 3176 | const object: Object = wasm.objects.items[object_index]; | 2360 | const obj_file = wasm.file(file_index).?; |
| 3177 | const symbol = object.symtable[symbol_index]; | 2361 | const symbol = obj_file.symbols()[@intFromEnum(symbol_index)]; |
| 3178 | const index: u32 = @intCast(wasm.segments.items.len); | 2362 | const index: u32 = @intCast(wasm.segments.items.len); |
| 3179 | const shared_memory = comp.config.shared_memory; | 2363 | const shared_memory = comp.config.shared_memory; |
| 3180 | | 2364 | |
| 3181 | switch (symbol.tag) { | 2365 | switch (symbol.tag) { |
| 3182 | .data => { | 2366 | .data => { |
| 3183 | const segment_info = object.segment_info[symbol.index]; | 2367 | const segment_info = obj_file.segmentInfo()[symbol.index]; |
| 3184 | const merge_segment = comp.config.output_mode != .Obj; | 2368 | const merge_segment = comp.config.output_mode != .Obj; |
| 3185 | const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); | 2369 | const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); |
| 3186 | if (!result.found_existing) { | 2370 | if (!result.found_existing) { |
| ... | @@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 | ... | @@ -3209,7 +2393,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 |
| 3209 | break :blk index; | 2393 | break :blk index; |
| 3210 | }, | 2394 | }, |
| 3211 | .section => { | 2395 | .section => { |
| 3212 | const section_name = object.string_table.get(symbol.name); | 2396 | const section_name = obj_file.symbolName(symbol_index); |
| 3213 | if (mem.eql(u8, section_name, ".debug_info")) { | 2397 | if (mem.eql(u8, section_name, ".debug_info")) { |
| 3214 | return wasm.debug_info_index orelse blk: { | 2398 | return wasm.debug_info_index orelse blk: { |
| 3215 | wasm.debug_info_index = index; | 2399 | wasm.debug_info_index = index; |
| ... | @@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 | ... | @@ -3257,319 +2441,68 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 |
| 3257 | wasm.debug_str_index = index; | 2441 | wasm.debug_str_index = index; |
| 3258 | try wasm.appendDummySegment(); | 2442 | try wasm.appendDummySegment(); |
| 3259 | break :blk index; | 2443 | break :blk index; |
| 3260 | }; | 2444 | }; |
| 3261 | } else { | 2445 | } else { |
| 3262 | log.warn("found unknown section '{s}'", .{section_name}); | 2446 | var err = try wasm.addErrorWithNotes(1); |
| 3263 | return error.UnexpectedValue; | 2447 | try err.addMsg(wasm, "found unknown section '{s}'", .{section_name}); |
| 3264 | } | 2448 | try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()}); |
| 3265 | }, | 2449 | return error.UnexpectedValue; |
| 3266 | else => unreachable, | 2450 | } |
| 3267 | } | 2451 | }, |
| 3268 | } | 2452 | else => unreachable, |
| 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}); | | |
| 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. | 2456 | /// Appends a new segment with default field values |
| 3393 | /// This initializes the index, appends a new segment, | 2457 | fn appendDummySegment(wasm: *Wasm) !void { |
| 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 { | | |
| 3415 | const gpa = wasm.base.comp.gpa; | 2458 | const gpa = wasm.base.comp.gpa; |
| 3416 | | 2459 | try wasm.segments.append(gpa, .{ |
| 3417 | for (wasm.segment_info.values()) |segment_info| { | 2460 | .alignment = .@"1", |
| 3418 | gpa.free(segment_info.name); | 2461 | .size = 0, |
| 3419 | } | 2462 | .offset = 0, |
| 3420 | | 2463 | .flags = 0, |
| 3421 | var atom_it = wasm.decls.valueIterator(); | 2464 | }); |
| 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; | | |
| 3448 | } | 2465 | } |
| 3449 | | 2466 | |
| 3450 | pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { | 2467 | pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 3451 | const comp = wasm.base.comp; | 2468 | const comp = wasm.base.comp; |
| 3452 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 2469 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 3453 | const use_llvm = comp.config.use_llvm; | | |
| 3454 | | 2470 | |
| 3455 | if (use_lld) { | 2471 | if (use_lld) { |
| 3456 | return wasm.linkWithLLD(arena, prog_node); | 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 | /// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary. | 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 | const tracy = trace(@src()); | 2479 | const tracy = trace(@src()); |
| 3467 | defer tracy.end(); | 2480 | defer tracy.end(); |
| 3468 | | 2481 | |
| 3469 | const comp = wasm.base.comp; | 2482 | const comp = wasm.base.comp; |
| 3470 | const shared_memory = comp.config.shared_memory; | 2483 | if (wasm.llvm_object) |llvm_object| { |
| 3471 | const import_memory = comp.config.import_memory; | 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 | const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. | 2493 | const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. |
| 3474 | const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); | 2494 | const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); |
| 3475 | const opt_zcu = comp.module; | 2495 | const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: { |
| 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 | | | |
| 3484 | if (fs.path.dirname(full_out_path)) |dirname| { | 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 | } else { | 2498 | } else { |
| 3487 | break :blk wasm.base.zcu_object_sub_path.?; | 2499 | break :blk path; |
| 3488 | } | 2500 | } |
| 3489 | } else null; | 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 | // Positional arguments to the linker such as object files and static archives. | 2503 | // Positional arguments to the linker such as object files and static archives. |
| 3571 | var positionals = std.ArrayList([]const u8).init(arena); | 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 | const target = comp.root_mod.resolved_target.result; | 2507 | const target = comp.root_mod.resolved_target.result; |
| 3575 | const output_mode = comp.config.output_mode; | 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,6 +2511,10 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3578 | const link_libcpp = comp.config.link_libcpp; | 2511 | const link_libcpp = comp.config.link_libcpp; |
| 3579 | const wasi_exec_model = comp.config.wasi_exec_model; | 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 | // When the target os is WASI, we allow linking with WASI-LIBC | 2518 | // When the target os is WASI, we allow linking with WASI-LIBC |
| 3582 | if (target.os.tag == .wasi) { | 2519 | if (target.os.tag == .wasi) { |
| 3583 | const is_exe_or_dyn_lib = output_mode == .Exe or | 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,7 +2546,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3609 | try positionals.append(path); | 2546 | try positionals.append(path); |
| 3610 | } | 2547 | } |
| 3611 | | 2548 | |
| 3612 | for (objects) |object| { | 2549 | for (comp.objects) |object| { |
| 3613 | try positionals.append(object.path); | 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,171 +2559,35 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin |
| 3622 | | 2559 | |
| 3623 | try wasm.parseInputFiles(positionals.items); | 2560 | try wasm.parseInputFiles(positionals.items); |
| 3624 | | 2561 | |
| 3625 | for (wasm.objects.items, 0..) |_, object_index| { | 2562 | if (wasm.zig_object_index != .null) { |
| 3626 | try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index))); | 2563 | try wasm.resolveSymbolsInObject(wasm.zig_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); | | |
| 3701 | } | 2564 | } |
| 3702 | | 2565 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3703 | if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path); | 2566 | for (wasm.objects.items) |object_index| { |
| 3704 | if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path); | 2567 | try wasm.resolveSymbolsInObject(object_index); |
| 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))); | | |
| 3710 | } | 2568 | } |
| | 2569 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3711 | | 2570 | |
| 3712 | var emit_features_count: u32 = 0; | 2571 | var emit_features_count: u32 = 0; |
| 3713 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; | 2572 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 3714 | try wasm.validateFeatures(&enabled_features, &emit_features_count); | 2573 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 3715 | try wasm.resolveSymbolsInArchives(); | 2574 | try wasm.resolveSymbolsInArchives(); |
| | 2575 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3716 | try wasm.resolveLazySymbols(); | 2576 | try wasm.resolveLazySymbols(); |
| 3717 | try wasm.checkUndefinedSymbols(); | 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 | try wasm.setupInitFunctions(); | 2580 | try wasm.setupInitFunctions(); |
| | 2581 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3723 | try wasm.setupStart(); | 2582 | try wasm.setupStart(); |
| | 2583 | |
| 3724 | try wasm.markReferences(); | 2584 | try wasm.markReferences(); |
| 3725 | try wasm.setupErrorsLen(); | | |
| 3726 | try wasm.setupImports(); | 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 | try wasm.mergeSections(); | 2586 | try wasm.mergeSections(); |
| 3787 | try wasm.mergeTypes(); | 2587 | try wasm.mergeTypes(); |
| 3788 | try wasm.allocateAtoms(); | 2588 | try wasm.allocateAtoms(); |
| 3789 | try wasm.setupMemory(); | 2589 | try wasm.setupMemory(); |
| | 2590 | if (comp.link_errors.items.len > 0) return error.FlushFailure; |
| 3790 | wasm.allocateVirtualAddresses(); | 2591 | wasm.allocateVirtualAddresses(); |
| 3791 | wasm.mapFunctionTable(); | 2592 | wasm.mapFunctionTable(); |
| 3792 | try wasm.initializeCallCtorsFunction(); | 2593 | try wasm.initializeCallCtorsFunction(); |
| ... | @@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) | ... | @@ -3796,6 +2597,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) |
| 3796 | try wasm.setupStartSection(); | 2597 | try wasm.setupStartSection(); |
| 3797 | try wasm.setupExports(); | 2598 | try wasm.setupExports(); |
| 3798 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 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 | /// Writes the WebAssembly in-memory module to the file | 2603 | /// Writes the WebAssembly in-memory module to the file |
| ... | @@ -4021,7 +2823,9 @@ fn writeToFile( | ... | @@ -4021,7 +2823,9 @@ fn writeToFile( |
| 4021 | try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count()))); | 2823 | try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count()))); |
| 4022 | var symbol_it = wasm.function_table.keyIterator(); | 2824 | var symbol_it = wasm.function_table.keyIterator(); |
| 4023 | while (symbol_it.next()) |symbol_loc_ptr| { | 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 | try leb.writeULEB128(binary_writer, sym.index); | 2829 | try leb.writeULEB128(binary_writer, sym.index); |
| 4026 | } | 2830 | } |
| 4027 | | 2831 | |
| ... | @@ -4124,8 +2928,8 @@ fn writeToFile( | ... | @@ -4124,8 +2928,8 @@ fn writeToFile( |
| 4124 | try binary_writer.writeAll(atom.code.items); | 2928 | try binary_writer.writeAll(atom.code.items); |
| 4125 | | 2929 | |
| 4126 | current_offset += atom.size; | 2930 | current_offset += atom.size; |
| 4127 | if (atom.prev) |prev| { | 2931 | if (atom.prev != .null) { |
| 4128 | atom_index = prev; | 2932 | atom_index = atom.prev; |
| 4129 | } else { | 2933 | } else { |
| 4130 | // also pad with zeroes when last atom to ensure | 2934 | // also pad with zeroes when last atom to ensure |
| 4131 | // segments are aligned. | 2935 | // segments are aligned. |
| ... | @@ -4191,19 +2995,9 @@ fn writeToFile( | ... | @@ -4191,19 +2995,9 @@ fn writeToFile( |
| 4191 | }) catch unreachable; | 2995 | }) catch unreachable; |
| 4192 | try emitBuildIdSection(&binary_bytes, str); | 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 | var debug_bytes = std.ArrayList(u8).init(gpa); | 3001 | var debug_bytes = std.ArrayList(u8).init(gpa); |
| 4208 | defer debug_bytes.deinit(); | 3002 | defer debug_bytes.deinit(); |
| 4209 | | 3003 | |
| ... | @@ -4229,7 +3023,8 @@ fn writeToFile( | ... | @@ -4229,7 +3023,8 @@ fn writeToFile( |
| 4229 | while (true) { | 3023 | while (true) { |
| 4230 | atom.resolveRelocs(wasm); | 3024 | atom.resolveRelocs(wasm); |
| 4231 | try debug_bytes.appendSlice(atom.code.items); | 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 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); | 3029 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); |
| 4235 | debug_bytes.clearRetainingCapacity(); | 3030 | debug_bytes.clearRetainingCapacity(); |
| ... | @@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: | ... | @@ -5004,7 +3799,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: |
| 5004 | try leb.writeULEB128(writer, @intFromEnum(symbol.tag)); | 3799 | try leb.writeULEB128(writer, @intFromEnum(symbol.tag)); |
| 5005 | try leb.writeULEB128(writer, symbol.flags); | 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 | switch (symbol.tag) { | 3803 | switch (symbol.tag) { |
| 5009 | .data => { | 3804 | .data => { |
| 5010 | try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len))); | 3805 | try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len))); |
| ... | @@ -5098,7 +3893,7 @@ fn emitCodeRelocations( | ... | @@ -5098,7 +3893,7 @@ fn emitCodeRelocations( |
| 5098 | size_offset += getULEB128Size(atom.size); | 3893 | size_offset += getULEB128Size(atom.size); |
| 5099 | for (atom.relocs.items) |relocation| { | 3894 | for (atom.relocs.items) |relocation| { |
| 5100 | count += 1; | 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 | const symbol_index = symbol_table.get(sym_loc).?; | 3897 | const symbol_index = symbol_table.get(sym_loc).?; |
| 5103 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); | 3898 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); |
| 5104 | const offset = atom.offset + relocation.offset + size_offset; | 3899 | const offset = atom.offset + relocation.offset + size_offset; |
| ... | @@ -5109,7 +3904,8 @@ fn emitCodeRelocations( | ... | @@ -5109,7 +3904,8 @@ fn emitCodeRelocations( |
| 5109 | } | 3904 | } |
| 5110 | log.debug("Emit relocation: {}", .{relocation}); | 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 | if (count == 0) return; | 3910 | if (count == 0) return; |
| 5115 | var buf: [5]u8 = undefined; | 3911 | var buf: [5]u8 = undefined; |
| ... | @@ -5145,10 +3941,7 @@ fn emitDataRelocations( | ... | @@ -5145,10 +3941,7 @@ fn emitDataRelocations( |
| 5145 | size_offset += getULEB128Size(atom.size); | 3941 | size_offset += getULEB128Size(atom.size); |
| 5146 | for (atom.relocs.items) |relocation| { | 3942 | for (atom.relocs.items) |relocation| { |
| 5147 | count += 1; | 3943 | count += 1; |
| 5148 | const sym_loc: SymbolLoc = .{ | 3944 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) }; |
| 5149 | .file = atom.file, | | |
| 5150 | .index = relocation.index, | | |
| 5151 | }; | | |
| 5152 | const symbol_index = symbol_table.get(sym_loc).?; | 3945 | const symbol_index = symbol_table.get(sym_loc).?; |
| 5153 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); | 3946 | try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type)); |
| 5154 | const offset = atom.offset + relocation.offset + size_offset; | 3947 | const offset = atom.offset + relocation.offset + size_offset; |
| ... | @@ -5159,7 +3952,8 @@ fn emitDataRelocations( | ... | @@ -5159,7 +3952,8 @@ fn emitDataRelocations( |
| 5159 | } | 3952 | } |
| 5160 | log.debug("Emit relocation: {}", .{relocation}); | 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 | if (count == 0) return; | 3959 | if (count == 0) return; |
| ... | @@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { | ... | @@ -5185,23 +3979,15 @@ fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { |
| 5185 | return false; | 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 | /// Searches for a matching function signature. When no matching signature is found, | 3982 | /// Searches for a matching function signature. When no matching signature is found, |
| 5197 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. | 3983 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 5198 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { | 3984 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 5199 | if (wasm.getTypeIndex(func_type)) |index| { | 3985 | if (wasm.getTypeIndex(func_type)) |index| { |
| 5200 | return index; | 3986 | return index; |
| 5201 | } | 3987 | } |
| 5202 | const gpa = wasm.base.comp.gpa; | | |
| 5203 | | 3988 | |
| 5204 | // functype does not exist. | 3989 | // functype does not exist. |
| | 3990 | const gpa = wasm.base.comp.gpa; |
| 5205 | const index: u32 = @intCast(wasm.func_types.items.len); | 3991 | const index: u32 = @intCast(wasm.func_types.items.len); |
| 5206 | const params = try gpa.dupe(std.wasm.Valtype, func_type.params); | 3992 | const params = try gpa.dupe(std.wasm.Valtype, func_type.params); |
| 5207 | errdefer gpa.free(params); | 3993 | errdefer gpa.free(params); |
| ... | @@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { | ... | @@ -5218,11 +4004,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 5218 | /// Asserts declaration has an associated `Atom`. | 4004 | /// Asserts declaration has an associated `Atom`. |
| 5219 | /// Returns the index into the list of types. | 4005 | /// Returns the index into the list of types. |
| 5220 | pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 { | 4006 | pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 { |
| 5221 | const gpa = wasm.base.comp.gpa; | 4007 | return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, decl_index, func_type); |
| 5222 | const atom_index = wasm.decls.get(decl_index).?; | 4008 | } |
| 5223 | const index = try wasm.putOrGetFuncType(func_type); | 4009 | |
| 5224 | try wasm.atom_types.put(gpa, atom_index, index); | 4010 | /// Returns the symbol index of the error name table. |
| 5225 | return index; | 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 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, | 4025 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, |
| ... | @@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void { | ... | @@ -5244,12 +4041,9 @@ fn markReferences(wasm: *Wasm) !void { |
| 5244 | // Debug sections may require to be parsed and marked when it contains | 4041 | // Debug sections may require to be parsed and marked when it contains |
| 5245 | // relocations to alive symbols. | 4042 | // relocations to alive symbols. |
| 5246 | if (sym.tag == .section and comp.config.debug_format != .strip) { | 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 | 4044 | const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently |
| 5248 | const object = &wasm.objects.items[file]; | 4045 | _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index); |
| 5249 | const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm); | 4046 | sym.mark(); |
| 5250 | const atom = wasm.getAtom(atom_index); | | |
| 5251 | const atom_sym = atom.symbolLoc().getSymbol(wasm); | | |
| 5252 | atom_sym.mark(); | | |
| 5253 | } | 4047 | } |
| 5254 | } | 4048 | } |
| 5255 | } | 4049 | } |
| ... | @@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void { | ... | @@ -5265,21 +4059,21 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void { |
| 5265 | return; | 4059 | return; |
| 5266 | } | 4060 | } |
| 5267 | symbol.mark(); | 4061 | symbol.mark(); |
| | 4062 | gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)}); |
| 5268 | if (symbol.isUndefined()) { | 4063 | if (symbol.isUndefined()) { |
| 5269 | // undefined symbols do not have an associated `Atom` and therefore also | 4064 | // undefined symbols do not have an associated `Atom` and therefore also |
| 5270 | // do not contain relocations. | 4065 | // do not contain relocations. |
| 5271 | return; | 4066 | return; |
| 5272 | } | 4067 | } |
| 5273 | | 4068 | |
| 5274 | const atom_index = if (loc.file) |file_index| idx: { | 4069 | const atom_index = if (wasm.file(loc.file)) |obj_file| |
| 5275 | const object = &wasm.objects.items[file_index]; | 4070 | try obj_file.parseSymbolIntoAtom(wasm, loc.index) |
| 5276 | const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm); | 4071 | else |
| 5277 | break :idx atom_index; | 4072 | wasm.symbol_atom.get(loc) orelse return; |
| 5278 | } else wasm.symbol_atom.get(loc) orelse return; | | |
| 5279 | | 4073 | |
| 5280 | const atom = wasm.getAtom(atom_index); | 4074 | const atom = wasm.getAtom(atom_index); |
| 5281 | for (atom.relocs.items) |reloc| { | 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 | try wasm.mark(target_loc.finalLoc(wasm)); | 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,3 +4084,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 |
| 5290 | .command => "_start", | 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 | } |